From 205f66958f4316a10540969421905758cf25afbc Mon Sep 17 00:00:00 2001 From: Meta <76539710+Metawolve@users.noreply.github.com> Date: Sun, 4 Aug 2024 00:02:13 +0200 Subject: [PATCH 1/7] feat: Added download url to images --- .../Images/ImageRecord.cs | 8 + .../Images/ImageResponse.cs | 7 + .../20240803204301_AddImageUrl.Designer.cs | 1551 +++++++++++++++++ .../Migrations/20240803204301_AddImageUrl.cs | 40 + .../Migrations/AppDbContextModelSnapshot.cs | 76 +- .../Images/ImageService.cs | 33 +- 6 files changed, 1675 insertions(+), 40 deletions(-) create mode 100644 src/Eurofurence.App.Infrastructure.EntityFramework/Migrations/20240803204301_AddImageUrl.Designer.cs create mode 100644 src/Eurofurence.App.Infrastructure.EntityFramework/Migrations/20240803204301_AddImageUrl.cs diff --git a/src/Eurofurence.App.Domain.Model/Images/ImageRecord.cs b/src/Eurofurence.App.Domain.Model/Images/ImageRecord.cs index d20aa6ad..e1f43b94 100644 --- a/src/Eurofurence.App.Domain.Model/Images/ImageRecord.cs +++ b/src/Eurofurence.App.Domain.Model/Images/ImageRecord.cs @@ -39,6 +39,14 @@ public class ImageRecord : EntityBase [DataMember] public string ContentHashSha1 { get; set; } + [Required] + [DataMember] + public string Url { get; set; } + + [Required] + [JsonIgnore] + public string InternalFileName { get; set; } + [IgnoreDataMember] [JsonIgnore] public virtual List Announcements { get; set; } = new(); diff --git a/src/Eurofurence.App.Domain.Model/Images/ImageResponse.cs b/src/Eurofurence.App.Domain.Model/Images/ImageResponse.cs index a08d87cb..37a75eca 100644 --- a/src/Eurofurence.App.Domain.Model/Images/ImageResponse.cs +++ b/src/Eurofurence.App.Domain.Model/Images/ImageResponse.cs @@ -1,4 +1,5 @@ using System.Runtime.Serialization; +using System.Text.Json.Serialization; namespace Eurofurence.App.Domain.Model.Images { @@ -22,5 +23,11 @@ public class ImageResponse : EntityBase [DataMember] public string ContentHashSha1 { get; set; } + + [DataMember] + public string Url { get; set; } + + [JsonIgnore] + public string InternalFileName { get; set; } } } diff --git a/src/Eurofurence.App.Infrastructure.EntityFramework/Migrations/20240803204301_AddImageUrl.Designer.cs b/src/Eurofurence.App.Infrastructure.EntityFramework/Migrations/20240803204301_AddImageUrl.Designer.cs new file mode 100644 index 00000000..bf0c2b96 --- /dev/null +++ b/src/Eurofurence.App.Infrastructure.EntityFramework/Migrations/20240803204301_AddImageUrl.Designer.cs @@ -0,0 +1,1551 @@ +// +using System; +using Eurofurence.App.Infrastructure.EntityFramework; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Eurofurence.App.Infrastructure.EntityFramework.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20240803204301_AddImageUrl")] + partial class AddImageUrl + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Announcements.AnnouncementRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Area") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Author") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Content") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ExternalReference") + .HasColumnType("longtext"); + + b.Property("ImageId") + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ValidFromDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("ValidUntilDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("Announcements"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.ArtShow.AgentClosingResultRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AgentBadgeNo") + .HasColumnType("int"); + + b.Property("AgentName") + .HasColumnType("longtext"); + + b.Property("ArtistName") + .HasColumnType("longtext"); + + b.Property("ExhibitsSold") + .HasColumnType("int"); + + b.Property("ExhibitsToAuction") + .HasColumnType("int"); + + b.Property("ExhibitsUnsold") + .HasColumnType("int"); + + b.Property("ImportDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("ImportHash") + .HasColumnType("longtext"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("NotificationDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("OwnerUid") + .HasColumnType("longtext"); + + b.Property("PrivateMessageId") + .HasColumnType("char(36)"); + + b.Property("TotalCashAmount") + .HasColumnType("decimal(65,30)"); + + b.HasKey("Id"); + + b.ToTable("AgentClosingResults"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.ArtShow.ItemActivityRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ASIDNO") + .HasColumnType("int"); + + b.Property("ArtPieceTitle") + .HasColumnType("longtext"); + + b.Property("ArtistName") + .HasColumnType("longtext"); + + b.Property("ImportDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("ImportHash") + .HasColumnType("longtext"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("NotificationDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("OwnerUid") + .HasColumnType("longtext"); + + b.Property("PrivateMessageId") + .HasColumnType("char(36)"); + + b.Property("Status") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("ItemActivitys"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.ArtistsAlley.TableRegistrationRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("DisplayName") + .HasColumnType("longtext"); + + b.Property("ImageId") + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("Location") + .HasColumnType("longtext"); + + b.Property("OwnerUid") + .HasColumnType("longtext"); + + b.Property("OwnerUsername") + .HasColumnType("longtext"); + + b.Property("ShortDescription") + .HasColumnType("longtext"); + + b.Property("State") + .HasColumnType("int"); + + b.Property("TelegramHandle") + .HasColumnType("longtext"); + + b.Property("WebsiteUrl") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("TableRegistrations"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.ArtistsAlley.TableRegistrationRecord+StateChangeRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ChangedByUid") + .HasColumnType("longtext"); + + b.Property("ChangedDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("NewState") + .HasColumnType("int"); + + b.Property("OldState") + .HasColumnType("int"); + + b.Property("TableRegistrationRecordId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("TableRegistrationRecordId"); + + b.ToTable("StateChangeRecord"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.CollectionGame.CollectionEntryRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("EventDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("FursuitParticipationId") + .HasColumnType("char(36)"); + + b.Property("FursuitParticipationRecordId") + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("PlayerParticipationId") + .HasColumnType("longtext"); + + b.Property("PlayerParticipationRecordId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("FursuitParticipationRecordId"); + + b.HasIndex("PlayerParticipationRecordId"); + + b.ToTable("CollectionEntries"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Communication.PrivateMessageRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AuthorName") + .HasColumnType("longtext"); + + b.Property("CreatedDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("Message") + .HasColumnType("longtext"); + + b.Property("ReadDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("ReceivedDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("RecipientUid") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SenderUid") + .HasColumnType("longtext"); + + b.Property("Subject") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("PrivateMessages"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Dealers.DealerRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AboutTheArtText") + .HasColumnType("longtext"); + + b.Property("AboutTheArtistText") + .HasColumnType("longtext"); + + b.Property("ArtPreviewCaption") + .HasColumnType("longtext"); + + b.Property("ArtPreviewImageId") + .HasColumnType("char(36)"); + + b.Property("ArtistImageId") + .HasColumnType("char(36)"); + + b.Property("ArtistThumbnailImageId") + .HasColumnType("char(36)"); + + b.Property("AttendeeNickname") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("AttendsOnFriday") + .HasColumnType("tinyint(1)"); + + b.Property("AttendsOnSaturday") + .HasColumnType("tinyint(1)"); + + b.Property("AttendsOnThursday") + .HasColumnType("tinyint(1)"); + + b.Property("BlueskyHandle") + .HasColumnType("longtext"); + + b.Property("Categories") + .HasColumnType("longtext"); + + b.Property("DiscordHandle") + .HasColumnType("longtext"); + + b.Property("DisplayName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IsAfterDark") + .HasColumnType("tinyint(1)"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("Keywords") + .HasColumnType("json"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("MastodonHandle") + .HasColumnType("longtext"); + + b.Property("Merchandise") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("RegistrationNumber") + .HasColumnType("int"); + + b.Property("ShortDescription") + .HasColumnType("longtext"); + + b.Property("TelegramHandle") + .HasColumnType("longtext"); + + b.Property("TwitterHandle") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ArtPreviewImageId"); + + b.HasIndex("ArtistImageId"); + + b.HasIndex("ArtistThumbnailImageId"); + + b.ToTable("Dealers"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Events.EventConferenceDayRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("EventConferenceDays"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Events.EventConferenceRoomRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("ShortName") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("EventConferenceRooms"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Events.EventConferenceTrackRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("EventConferenceTracks"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Events.EventFeedbackRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("EventId") + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("Message") + .HasColumnType("longtext"); + + b.Property("Rating") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("EventId"); + + b.ToTable("EventFeedbacks"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Events.EventRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Abstract") + .HasColumnType("longtext"); + + b.Property("BannerImageId") + .HasColumnType("char(36)"); + + b.Property("ConferenceDayId") + .HasColumnType("char(36)"); + + b.Property("ConferenceRoomId") + .HasColumnType("char(36)"); + + b.Property("ConferenceTrackId") + .HasColumnType("char(36)"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("Duration") + .HasColumnType("time(6)"); + + b.Property("EndDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("EndTime") + .HasColumnType("time(6)"); + + b.Property("IsAcceptingFeedback") + .HasColumnType("tinyint(1)"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("IsDeviatingFromConBook") + .HasColumnType("tinyint(1)"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("PanelHosts") + .HasColumnType("longtext"); + + b.Property("PosterImageId") + .HasColumnType("char(36)"); + + b.Property("Slug") + .HasColumnType("longtext"); + + b.Property("SourceEventId") + .HasColumnType("int"); + + b.Property("StartDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("StartTime") + .HasColumnType("time(6)"); + + b.Property("SubTitle") + .HasColumnType("longtext"); + + b.Property("Tags") + .HasColumnType("longtext"); + + b.Property("Title") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("BannerImageId"); + + b.HasIndex("ConferenceDayId"); + + b.HasIndex("ConferenceRoomId"); + + b.HasIndex("ConferenceTrackId"); + + b.HasIndex("PosterImageId"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Fragments.LinkFragment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("DealerRecordId") + .HasColumnType("char(36)"); + + b.Property("FragmentType") + .HasColumnType("int"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("KnowledgeEntryRecordId") + .HasColumnType("char(36)"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("MapEntryRecordId") + .HasColumnType("char(36)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("Target") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("DealerRecordId"); + + b.HasIndex("KnowledgeEntryRecordId"); + + b.HasIndex("MapEntryRecordId"); + + b.ToTable("LinkFragments"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Fursuits.CollectingGame.FursuitParticipationRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CollectionCount") + .HasColumnType("int"); + + b.Property("FursuitBadgeId") + .HasColumnType("char(36)"); + + b.Property("IsBanned") + .HasColumnType("tinyint(1)"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("LastCollectionDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("OwnerUid") + .HasColumnType("longtext"); + + b.Property("TokenRegistrationDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("TokenValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("FursuitParticipations"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Fursuits.CollectingGame.PlayerParticipationRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CollectionCount") + .HasColumnType("int"); + + b.Property("IsBanned") + .HasColumnType("tinyint(1)"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("Karma") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("LastCollectionDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("PlayerUid") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("PlayerParticipations"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Fursuits.CollectingGame.TokenRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("IsLinked") + .HasColumnType("tinyint(1)"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("LinkDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("LinkedFursuitParticipantUid") + .HasColumnType("char(36)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Fursuits.FursuitBadgeRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CollectionCode") + .HasColumnType("longtext"); + + b.Property("ExternalReference") + .HasColumnType("longtext"); + + b.Property("Gender") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ImageId") + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("IsPublic") + .HasColumnType("tinyint(1)"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerUid") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Species") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("WornBy") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("FursuitBadges"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Images.ImageRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ContentHashSha1") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Height") + .HasColumnType("int"); + + b.Property("InternalFileName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("InternalReference") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("MimeType") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SizeInBytes") + .HasColumnType("bigint"); + + b.Property("Url") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Width") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Images"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Knowledge.KnowledgeEntryRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("KnowledgeGroupId") + .HasColumnType("char(36)"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("Order") + .HasColumnType("int"); + + b.Property("Text") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("KnowledgeEntries"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Knowledge.KnowledgeGroupRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FontAwesomeIconName") + .HasColumnType("longtext"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Order") + .HasColumnType("int"); + + b.Property("ShowInHamburgerMenu") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.ToTable("KnowledgeGroups"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.LostAndFound.LostAndFoundRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Description") + .HasColumnType("longtext"); + + b.Property("ExternalId") + .HasColumnType("int"); + + b.Property("FoundDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("ImageUrl") + .HasColumnType("longtext"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("LostDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("ReturnDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("Title") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("LostAndFounds"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Maps.MapEntryRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("MapId") + .HasColumnType("char(36)"); + + b.Property("TapRadius") + .HasColumnType("int"); + + b.Property("X") + .HasColumnType("int"); + + b.Property("Y") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("MapId"); + + b.ToTable("MapEntries"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Maps.MapRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ImageId") + .HasColumnType("char(36)"); + + b.Property("IsBrowseable") + .HasColumnType("tinyint(1)"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("Order") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ImageId"); + + b.ToTable("Maps"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.PushNotifications.PushNotificationChannelRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ChannelUri") + .HasColumnType("longtext"); + + b.Property("DeviceId") + .HasColumnType("longtext"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("Platform") + .HasColumnType("int"); + + b.Property("Uid") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("PushNotificationChannels"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.PushNotifications.TopicRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("PushNotificationChannelRecordId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("PushNotificationChannelRecordId"); + + b.ToTable("Topics"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Security.IssueRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("NameOnBadge") + .HasColumnType("longtext"); + + b.Property("RegSysAlternativePinRecordId") + .HasColumnType("char(36)"); + + b.Property("RequestDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("RequesterUid") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("RegSysAlternativePinRecordId"); + + b.ToTable("IssueRecords"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Security.RegSysAccessTokenRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ClaimedAtDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("ClaimedByUid") + .HasColumnType("longtext"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("Token") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("RegSysAccessTokens"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Security.RegSysAlternativePinRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("IssuedByUid") + .HasColumnType("longtext"); + + b.Property("IssuedDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("NameOnBadge") + .HasColumnType("longtext"); + + b.Property("Pin") + .HasColumnType("longtext"); + + b.Property("PinConsumptionDatesUtc") + .HasColumnType("json"); + + b.Property("RegNo") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("RegSysAlternativePins"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Security.RegSysIdentityRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("Uid") + .HasColumnType("longtext"); + + b.Property("Username") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("RegSysIdentities"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Security.RoleRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .HasColumnType("longtext"); + + b.Property("RegSysAccessTokenRecordId") + .HasColumnType("char(36)"); + + b.Property("RegSysIdentityRecordId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RegSysAccessTokenRecordId"); + + b.HasIndex("RegSysIdentityRecordId"); + + b.ToTable("Roles"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Sync.EntityStorageInfoRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("DeltaStartDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("EntityType") + .HasColumnType("longtext"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.ToTable("EntityStorageInfos"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Telegram.UserRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Acl") + .HasColumnType("longtext"); + + b.Property("IsDeleted") + .HasColumnType("int"); + + b.Property("LastChangeDateTimeUtc") + .HasColumnType("datetime(6)"); + + b.Property("Username") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("ImageRecordKnowledgeEntryRecord", b => + { + b.Property("ImagesId") + .HasColumnType("char(36)"); + + b.Property("KnowledgeEntriesId") + .HasColumnType("char(36)"); + + b.HasKey("ImagesId", "KnowledgeEntriesId"); + + b.HasIndex("KnowledgeEntriesId"); + + b.ToTable("ImageRecordKnowledgeEntryRecord"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Announcements.AnnouncementRecord", b => + { + b.HasOne("Eurofurence.App.Domain.Model.Images.ImageRecord", "Image") + .WithMany("Announcements") + .HasForeignKey("ImageId"); + + b.Navigation("Image"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.ArtistsAlley.TableRegistrationRecord", b => + { + b.HasOne("Eurofurence.App.Domain.Model.Images.ImageRecord", "Image") + .WithMany("TableRegistrations") + .HasForeignKey("ImageId"); + + b.Navigation("Image"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.ArtistsAlley.TableRegistrationRecord+StateChangeRecord", b => + { + b.HasOne("Eurofurence.App.Domain.Model.ArtistsAlley.TableRegistrationRecord", null) + .WithMany("StateChangeLog") + .HasForeignKey("TableRegistrationRecordId"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.CollectionGame.CollectionEntryRecord", b => + { + b.HasOne("Eurofurence.App.Domain.Model.Fursuits.CollectingGame.FursuitParticipationRecord", null) + .WithMany("CollectionEntries") + .HasForeignKey("FursuitParticipationRecordId"); + + b.HasOne("Eurofurence.App.Domain.Model.Fursuits.CollectingGame.PlayerParticipationRecord", null) + .WithMany("CollectionEntries") + .HasForeignKey("PlayerParticipationRecordId"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Dealers.DealerRecord", b => + { + b.HasOne("Eurofurence.App.Domain.Model.Images.ImageRecord", "ArtPreviewImage") + .WithMany("DealerArtPreviews") + .HasForeignKey("ArtPreviewImageId"); + + b.HasOne("Eurofurence.App.Domain.Model.Images.ImageRecord", "ArtistImage") + .WithMany("DealerArtists") + .HasForeignKey("ArtistImageId"); + + b.HasOne("Eurofurence.App.Domain.Model.Images.ImageRecord", "ArtistThumbnailImage") + .WithMany("DealerArtistThumbnails") + .HasForeignKey("ArtistThumbnailImageId"); + + b.Navigation("ArtPreviewImage"); + + b.Navigation("ArtistImage"); + + b.Navigation("ArtistThumbnailImage"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Events.EventFeedbackRecord", b => + { + b.HasOne("Eurofurence.App.Domain.Model.Events.EventRecord", "Event") + .WithMany() + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Event"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Events.EventRecord", b => + { + b.HasOne("Eurofurence.App.Domain.Model.Images.ImageRecord", "BannerImage") + .WithMany("EventBanners") + .HasForeignKey("BannerImageId"); + + b.HasOne("Eurofurence.App.Domain.Model.Events.EventConferenceDayRecord", "ConferenceDay") + .WithMany("Events") + .HasForeignKey("ConferenceDayId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Eurofurence.App.Domain.Model.Events.EventConferenceRoomRecord", "ConferenceRoom") + .WithMany("Events") + .HasForeignKey("ConferenceRoomId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Eurofurence.App.Domain.Model.Events.EventConferenceTrackRecord", "ConferenceTrack") + .WithMany("Events") + .HasForeignKey("ConferenceTrackId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Eurofurence.App.Domain.Model.Images.ImageRecord", "PosterImage") + .WithMany("EventPosters") + .HasForeignKey("PosterImageId"); + + b.Navigation("BannerImage"); + + b.Navigation("ConferenceDay"); + + b.Navigation("ConferenceRoom"); + + b.Navigation("ConferenceTrack"); + + b.Navigation("PosterImage"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Fragments.LinkFragment", b => + { + b.HasOne("Eurofurence.App.Domain.Model.Dealers.DealerRecord", null) + .WithMany("Links") + .HasForeignKey("DealerRecordId"); + + b.HasOne("Eurofurence.App.Domain.Model.Knowledge.KnowledgeEntryRecord", null) + .WithMany("Links") + .HasForeignKey("KnowledgeEntryRecordId"); + + b.HasOne("Eurofurence.App.Domain.Model.Maps.MapEntryRecord", null) + .WithMany("Links") + .HasForeignKey("MapEntryRecordId"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Fursuits.FursuitBadgeRecord", b => + { + b.HasOne("Eurofurence.App.Domain.Model.Images.ImageRecord", "Image") + .WithMany("FursuitBadges") + .HasForeignKey("ImageId"); + + b.Navigation("Image"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Maps.MapEntryRecord", b => + { + b.HasOne("Eurofurence.App.Domain.Model.Maps.MapRecord", "Map") + .WithMany("Entries") + .HasForeignKey("MapId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Map"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Maps.MapRecord", b => + { + b.HasOne("Eurofurence.App.Domain.Model.Images.ImageRecord", "Image") + .WithMany("Maps") + .HasForeignKey("ImageId"); + + b.Navigation("Image"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.PushNotifications.TopicRecord", b => + { + b.HasOne("Eurofurence.App.Domain.Model.PushNotifications.PushNotificationChannelRecord", null) + .WithMany("Topics") + .HasForeignKey("PushNotificationChannelRecordId"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Security.IssueRecord", b => + { + b.HasOne("Eurofurence.App.Domain.Model.Security.RegSysAlternativePinRecord", null) + .WithMany("IssueLog") + .HasForeignKey("RegSysAlternativePinRecordId"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Security.RoleRecord", b => + { + b.HasOne("Eurofurence.App.Domain.Model.Security.RegSysAccessTokenRecord", null) + .WithMany("GrantRoles") + .HasForeignKey("RegSysAccessTokenRecordId"); + + b.HasOne("Eurofurence.App.Domain.Model.Security.RegSysIdentityRecord", null) + .WithMany("Roles") + .HasForeignKey("RegSysIdentityRecordId"); + }); + + modelBuilder.Entity("ImageRecordKnowledgeEntryRecord", b => + { + b.HasOne("Eurofurence.App.Domain.Model.Images.ImageRecord", null) + .WithMany() + .HasForeignKey("ImagesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Eurofurence.App.Domain.Model.Knowledge.KnowledgeEntryRecord", null) + .WithMany() + .HasForeignKey("KnowledgeEntriesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.ArtistsAlley.TableRegistrationRecord", b => + { + b.Navigation("StateChangeLog"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Dealers.DealerRecord", b => + { + b.Navigation("Links"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Events.EventConferenceDayRecord", b => + { + b.Navigation("Events"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Events.EventConferenceRoomRecord", b => + { + b.Navigation("Events"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Events.EventConferenceTrackRecord", b => + { + b.Navigation("Events"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Fursuits.CollectingGame.FursuitParticipationRecord", b => + { + b.Navigation("CollectionEntries"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Fursuits.CollectingGame.PlayerParticipationRecord", b => + { + b.Navigation("CollectionEntries"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Images.ImageRecord", b => + { + b.Navigation("Announcements"); + + b.Navigation("DealerArtPreviews"); + + b.Navigation("DealerArtistThumbnails"); + + b.Navigation("DealerArtists"); + + b.Navigation("EventBanners"); + + b.Navigation("EventPosters"); + + b.Navigation("FursuitBadges"); + + b.Navigation("Maps"); + + b.Navigation("TableRegistrations"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Knowledge.KnowledgeEntryRecord", b => + { + b.Navigation("Links"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Maps.MapEntryRecord", b => + { + b.Navigation("Links"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Maps.MapRecord", b => + { + b.Navigation("Entries"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.PushNotifications.PushNotificationChannelRecord", b => + { + b.Navigation("Topics"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Security.RegSysAccessTokenRecord", b => + { + b.Navigation("GrantRoles"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Security.RegSysAlternativePinRecord", b => + { + b.Navigation("IssueLog"); + }); + + modelBuilder.Entity("Eurofurence.App.Domain.Model.Security.RegSysIdentityRecord", b => + { + b.Navigation("Roles"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Eurofurence.App.Infrastructure.EntityFramework/Migrations/20240803204301_AddImageUrl.cs b/src/Eurofurence.App.Infrastructure.EntityFramework/Migrations/20240803204301_AddImageUrl.cs new file mode 100644 index 00000000..269f1018 --- /dev/null +++ b/src/Eurofurence.App.Infrastructure.EntityFramework/Migrations/20240803204301_AddImageUrl.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Eurofurence.App.Infrastructure.EntityFramework.Migrations +{ + /// + public partial class AddImageUrl : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "InternalFileName", + table: "Images", + type: "longtext", + nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "Url", + table: "Images", + type: "longtext", + nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "InternalFileName", + table: "Images"); + + migrationBuilder.DropColumn( + name: "Url", + table: "Images"); + } + } +} diff --git a/src/Eurofurence.App.Infrastructure.EntityFramework/Migrations/AppDbContextModelSnapshot.cs b/src/Eurofurence.App.Infrastructure.EntityFramework/Migrations/AppDbContextModelSnapshot.cs index f1c4bdc0..6f0e4363 100644 --- a/src/Eurofurence.App.Infrastructure.EntityFramework/Migrations/AppDbContextModelSnapshot.cs +++ b/src/Eurofurence.App.Infrastructure.EntityFramework/Migrations/AppDbContextModelSnapshot.cs @@ -66,7 +66,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("ImageId"); - b.ToTable("Announcements", (string)null); + b.ToTable("Announcements"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.ArtShow.AgentClosingResultRecord", b => @@ -119,7 +119,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("AgentClosingResults", (string)null); + b.ToTable("AgentClosingResults"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.ArtShow.ItemActivityRecord", b => @@ -163,7 +163,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("ItemActivitys", (string)null); + b.ToTable("ItemActivitys"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.ArtistsAlley.TableRegistrationRecord", b => @@ -212,7 +212,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("ImageId"); - b.ToTable("TableRegistrations", (string)null); + b.ToTable("TableRegistrations"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.ArtistsAlley.TableRegistrationRecord+StateChangeRecord", b => @@ -246,7 +246,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("TableRegistrationRecordId"); - b.ToTable("StateChangeRecord", (string)null); + b.ToTable("StateChangeRecord"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.CollectionGame.CollectionEntryRecord", b => @@ -282,7 +282,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("PlayerParticipationRecordId"); - b.ToTable("CollectionEntries", (string)null); + b.ToTable("CollectionEntries"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Communication.PrivateMessageRecord", b => @@ -324,7 +324,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("PrivateMessages", (string)null); + b.ToTable("PrivateMessages"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Dealers.DealerRecord", b => @@ -416,7 +416,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("ArtistThumbnailImageId"); - b.ToTable("Dealers", (string)null); + b.ToTable("Dealers"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Events.EventConferenceDayRecord", b => @@ -439,7 +439,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("EventConferenceDays", (string)null); + b.ToTable("EventConferenceDays"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Events.EventConferenceRoomRecord", b => @@ -462,7 +462,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("EventConferenceRooms", (string)null); + b.ToTable("EventConferenceRooms"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Events.EventConferenceTrackRecord", b => @@ -482,7 +482,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("EventConferenceTracks", (string)null); + b.ToTable("EventConferenceTracks"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Events.EventFeedbackRecord", b => @@ -510,7 +510,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("EventId"); - b.ToTable("EventFeedbacks", (string)null); + b.ToTable("EventFeedbacks"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Events.EventRecord", b => @@ -597,7 +597,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("PosterImageId"); - b.ToTable("Events", (string)null); + b.ToTable("Events"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Fragments.LinkFragment", b => @@ -639,7 +639,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("MapEntryRecordId"); - b.ToTable("LinkFragments", (string)null); + b.ToTable("LinkFragments"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Fursuits.CollectingGame.FursuitParticipationRecord", b => @@ -677,7 +677,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("FursuitParticipations", (string)null); + b.ToTable("FursuitParticipations"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Fursuits.CollectingGame.PlayerParticipationRecord", b => @@ -709,7 +709,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("PlayerParticipations", (string)null); + b.ToTable("PlayerParticipations"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Fursuits.CollectingGame.TokenRecord", b => @@ -738,7 +738,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("Tokens", (string)null); + b.ToTable("Tokens"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Fursuits.FursuitBadgeRecord", b => @@ -789,7 +789,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("ImageId"); - b.ToTable("FursuitBadges", (string)null); + b.ToTable("FursuitBadges"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Images.ImageRecord", b => @@ -805,6 +805,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Height") .HasColumnType("int"); + b.Property("InternalFileName") + .IsRequired() + .HasColumnType("longtext"); + b.Property("InternalReference") .IsRequired() .HasColumnType("longtext"); @@ -822,12 +826,16 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("SizeInBytes") .HasColumnType("bigint"); + b.Property("Url") + .IsRequired() + .HasColumnType("longtext"); + b.Property("Width") .HasColumnType("int"); b.HasKey("Id"); - b.ToTable("Images", (string)null); + b.ToTable("Images"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Knowledge.KnowledgeEntryRecord", b => @@ -858,7 +866,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("KnowledgeEntries", (string)null); + b.ToTable("KnowledgeEntries"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Knowledge.KnowledgeGroupRecord", b => @@ -892,7 +900,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("KnowledgeGroups", (string)null); + b.ToTable("KnowledgeGroups"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.LostAndFound.LostAndFoundRecord", b => @@ -933,7 +941,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("LostAndFounds", (string)null); + b.ToTable("LostAndFounds"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Maps.MapEntryRecord", b => @@ -958,7 +966,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("MapId"); - b.ToTable("MapEntries", (string)null); + b.ToTable("MapEntries"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Maps.MapRecord", b => @@ -990,7 +998,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("ImageId"); - b.ToTable("Maps", (string)null); + b.ToTable("Maps"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.PushNotifications.PushNotificationChannelRecord", b => @@ -1019,7 +1027,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("PushNotificationChannels", (string)null); + b.ToTable("PushNotificationChannels"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.PushNotifications.TopicRecord", b => @@ -1044,7 +1052,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("PushNotificationChannelRecordId"); - b.ToTable("Topics", (string)null); + b.ToTable("Topics"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Security.IssueRecord", b => @@ -1075,7 +1083,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("RegSysAlternativePinRecordId"); - b.ToTable("IssueRecords", (string)null); + b.ToTable("IssueRecords"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Security.RegSysAccessTokenRecord", b => @@ -1101,7 +1109,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("RegSysAccessTokens", (string)null); + b.ToTable("RegSysAccessTokens"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Security.RegSysAlternativePinRecord", b => @@ -1136,7 +1144,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("RegSysAlternativePins", (string)null); + b.ToTable("RegSysAlternativePins"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Security.RegSysIdentityRecord", b => @@ -1159,7 +1167,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("RegSysIdentities", (string)null); + b.ToTable("RegSysIdentities"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Security.RoleRecord", b => @@ -1189,7 +1197,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("RegSysIdentityRecordId"); - b.ToTable("Roles", (string)null); + b.ToTable("Roles"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Sync.EntityStorageInfoRecord", b => @@ -1212,7 +1220,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("EntityStorageInfos", (string)null); + b.ToTable("EntityStorageInfos"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Telegram.UserRecord", b => @@ -1235,7 +1243,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("Users", (string)null); + b.ToTable("Users"); }); modelBuilder.Entity("ImageRecordKnowledgeEntryRecord", b => @@ -1250,7 +1258,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("KnowledgeEntriesId"); - b.ToTable("ImageRecordKnowledgeEntryRecord", (string)null); + b.ToTable("ImageRecordKnowledgeEntryRecord"); }); modelBuilder.Entity("Eurofurence.App.Domain.Model.Announcements.AnnouncementRecord", b => diff --git a/src/Eurofurence.App.Server.Services/Images/ImageService.cs b/src/Eurofurence.App.Server.Services/Images/ImageService.cs index 2de05761..f78cc0a7 100644 --- a/src/Eurofurence.App.Server.Services/Images/ImageService.cs +++ b/src/Eurofurence.App.Server.Services/Images/ImageService.cs @@ -8,7 +8,6 @@ using SixLabors.ImageSharp; using System.IO; using System.Linq; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; using Eurofurence.App.Infrastructure.EntityFramework; using Eurofurence.App.Server.Services.Abstractions.MinIO; @@ -110,8 +109,17 @@ public async Task InsertImageAsync(string internalReference, Stream var imageFormat = image.Metadata.DecodedImageFormat; + var guid = Guid.NewGuid(); + + var fileName = imageFormat != null + ? guid + "." + imageFormat.FileExtensions.FirstOrDefault("png") + : guid + ".png"; + var record = new ImageRecord { + Id = guid, + InternalFileName = fileName, + Url = $"{_minIoClient.Config.Endpoint}/{_minIoConfiguration.Bucket}/{fileName}", InternalReference = internalReference, IsDeleted = 0, MimeType = imageFormat?.DefaultMimeType, @@ -123,7 +131,7 @@ public async Task InsertImageAsync(string internalReference, Stream await base.InsertOneAsync(record); - await UploadFileToMinIoAsync(_minIoConfiguration.Bucket, record.Id.ToString(), + await UploadFileToMinIoAsync(_minIoConfiguration.Bucket, record.InternalFileName, imageFormat?.DefaultMimeType, stream); await _appDbContext.SaveChangesAsync(); @@ -142,16 +150,24 @@ public async Task ReplaceImageAsync(Guid id, string internalReferen image = Image.Load(byteArray); } - IImageFormat imageFormat = image.Metadata.DecodedImageFormat; + var imageFormat = image.Metadata.DecodedImageFormat; var existingRecord = await _appDbContext.Images .AsNoTracking() .FirstOrDefaultAsync(entity => entity.Id == id); - await UploadFileToMinIoAsync(_minIoConfiguration.Bucket, existingRecord.Id.ToString(), + await DeleteFileFromMinIoAsync(_minIoConfiguration.Bucket, existingRecord.InternalFileName); + + var fileName = imageFormat != null + ? existingRecord.Id + "." + imageFormat.FileExtensions.FirstOrDefault("png") + : existingRecord.Id + ".png"; + + await UploadFileToMinIoAsync(_minIoConfiguration.Bucket, existingRecord.InternalFileName, imageFormat?.DefaultMimeType, stream); + existingRecord.InternalFileName = fileName; + existingRecord.Url = $"{_minIoClient.Config.Endpoint}/{_minIoConfiguration.Bucket}/{fileName}"; existingRecord.InternalReference = internalReference; existingRecord.MimeType = imageFormat?.DefaultMimeType; existingRecord.Width = image.Width; @@ -164,15 +180,20 @@ await UploadFileToMinIoAsync(_minIoConfiguration.Bucket, existingRecord.Id.ToStr public async Task GetImageContentByImageIdAsync(Guid id) { + var existingRecord = await + _appDbContext.Images + .AsNoTracking() + .FirstOrDefaultAsync(entity => entity.Id == id); + // Checks if the object exists await _minIoClient.StatObjectAsync(new StatObjectArgs() .WithBucket(_minIoConfiguration.Bucket) - .WithObject(id.ToString())); + .WithObject(existingRecord.InternalFileName)); var ms = new MemoryStream(); await _minIoClient.GetObjectAsync(new GetObjectArgs() .WithBucket(_minIoConfiguration.Bucket) - .WithObject(id.ToString()) + .WithObject(existingRecord.InternalFileName) .WithCallbackStream(stream => { stream.CopyTo(ms); From 6607cd49d548a55a9edcd4430da7fc5a8dc047d9 Mon Sep 17 00:00:00 2001 From: Meta <76539710+Metawolve@users.noreply.github.com> Date: Sun, 4 Aug 2024 00:05:50 +0200 Subject: [PATCH 2/7] feat: Set image content endpoints depcreated --- src/Eurofurence.App.Server.Web/Controllers/ImagesController.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Eurofurence.App.Server.Web/Controllers/ImagesController.cs b/src/Eurofurence.App.Server.Web/Controllers/ImagesController.cs index 7eeaef0b..a7291440 100644 --- a/src/Eurofurence.App.Server.Web/Controllers/ImagesController.cs +++ b/src/Eurofurence.App.Server.Web/Controllers/ImagesController.cs @@ -81,6 +81,7 @@ public async Task GetImageAsync([FromRoute] Guid id) [HttpGet("{id}/Content")] [ProducesResponseType(typeof(string), 404)] [ProducesResponseType(typeof(byte[]), 200)] + [Obsolete("Deprecated. Please use the 'Url' property of the image to stream the image from there instead.")] public async Task GetImageContentAsync([FromRoute] Guid id) { var record = await _imageService.FindOneAsync(id); @@ -99,6 +100,7 @@ public async Task GetImageContentAsync([FromRoute] Guid id) [ProducesResponseType(typeof(string), 404)] [ProducesResponseType(typeof(byte[]), 200)] [ResponseCache(Duration = 60 * 60 * 24, Location = ResponseCacheLocation.Any)] + [Obsolete("Deprecated. Please use the 'Url' property of the image to stream the image from there instead.")] public async Task GetImageWithHashContentAsync( [EnsureNotNull][FromRoute] Guid id, [EnsureNotNull][FromRoute] string contentHashBase64Encoded) From 2f927c9e52b4f68a5557f50b302725c6b3b436e0 Mon Sep 17 00:00:00 2001 From: Meta <76539710+Metawolve@users.noreply.github.com> Date: Sun, 4 Aug 2024 01:34:19 +0200 Subject: [PATCH 3/7] feat: Replaced image entities in knowledge entry response by image ids --- .../Knowledge/KnowledgeEntryResponse.cs | 2 +- .../Mapper/KnowledgeEntryResponseRegister.cs | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 src/Eurofurence.App.Server.Web/Mapper/KnowledgeEntryResponseRegister.cs diff --git a/src/Eurofurence.App.Domain.Model/Knowledge/KnowledgeEntryResponse.cs b/src/Eurofurence.App.Domain.Model/Knowledge/KnowledgeEntryResponse.cs index 93ce55f4..c2d8c694 100644 --- a/src/Eurofurence.App.Domain.Model/Knowledge/KnowledgeEntryResponse.cs +++ b/src/Eurofurence.App.Domain.Model/Knowledge/KnowledgeEntryResponse.cs @@ -25,6 +25,6 @@ public class KnowledgeEntryResponse : EntityBase public virtual List Links { get; set; } = new(); [DataMember] - public virtual List Images { get; set; } = new(); + public virtual List ImageIds { get; set; } = new(); } } diff --git a/src/Eurofurence.App.Server.Web/Mapper/KnowledgeEntryResponseRegister.cs b/src/Eurofurence.App.Server.Web/Mapper/KnowledgeEntryResponseRegister.cs new file mode 100644 index 00000000..f0594ec2 --- /dev/null +++ b/src/Eurofurence.App.Server.Web/Mapper/KnowledgeEntryResponseRegister.cs @@ -0,0 +1,20 @@ +using System.Linq; +using Eurofurence.App.Domain.Model.Images; +using Eurofurence.App.Domain.Model.Knowledge; +using Mapster; + +namespace Eurofurence.App.Server.Web.Mapper +{ + /// + /// Mapping register for mapping IDs of knowledge entry images to the response type + /// + public class KnowledgeEntryResponseRegister : IRegister + { + public void Register(TypeAdapterConfig config) + { + config + .NewConfig() + .Map(dest => dest.ImageIds, src => src.Images.Select(ke => ke.Id)); + } + } +} From e718c86014959c83beac93ba2afc8d3aa5ebe076 Mon Sep 17 00:00:00 2001 From: Meta <76539710+Metawolve@users.noreply.github.com> Date: Sun, 4 Aug 2024 01:34:44 +0200 Subject: [PATCH 4/7] feat: Implemented loading images from the url instead of the image content endpoint into the backoffice --- .../Components/ImageDialog.razor | 44 +++++----- .../Components/KnowledgeEntryDialog.razor | 16 ++-- .../Pages/Images.razor | 33 ++++---- .../Pages/KnowledgeBase.razor | 84 ++++++------------- .../Services/IImageService.cs | 2 +- .../Services/ImageService.cs | 17 ++-- 6 files changed, 75 insertions(+), 121 deletions(-) diff --git a/src/Eurofurence.App.Backoffice/Components/ImageDialog.razor b/src/Eurofurence.App.Backoffice/Components/ImageDialog.razor index dcfef25e..df388d01 100644 --- a/src/Eurofurence.App.Backoffice/Components/ImageDialog.razor +++ b/src/Eurofurence.App.Backoffice/Components/ImageDialog.razor @@ -11,8 +11,8 @@ - - @if (string.IsNullOrEmpty(_imageSource)) + + @if (string.IsNullOrEmpty(@Record.Url)) { } @@ -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) { @@ -68,25 +66,9 @@ 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) @@ -94,9 +76,10 @@ 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); @@ -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); @@ -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() diff --git a/src/Eurofurence.App.Backoffice/Components/KnowledgeEntryDialog.razor b/src/Eurofurence.App.Backoffice/Components/KnowledgeEntryDialog.razor index b605525b..15250651 100644 --- a/src/Eurofurence.App.Backoffice/Components/KnowledgeEntryDialog.razor +++ b/src/Eurofurence.App.Backoffice/Components/KnowledgeEntryDialog.razor @@ -61,13 +61,18 @@ { - - - - - + + + + + + + + + + @@ -152,6 +157,7 @@ Record?.Images.Add(new ImageRecord() { Id = image.Id, + Url = image.Url, InternalReference = image.InternalReference, MimeType = image.MimeType, SizeInBytes = image.SizeInBytes, diff --git a/src/Eurofurence.App.Backoffice/Pages/Images.razor b/src/Eurofurence.App.Backoffice/Pages/Images.razor index 0b595964..d0c854b7 100644 --- a/src/Eurofurence.App.Backoffice/Pages/Images.razor +++ b/src/Eurofurence.App.Backoffice/Pages/Images.razor @@ -19,9 +19,11 @@ - @if (!string.IsNullOrEmpty(@_imageContents.FirstOrDefault(kvp => kvp.Key == @context.Item.Id).Value)) + @if (!string.IsNullOrEmpty(@context.Item.Url)) { - + + + } else { @@ -98,7 +100,6 @@ @code { private string? _imageSearch; private List _images = new(); - private List> _imageContents = []; private MudDataGrid? _dataGrid; private async Task> GetImages(GridState gridState) @@ -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 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(imageId, $"data:image/jpeg;base64,{imageContent}")); - StateHasChanged(); - } + image.Url += "?" + Guid.NewGuid(); } } @@ -155,8 +145,12 @@ var parameters = new DialogParameters { { x => x.Record, null } }; var options = new DialogOptions() { MaxWidth = MaxWidth.Large, FullWidth = true }; - await DialogService.ShowAsync("New image", parameters, options); + var dialog = await DialogService.ShowAsync("New image", parameters, options); + + await dialog.Result; + await LoadImagesAsync(); + StateHasChanged(); _dataGrid?.ReloadServerData(); } @@ -165,8 +159,11 @@ var parameters = new DialogParameters { { x => x.Record, record } }; var options = new DialogOptions { MaxWidth = MaxWidth.Large, FullWidth = true }; - await DialogService.ShowAsync("Update image", parameters, options); + var dialog = await DialogService.ShowAsync("Update image", parameters, options); + await dialog.Result; + await LoadImagesAsync(); + StateHasChanged(); _dataGrid?.ReloadServerData(); } diff --git a/src/Eurofurence.App.Backoffice/Pages/KnowledgeBase.razor b/src/Eurofurence.App.Backoffice/Pages/KnowledgeBase.razor index ce95af08..1100054a 100644 --- a/src/Eurofurence.App.Backoffice/Pages/KnowledgeBase.razor +++ b/src/Eurofurence.App.Backoffice/Pages/KnowledgeBase.razor @@ -100,18 +100,10 @@ @((MarkupString)Markdown.ToHtml(knowledgeEntry.Text)) - @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) - { - - } + } - else if (knowledgeEntry.Images.Count > 0) - { - - } - @foreach (var link in knowledgeEntry.Links) @@ -131,13 +123,11 @@ private KnowledgeGroupRecord? _selectedGroup; private List _knowledgeGroups = new List(); private List _knowledgeEntries = new List(); - private List>> _imageContents = []; protected override async Task OnInitializedAsync() { await LoadKnowledgeEntries(); await LoadKnowledgeGroups(); - await LoadImageSourcesAsync(); } private async Task LoadKnowledgeEntries() @@ -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() @@ -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); @@ -189,31 +183,6 @@ Loading = false; } - private async Task LoadImageSourcesAsync() - { - Loading = true; - _imageContents = []; - - foreach (var knowledgeEntry in _knowledgeEntries) - { - StateHasChanged(); - var keyValuePair = new KeyValuePair>(knowledgeEntry.Id, new List()); - - 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 GetKnowledgeEntities() { if (_selectedGroup == null) @@ -238,11 +207,10 @@ var dialog = await DialogService.ShowAsync("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(); } } @@ -252,14 +220,10 @@ var options = new DialogOptions() { MaxWidth = MaxWidth.Large, FullWidth = true }; var dialog = await DialogService.ShowAsync("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) @@ -291,7 +255,7 @@ var dialog = await DialogService.ShowAsync("Update knowledge base group", parameters, options); var result = await dialog.Result; - if (!result.Canceled) + if (result is { Canceled: false }) { Loading = true; await LoadKnowledgeGroups(); diff --git a/src/Eurofurence.App.Backoffice/Services/IImageService.cs b/src/Eurofurence.App.Backoffice/Services/IImageService.cs index 4798fb87..784d297d 100644 --- a/src/Eurofurence.App.Backoffice/Services/IImageService.cs +++ b/src/Eurofurence.App.Backoffice/Services/IImageService.cs @@ -5,9 +5,9 @@ namespace Eurofurence.App.Backoffice.Services { public interface IImageService { + public Task GetImageAsync(Guid id); public Task GetImagesAsync(); public Task GetImagesWithRelationsAsync(); - public Task GetImageContentAsync(Guid id); public Task PutImageAsync(Guid id, IBrowserFile file); public Task PostImageAsync(IBrowserFile file); public Task DeleteImageAsync(Guid id); diff --git a/src/Eurofurence.App.Backoffice/Services/ImageService.cs b/src/Eurofurence.App.Backoffice/Services/ImageService.cs index 5b2e9020..6be18f65 100644 --- a/src/Eurofurence.App.Backoffice/Services/ImageService.cs +++ b/src/Eurofurence.App.Backoffice/Services/ImageService.cs @@ -9,6 +9,11 @@ namespace Eurofurence.App.Backoffice.Services { public class ImageService(HttpClient http) : IImageService { + public async Task GetImageAsync(Guid id) + { + return (await http.GetFromJsonAsync($"images/{id}")); + } + public async Task GetImagesAsync() { return (await http.GetFromJsonAsync("images"))?.Where(ke => ke.IsDeleted != 1).ToArray() ?? []; @@ -19,18 +24,6 @@ public async Task GetImagesWithRelationsAsync() return (await http.GetFromJsonAsync("images/with-relations"))?.Where(ke => ke.IsDeleted != 1).ToArray() ?? []; } - public async Task GetImageContentAsync(Guid id) - { - try - { - return Convert.ToBase64String(await http.GetByteArrayAsync($"images/{id}/Content")); - } - catch - { - return string.Empty; - } - } - public async Task PutImageAsync(Guid id, IBrowserFile file) { using (var content = new MultipartFormDataContent()) From be9fb3568fc6733c5ab3bc27ecf5216641439eb5 Mon Sep 17 00:00:00 2001 From: Fenrikur <3359222+Fenrikur@users.noreply.github.com> Date: Sun, 4 Aug 2024 16:45:57 +0200 Subject: [PATCH 5/7] fix(migrations): missing DbContext for ef migrations --- Dockerfile | 2 +- docker-compose.yml | 2 ++ .../AppDbContext.cs | 24 ++++++++++++++++++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 40b2c814..a512505e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/docker-compose.yml b/docker-compose.yml index 07484e72..7da3634f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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 diff --git a/src/Eurofurence.App.Infrastructure.EntityFramework/AppDbContext.cs b/src/Eurofurence.App.Infrastructure.EntityFramework/AppDbContext.cs index 7b744b21..d3dded44 100644 --- a/src/Eurofurence.App.Infrastructure.EntityFramework/AppDbContext.cs +++ b/src/Eurofurence.App.Infrastructure.EntityFramework/AppDbContext.cs @@ -1,4 +1,5 @@ -using Eurofurence.App.Domain.Model.Announcements; +using System; +using Eurofurence.App.Domain.Model.Announcements; using Eurofurence.App.Domain.Model.ArtistsAlley; using Eurofurence.App.Domain.Model.ArtShow; using Eurofurence.App.Domain.Model.CollectionGame; @@ -22,6 +23,7 @@ namespace Eurofurence.App.Infrastructure.EntityFramework { public class AppDbContext : DbContext { + public AppDbContext() { } public AppDbContext(DbContextOptions options) : base(options) { @@ -60,6 +62,26 @@ public AppDbContext(DbContextOptions options) public virtual DbSet Topics { get; set; } public virtual DbSet LinkFragments { get; set; } + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + if (!optionsBuilder.IsConfigured) + { + var connectionString = "Server=db; Port=3306; Database=ef_backend; user=root; SslMode=Preferred;"; + + var serverVersionString = Environment.GetEnvironmentVariable("MYSQL_VERSION"); + ServerVersion serverVersion; + if (string.IsNullOrEmpty(serverVersionString) || !ServerVersion.TryParse(serverVersionString, out serverVersion)) + { + serverVersion = ServerVersion.AutoDetect(connectionString); + } + + optionsBuilder.UseMySql( + connectionString, + serverVersion, + mySqlOptions => mySqlOptions.UseMicrosoftJson()); + } + } + protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); From a81757c43f61fa86123cf2c19464575ed93c665f Mon Sep 17 00:00:00 2001 From: Fenrikur <3359222+Fenrikur@users.noreply.github.com> Date: Sun, 4 Aug 2024 16:47:27 +0200 Subject: [PATCH 6/7] fix(logging): reduce noise by using debug level --- .../Jobs/FlushPrivateMessageNotificationsJob.cs | 2 +- src/Eurofurence.App.Server.Web/Jobs/UpdateAnnouncementsJob.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Eurofurence.App.Server.Web/Jobs/FlushPrivateMessageNotificationsJob.cs b/src/Eurofurence.App.Server.Web/Jobs/FlushPrivateMessageNotificationsJob.cs index 5b4d7eaf..0624b757 100644 --- a/src/Eurofurence.App.Server.Web/Jobs/FlushPrivateMessageNotificationsJob.cs +++ b/src/Eurofurence.App.Server.Web/Jobs/FlushPrivateMessageNotificationsJob.cs @@ -22,7 +22,7 @@ IPrivateMessageService privateMessageService public Task Execute(IJobExecutionContext context) { - _logger.LogInformation($"Starting job {context.JobDetail.Key.Name}"); + _logger.LogDebug($"Starting job {context.JobDetail.Key.Name}"); try { diff --git a/src/Eurofurence.App.Server.Web/Jobs/UpdateAnnouncementsJob.cs b/src/Eurofurence.App.Server.Web/Jobs/UpdateAnnouncementsJob.cs index e4ee1339..b5fde6e9 100644 --- a/src/Eurofurence.App.Server.Web/Jobs/UpdateAnnouncementsJob.cs +++ b/src/Eurofurence.App.Server.Web/Jobs/UpdateAnnouncementsJob.cs @@ -51,7 +51,7 @@ public async Task Execute(IJobExecutionContext context) var url = _configuration.Url; if (string.IsNullOrWhiteSpace(url)) { - _logger.LogError(LogEvents.Import, "Empty source url; cancelling job", url); + _logger.LogError(LogEvents.Import, "Empty source url; cancelling job"); return; } From 56940cba15c7f125d72843126895fcda80f16975 Mon Sep 17 00:00:00 2001 From: Fenrikur <3359222+Fenrikur@users.noreply.github.com> Date: Sun, 4 Aug 2024 17:26:12 +0200 Subject: [PATCH 7/7] fix(minio): support baseUrl for private endpoints --- .../Abstractions/MinIO/MinIOConfiguration.cs | 2 ++ src/Eurofurence.App.Server.Services/Images/ImageService.cs | 4 ++-- src/Eurofurence.App.Server.Web/appsettings.sample.json | 5 +++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Eurofurence.App.Server.Services/Abstractions/MinIO/MinIOConfiguration.cs b/src/Eurofurence.App.Server.Services/Abstractions/MinIO/MinIOConfiguration.cs index 3c5314ae..ea8d5692 100644 --- a/src/Eurofurence.App.Server.Services/Abstractions/MinIO/MinIOConfiguration.cs +++ b/src/Eurofurence.App.Server.Services/Abstractions/MinIO/MinIOConfiguration.cs @@ -5,6 +5,7 @@ namespace Eurofurence.App.Server.Services.Abstractions.MinIO public class MinIoConfiguration { public string Endpoint { get; set; } + public string BaseUrl { get; set; } public string Region { get; set; } public string AccessKey { get; set; } public string SecretKey { get; set; } @@ -15,6 +16,7 @@ public static MinIoConfiguration FromConfiguration(IConfiguration configuration) => new() { Endpoint = configuration["minIo:endpoint"], + BaseUrl = configuration["minIo:baseUrl"] ?? configuration["minIo:endpoint"], Region = configuration["minIo:region"] ?? "us-east-1", AccessKey = configuration["minIo:accessKey"], SecretKey = configuration["minIo:secretKey"], diff --git a/src/Eurofurence.App.Server.Services/Images/ImageService.cs b/src/Eurofurence.App.Server.Services/Images/ImageService.cs index f78cc0a7..1e24bcf3 100644 --- a/src/Eurofurence.App.Server.Services/Images/ImageService.cs +++ b/src/Eurofurence.App.Server.Services/Images/ImageService.cs @@ -119,7 +119,7 @@ public async Task InsertImageAsync(string internalReference, Stream { Id = guid, InternalFileName = fileName, - Url = $"{_minIoClient.Config.Endpoint}/{_minIoConfiguration.Bucket}/{fileName}", + Url = $"{_minIoConfiguration.BaseUrl ?? _minIoClient.Config.BaseUrl}/{_minIoConfiguration.Bucket}/{fileName}", InternalReference = internalReference, IsDeleted = 0, MimeType = imageFormat?.DefaultMimeType, @@ -167,7 +167,7 @@ await UploadFileToMinIoAsync(_minIoConfiguration.Bucket, existingRecord.Internal imageFormat?.DefaultMimeType, stream); existingRecord.InternalFileName = fileName; - existingRecord.Url = $"{_minIoClient.Config.Endpoint}/{_minIoConfiguration.Bucket}/{fileName}"; + existingRecord.Url = $"{_minIoConfiguration.BaseUrl ?? _minIoClient.Config.BaseUrl}/{_minIoConfiguration.Bucket}/{fileName}"; existingRecord.InternalReference = internalReference; existingRecord.MimeType = imageFormat?.DefaultMimeType; existingRecord.Width = image.Width; diff --git a/src/Eurofurence.App.Server.Web/appsettings.sample.json b/src/Eurofurence.App.Server.Web/appsettings.sample.json index a4c59016..6e8eef8b 100644 --- a/src/Eurofurence.App.Server.Web/appsettings.sample.json +++ b/src/Eurofurence.App.Server.Web/appsettings.sample.json @@ -22,7 +22,7 @@ "baseUrl": "", "appIdITunes": 0, "appIdPlay": "", - "workingDirectory": "/tmp/workingDir" // Working directory to store temporary data, e.g. the dealers export file + "workingDirectory": "/tmp/workingDir" }, "logLevel": 1, "auditLog": "/tmp/audit.log", @@ -109,10 +109,11 @@ "url": "" }, "events": { - "url": "" // URL to statically hosted events csv export file + "url": "" }, "minIo": { "endpoint": "minio:9000", + "baseUrl": "http://127.0.0.1:9000", "region": "us-east-1", "accessKey": "minioAccessKey", "secretKey": "minioVerySecretKey",