Skip to content

Commit

Permalink
feat(images): provide direct minIO URL in API
Browse files Browse the repository at this point in the history
Merge pull request #138 from eurofurence/108-add-minio-url-to-image
108 add minio url to image
  • Loading branch information
Fenrikur authored Aug 4, 2024
2 parents 68e96f4 + 56940cb commit 4c30cb5
Show file tree
Hide file tree
Showing 22 changed files with 1,806 additions and 168 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ RUN dotnet restore \
&& dotnet publish src/Eurofurence.App.Server.Web/Eurofurence.App.Server.Web.csproj --output "/app/artifacts" --configuration Release
RUN dotnet tool install --global dotnet-ef \
&& export PATH="$PATH:/root/.dotnet/tools" \
&& dotnet ef migrations bundle -o "/app/artifacts/db-migration-bundle" -p src/Eurofurence.App.Server.Web
&& dotnet ef migrations bundle -o "/app/artifacts/db-migration-bundle" -p src/Eurofurence.App.Infrastructure.EntityFramework
ENTRYPOINT dotnet artifacts/Eurofurence.App.Server.Web.dll http://*:30001
EXPOSE 30001

Expand Down
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ services:
MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1
volumes:
- db:/var/lib/mysql
ports:
- '33306:3306'
healthcheck:
test: mariadb-admin ping -h 127.0.0.1 -u root
start_period: 5s
Expand Down
44 changes: 19 additions & 25 deletions src/Eurofurence.App.Backoffice/Components/ImageDialog.razor
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
<MudContainer Style="height: 600px" Class="overflow-scroll pr-4">
<MudGrid>
<MudItem xs="6">
<MudImage ObjectFit="ObjectFit.Contain" Height="500" Width="500" Class="ml-2" Src="@_imageSource">
@if (string.IsNullOrEmpty(_imageSource))
<MudImage ObjectFit="ObjectFit.Contain" Height="500" Width="500" Class="ml-2" Src="@Record.Url">
@if (string.IsNullOrEmpty(@Record.Url))
{
<MudProgressCircular Color="Color.Primary" Indeterminate="true"/>
}
Expand Down Expand Up @@ -57,9 +57,7 @@

private bool Update { get; set; }

private string _imageSource = string.Empty;

protected override async Task OnInitializedAsync()
protected override void OnInitialized()
{
if (Record == null)
{
Expand All @@ -68,35 +66,20 @@
else
{
Update = true;
await LoadImageSourceAsync();
}
}

private async Task LoadImageSourceAsync()
{
if (Record == null)
{
return;
}

var imageContent = await ImageService.GetImageContentAsync(Record.Id);
if (!string.IsNullOrEmpty(imageContent))
{
_imageSource = $"data:image/jpeg;base64,{imageContent}";
StateHasChanged();
}
}


private async Task UploadImage(IBrowserFile? file)
{
if (file == null || Record == null)
{
return;
}

ImageResponse? image;
if (Update)
{
var image = await ImageService.PutImageAsync(Record.Id, file);
image = await ImageService.PutImageAsync(Record.Id, file);
if (image != null)
{
Snackbar.Add("Image updated.", Severity.Success);
Expand All @@ -108,7 +91,7 @@
}
else
{
var image = await ImageService.PostImageAsync(file);
image = await ImageService.PostImageAsync(file);
if (image != null)
{
Snackbar.Add("Image added.", Severity.Success);
Expand All @@ -119,7 +102,18 @@
}
}

await LoadImageSourceAsync();
if (image != null)
{
Record.Id = image.Id;
Record.InternalReference = image.InternalReference;
Record.SizeInBytes = image.SizeInBytes;
Record.MimeType = image.MimeType;
Record.Width = image.Width;
Record.Height = image.Height;
Record.Url = image.Url + "?" + Guid.NewGuid();
}

StateHasChanged();
}

private void Close()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,18 @@
{
<MudDataGrid Items="@Record.Images" Filterable="false" SortMode="@SortMode.None" Groupable="false">
<Columns>
<PropertyColumn Title="ID" Property="image => image.Id" />
<PropertyColumn Title="Name" Property="image => image.InternalReference" />
<PropertyColumn Title="Size (Bytes)" Property="image => image.SizeInBytes" />
<PropertyColumn Title="Last Changed" Property="image => image.LastChangeDateTimeUtc" />
<TemplateColumn CellClass="d-flex justify-end">
<CellTemplate>
<MudIconButton Icon="@Icons.Material.Filled.Delete" OnClick="@(() => DeleteImage(@context.Item.Id))" />
<MudImage Height="100" ObjectFit="ObjectFit.Contain" Src="@context.Item.Url" />
</CellTemplate>
</TemplateColumn>
<PropertyColumn Title="ID" Property="image => image.Id"/>
<PropertyColumn Title="Name" Property="image => image.InternalReference"/>
<PropertyColumn Title="Size (Bytes)" Property="image => image.SizeInBytes"/>
<PropertyColumn Title="Last Changed" Property="image => image.LastChangeDateTimeUtc"/>
<TemplateColumn>
<CellTemplate>
<MudIconButton Icon="@Icons.Material.Filled.Delete" OnClick="@(() => DeleteImage(@context.Item.Id))"/>
</CellTemplate>
</TemplateColumn>
</Columns>
Expand Down Expand Up @@ -152,6 +157,7 @@
Record?.Images.Add(new ImageRecord()
{
Id = image.Id,
Url = image.Url,
InternalReference = image.InternalReference,
MimeType = image.MimeType,
SizeInBytes = image.SizeInBytes,
Expand Down
33 changes: 15 additions & 18 deletions src/Eurofurence.App.Backoffice/Pages/Images.razor
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@
<Columns>
<TemplateColumn Title="Preview" CellClass="d-flex justify-center">
<CellTemplate>
@if (!string.IsNullOrEmpty(@_imageContents.FirstOrDefault(kvp => kvp.Key == @context.Item.Id).Value))
@if (!string.IsNullOrEmpty(@context.Item.Url))
{
<MudImage Class="ml-2" Width="100" Src="@_imageContents.FirstOrDefault(kvp => kvp.Key == @context.Item.Id).Value"/>
<MudLink Href="@context.Item.Url">
<MudImage Class="ml-2" Width="100" Height="100" ObjectFit="ObjectFit.Contain" Src="@context.Item.Url" />
</MudLink>
}
else
{
Expand Down Expand Up @@ -98,7 +100,6 @@
@code {
private string? _imageSearch;
private List<ImageWithRelationsResponse> _images = new();
private List<KeyValuePair<Guid, string>> _imageContents = [];
private MudDataGrid<ImageWithRelationsResponse>? _dataGrid;

private async Task<GridData<ImageWithRelationsResponse?>> GetImages(GridState<ImageWithRelationsResponse?> gridState)
Expand All @@ -112,26 +113,15 @@
var filteredItems = FilterImages(_images, _imageSearch).ToList();
result.Items = filteredItems.Skip(gridState.Page * gridState.PageSize).Take(gridState.PageSize);
result.TotalItems = filteredItems.Count;
_ = Task.Run(() => LoadImageSourcesAsync(result.Items.Select(item => item!.Id)));
return result;
}

private async Task LoadImagesAsync()
{
_images = (await ImageService.GetImagesWithRelationsAsync()).ToList();
}

private async Task LoadImageSourcesAsync(IEnumerable<Guid> imageIds)
{
_imageContents = [];
foreach (var imageId in imageIds)
foreach (var image in _images)
{
var imageContent = await ImageService.GetImageContentAsync(imageId);
if (!string.IsNullOrEmpty(imageContent))
{
_imageContents.Add(new KeyValuePair<Guid, string>(imageId, $"data:image/jpeg;base64,{imageContent}"));
StateHasChanged();
}
image.Url += "?" + Guid.NewGuid();
}
}

Expand All @@ -155,8 +145,12 @@
var parameters = new DialogParameters<ImageDialog> { { x => x.Record, null } };
var options = new DialogOptions() { MaxWidth = MaxWidth.Large, FullWidth = true };

await DialogService.ShowAsync<ImageDialog>("New image", parameters, options);
var dialog = await DialogService.ShowAsync<ImageDialog>("New image", parameters, options);

await dialog.Result;

await LoadImagesAsync();
StateHasChanged();
_dataGrid?.ReloadServerData();
}

Expand All @@ -165,8 +159,11 @@
var parameters = new DialogParameters<ImageDialog> { { x => x.Record, record } };
var options = new DialogOptions { MaxWidth = MaxWidth.Large, FullWidth = true };

await DialogService.ShowAsync<ImageDialog>("Update image", parameters, options);
var dialog = await DialogService.ShowAsync<ImageDialog>("Update image", parameters, options);
await dialog.Result;

await LoadImagesAsync();
StateHasChanged();
_dataGrid?.ReloadServerData();
}

Expand Down
84 changes: 24 additions & 60 deletions src/Eurofurence.App.Backoffice/Pages/KnowledgeBase.razor
Original file line number Diff line number Diff line change
Expand Up @@ -100,18 +100,10 @@
<MudCardContent>
<MudText Class="mb-4">@((MarkupString)Markdown.ToHtml(knowledgeEntry.Text))</MudText>

@if (_imageContents.Any(kvp => kvp.Key == knowledgeEntry.Id))
@foreach (var image in knowledgeEntry.Images)
{
@foreach (var image in _imageContents.FirstOrDefault(kvp => kvp.Key == knowledgeEntry.Id).Value)
{
<MudImage Class="ml-2" Height="100" Src="@image"/>
}
<MudImage Class="ml-2" Height="100" ObjectFit="ObjectFit.Contain" Src="@image.Url" />
}
else if (knowledgeEntry.Images.Count > 0)
{
<MudProgressCircular Color="Color.Primary" Indeterminate="true"/>
}

</MudCardContent>
<MudCardActions>
@foreach (var link in knowledgeEntry.Links)
Expand All @@ -131,13 +123,11 @@
private KnowledgeGroupRecord? _selectedGroup;
private List<KnowledgeGroupRecord> _knowledgeGroups = new List<KnowledgeGroupRecord>();
private List<KnowledgeEntryRecord> _knowledgeEntries = new List<KnowledgeEntryRecord>();
private List<KeyValuePair<Guid, List<string>>> _imageContents = [];

protected override async Task OnInitializedAsync()
{
await LoadKnowledgeEntries();
await LoadKnowledgeGroups();
await LoadImageSourcesAsync();
}

private async Task LoadKnowledgeEntries()
Expand All @@ -146,7 +136,6 @@
_knowledgeEntries = [];

var responses = (await KnowledgeService.GetKnowledgeEntriesAsync()).OrderBy(ke => ke.Order);
var imageResponses = await ImageService.GetImagesAsync();
foreach (var response in responses)
{
var record = new KnowledgeEntryRecord()
Expand All @@ -160,20 +149,25 @@
IsDeleted = response.IsDeleted,
Links = response.Links
};
foreach (var image in response.Images)
foreach (var imageId in response.ImageIds)
{
record.Images.Add(new ImageRecord()
var image = await ImageService.GetImageAsync(imageId);
if (image != null)
{
Id = image.Id,
Height = image.Height,
Width = image.Width,
ContentHashSha1 = image.ContentHashSha1,
InternalReference = image.InternalReference,
MimeType = image.MimeType,
SizeInBytes = image.SizeInBytes,
LastChangeDateTimeUtc = image.LastChangeDateTimeUtc,
IsDeleted = image.IsDeleted
});
record.Images.Add(new ImageRecord()
{
Id = image.Id,
Url = image.Url,
Height = image.Height,
Width = image.Width,
ContentHashSha1 = image.ContentHashSha1,
InternalReference = image.InternalReference,
MimeType = image.MimeType,
SizeInBytes = image.SizeInBytes,
LastChangeDateTimeUtc = image.LastChangeDateTimeUtc,
IsDeleted = image.IsDeleted
});
}
}

_knowledgeEntries.Add(record);
Expand All @@ -189,31 +183,6 @@
Loading = false;
}

private async Task LoadImageSourcesAsync()
{
Loading = true;
_imageContents = [];

foreach (var knowledgeEntry in _knowledgeEntries)
{
StateHasChanged();
var keyValuePair = new KeyValuePair<Guid, List<string>>(knowledgeEntry.Id, new List<string>());

foreach (var image in knowledgeEntry.Images)
{
var imageContent = await ImageService.GetImageContentAsync(image.Id);
if (!string.IsNullOrEmpty(imageContent))
{
keyValuePair.Value.Add($"data:image/jpeg;base64,{imageContent}");
}
}

_imageContents.Add(keyValuePair);
}

Loading = false;
}

private IEnumerable<KnowledgeEntryRecord> GetKnowledgeEntities()
{
if (_selectedGroup == null)
Expand All @@ -238,11 +207,10 @@
var dialog = await DialogService.ShowAsync<KnowledgeEntryDialog>("New knowledge base entry", parameters, options);
var result = await dialog.Result;

if (!result.Canceled)
if (result is { Canceled: false })
{
Loading = true;
await LoadKnowledgeEntries();
await LoadImageSourcesAsync();
}
}

Expand All @@ -252,14 +220,10 @@
var options = new DialogOptions() { MaxWidth = MaxWidth.Large, FullWidth = true };

var dialog = await DialogService.ShowAsync<KnowledgeEntryDialog>("Update knowledge base entry", parameters, options);
var result = await dialog.Result;
await dialog.Result;

if (!result.Canceled)
{
Loading = true;
await LoadKnowledgeEntries();
await LoadImageSourcesAsync();
}
Loading = true;
await LoadKnowledgeEntries();
}

private async Task DeleteKnowledgeEntry(Guid id)
Expand Down Expand Up @@ -291,7 +255,7 @@
var dialog = await DialogService.ShowAsync<KnowledgeGroupDialog>("Update knowledge base group", parameters, options);
var result = await dialog.Result;

if (!result.Canceled)
if (result is { Canceled: false })
{
Loading = true;
await LoadKnowledgeGroups();
Expand Down
2 changes: 1 addition & 1 deletion src/Eurofurence.App.Backoffice/Services/IImageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ namespace Eurofurence.App.Backoffice.Services
{
public interface IImageService
{
public Task<ImageResponse?> GetImageAsync(Guid id);
public Task<ImageResponse[]> GetImagesAsync();
public Task<ImageWithRelationsResponse[]> GetImagesWithRelationsAsync();
public Task<string> GetImageContentAsync(Guid id);
public Task<ImageResponse?> PutImageAsync(Guid id, IBrowserFile file);
public Task<ImageResponse?> PostImageAsync(IBrowserFile file);
public Task<bool> DeleteImageAsync(Guid id);
Expand Down
Loading

0 comments on commit 4c30cb5

Please sign in to comment.