LANCommander/LANCommander.Server/UI/Components/ArchiveUploader.razor
Pat Hartl 120e14d40c Add Tools section to server app
Allows users to upload additional tools and tie them to games. This can be useful for software like map editors, trainers, etc.
2026-02-08 02:24:53 -06:00

197 lines
6.7 KiB
Text

@using Hangfire;
@using LANCommander.SDK.Enums
@using LANCommander.Server.Jobs.Background;
@using Microsoft.Extensions.Options
@inject ArchiveService ArchiveService
@inject IOptions<Settings.Settings> Settings
@inject IMessageService MessageService
@inject IJSRuntime JS
@inject ILogger<ArchiveUploader> Logger
@{
RenderFragment Footer =
@<Template>
<FilePickerButton EntrySelectable="@(entry => !String.IsNullOrWhiteSpace(entry.Name) && entry.Name.ToLower().EndsWith(".zip"))"
OnSelected="OnLocalFileSelected"
Root="@RootPath"
Disabled="@(Status != "" || String.IsNullOrWhiteSpace(Archive.Version))">
Use Local File
</FilePickerButton>
<Button OnClick="Start" Disabled="@(File == null || Status != "" || String.IsNullOrWhiteSpace(Archive.Version))" Type="@ButtonType.Primary">Upload</Button>
<Button OnClick="Cancel">Cancel</Button>
</Template>;
}
<Modal Visible="@Visible" Title="Upload Archive" OnOk="Start" OnCancel="Cancel" Footer="@Footer" Closable="@(!Uploading)" MaskClosable="@(!Uploading)">
<Form Model="@Archive" Layout="@FormLayout.Vertical">
<FormItem Label="Version">
<Input @bind-Value="@context.Version" />
</FormItem>
<FormItem Label="Changelog">
<TextArea @bind-Value="@context.Changelog" MaxLength=500 ShowCount />
</FormItem>
<FormItem Label="Path">
<StorageLocationSelector Type="StorageLocationType.Archive" @bind-Value="@context.StorageLocation" />
</FormItem>
<FormItem>
<ChunkUploader @ref="ChunkUploader" Accept=".zip" @bind-File="File" @bind-Status="Status" @bind-StorageLocationId="@(context.StorageLocation.Id)" OnUploadCompleted="OnUploadCompleted" OnUploadError="OnUploadError">
<Text>
<p>Drag and Drop</p>
<p>or</p>
<p>
<Button Type="@ButtonType.Primary" Style="margin-top: 8px;">Browse</Button>
</p>
</Text>
<Hint>Only ZIP files are supported as game archives</Hint>
</ChunkUploader>
</FormItem>
</Form>
</Modal>
@code {
[Parameter] public Guid GameId { get; set; }
[Parameter] public Guid RedistributableId { get; set; }
[Parameter] public Guid ToolId { get; set; }
[Parameter] public EventCallback<Guid> OnArchiveUploaded { get; set; }
Archive Archive;
string RootPath = Path.GetPathRoot(Directory.GetCurrentDirectory());
IBrowserFile File;
ChunkUploader ChunkUploader;
Guid StorageLocationId;
StorageLocation StorageLocation;
bool Visible = false;
bool Uploading = false;
string Filename;
string Status;
private async Task Start()
{
Uploading = true;
await ChunkUploader.Start();
}
private async Task Clear()
{
await ChunkUploader.Clear();
}
private async Task Cancel()
{
await ChunkUploader.Clear();
Visible = false;
}
public async Task Open(Guid? archiveId = null)
{
if (archiveId.HasValue && archiveId != Guid.Empty)
{
Archive = await ArchiveService.GetAsync(archiveId.Value);
}
else
{
Archive = new Archive();
if (GameId != Guid.Empty)
Archive.GameId = GameId;
else if (RedistributableId != Guid.Empty)
Archive.RedistributableId = RedistributableId;
else if (ToolId != Guid.Empty)
Archive.ToolId = ToolId;
}
Visible = true;
await InvokeAsync(StateHasChanged);
}
public async Task OnUploadCompleted(string data)
{
Uploading = false;
if (Guid.TryParse(data, out var objectKey))
{
var uploadedArchive = await ArchiveService.FirstOrDefaultAsync(a => a.ObjectKey == objectKey.ToString());
uploadedArchive.GameId = Archive.GameId;
uploadedArchive.RedistributableId = Archive.RedistributableId;
uploadedArchive.ToolId = Archive.ToolId;
uploadedArchive.Version = Archive.Version;
uploadedArchive.Changelog = Archive.Changelog;
uploadedArchive.CompressedSize = await ArchiveService.GetCompressedSizeAsync(uploadedArchive);
uploadedArchive.UncompressedSize = await ArchiveService.GetUncompressedSizeAsync(uploadedArchive);
await ArchiveService.UpdateAsync(uploadedArchive);
Visible = false;
await InvokeAsync(StateHasChanged);
Archive? lastArchive = null;
if (Settings.Value.Server.Archives.EnablePatching)
{
if (Archive.GameId != Guid.Empty)
lastArchive = await ArchiveService.GetLatestArchiveAsync(a => a.Id != Archive.Id && a.GameId == Archive.GameId);
else if (Archive.RedistributableId != Guid.Empty)
lastArchive = await ArchiveService.GetLatestArchiveAsync(a => a.Id != Archive.Id && a.RedistributableId == Archive.RedistributableId);
else if (Archive.ToolId != Guid.Empty)
lastArchive = await ArchiveService.GetLatestArchiveAsync(a => a.Id != Archive.Id && a.ToolId == Archive.ToolId);
if (lastArchive != null && Settings.Value.Server.Archives.EnablePatching)
BackgroundJob.Enqueue<PatchArchiveBackgroundJob>(x => x.Execute(lastArchive.Id, Archive.Id));
}
if (OnArchiveUploaded.HasDelegate)
await OnArchiveUploaded.InvokeAsync(Archive.Id);
MessageService.Success("Archive uploaded!");
}
else
{
Visible = false;
await InvokeAsync(StateHasChanged);
MessageService.Error("Archive failed to upload!");
Logger.LogError("Archive failed to upload!");
}
await ChunkUploader.Clear();
}
public async Task OnUploadError(string message)
{
Uploading = false;
Visible = false;
await InvokeAsync(StateHasChanged);
MessageService.Error("Archive failed to upload!");
Logger.LogError($"Archive failed to upload: {message}");
}
public async Task OnLocalFileSelected(string path)
{
try
{
var objectKey = await ArchiveService.CopyFromLocalFileAsync(path);
await OnUploadCompleted(objectKey.ToString());
}
catch (Exception ex)
{
MessageService.Error("An unknown error occurred while trying to use a local file");
Logger.LogError(ex, "An unknown error occurred while trying to use a local file");
}
}
}