diff --git a/docs/pages/installation.md b/docs/pages/installation.md index 7d88cb90..7fad538b 100644 --- a/docs/pages/installation.md +++ b/docs/pages/installation.md @@ -66,6 +66,8 @@ The following environment variables can be set if you want to enable specific fe | `Turnierplan__LogoUrl` | The URL of the custom logo to be displayed in the header of the public pages. If not specified, the turnierplan.NET logo will be shown instead. | - | | `Turnierplan__ImprintUrl` | The URL of your external imprint page if you want it to be linked on the public pages. | - | | `Turnierplan__PrivacyUrl` | The URL of your external privacy page if you want it to be linked on the public pages. | - | +| `Turnierplan__ImageMaxSize` | The maximum allowed file size when uploading an image file. The default value equates to 8 MiB (8 · 1024 · 1024) | `8388608` | +| `Turnierplan__ImageQuality` | Uploaded images are compressed using the `webp` format with the specified quality. A value of `100` will result in lossless compression being uesd. | `80` | !!! note The token lifetimes must be specified as .NET `TimeSpan` strings. For example `00:03:00` means 3 minutes or `1.00:00.00` means 1 day. diff --git a/src/Turnierplan.App.Test.Unit/Security/AccessValidatorTest.cs b/src/Turnierplan.App.Test.Unit/Security/AccessValidatorTest.cs index 56142d97..cf7f5495 100644 --- a/src/Turnierplan.App.Test.Unit/Security/AccessValidatorTest.cs +++ b/src/Turnierplan.App.Test.Unit/Security/AccessValidatorTest.cs @@ -46,7 +46,7 @@ public void IsActionAllowed___When_Called_With_Indirect_Target___Returns_Expecte organization.AddRoleAssignment(Role.Contributor, otherPrincipal); Test(() => new ApiKey(organization, "Test", null, DateTime.MaxValue)); - Test(() => new Image(organization, "Test", ImageType.Logo, "", 0, 1, 1)); + Test(() => new Image(organization, "Test", "", 0, 1, 1)); Test(() => new Folder(organization, "Test")); Test(() => new Tournament(organization, "Test", Visibility.Public)); Test(() => new Venue(organization, "Test", "")); @@ -121,7 +121,7 @@ public void AddAvailableRoles___When_Called_With_Indirect_Target___Returns_Expec organization.AddRoleAssignment(Role.Contributor, otherPrincipal); Test(() => new ApiKey(organization, "Test", null, DateTime.MaxValue)); - Test(() => new Image(organization, "Test", ImageType.Logo, "", 0, 1, 1)); + Test(() => new Image(organization, "Test", "", 0, 1, 1)); Test(() => new Folder(organization, "Test")); Test(() => new Tournament(organization, "Test", Visibility.Public)); Test(() => new Venue(organization, "Test", "")); @@ -260,12 +260,10 @@ public void AddRolesToResponseHeader___When_Called_Such_That_Entities_Are_Proces var headers = httpContextAccessor.HttpContext!.Response.Headers; var headerValues = headers["X-Turnierplan-Roles"]; - var organizationId = organization.PublicId.ToString(); var tournamentId1 = tournament1.PublicId.ToString(); var tournamentId2 = tournament2.PublicId.ToString(); - headerValues.Should().HaveCount(3); - headerValues.Single(x => x!.StartsWith(organizationId)).Should().Be($"{organizationId}=Reader"); + headerValues.Should().HaveCount(2); headerValues.Single(x => x!.StartsWith(tournamentId1)).Should().Be($"{tournamentId1}=Owner+Reader+Contributor"); headerValues.Single(x => x!.StartsWith(tournamentId2)).Should().Be($"{tournamentId2}=Reader+Contributor"); } diff --git a/src/Turnierplan.App/Client/src/app/i18n/de.ts b/src/Turnierplan.App/Client/src/app/i18n/de.ts index 49d6da23..e7e70a71 100644 --- a/src/Turnierplan.App/Client/src/app/i18n/de.ts +++ b/src/Turnierplan.App/Client/src/app/i18n/de.ts @@ -170,6 +170,7 @@ export const de = { Tournaments: 'Turniere', Venues: 'Spielstätten', PlanningRealms: 'Turnierplaner', + Images: 'Bilder', ApiKeys: 'API-Schlüssel', Settings: 'Einstellungen' }, @@ -177,11 +178,14 @@ export const de = { TournamentCount: 'Turniere', VenueCount: 'Spielstätten', PlanningRealmCount: 'Turnierplaner', + ImagesCount: 'Bilder', + ImagesTotalSize: 'Gesamtgröße', ApiKeyCount: 'API-Schlüssel' }, NewTournament: 'Neues Turnier', NewVenue: 'Neue Spielstätte', NewPlanningRealm: 'Neuer Turnierplaner', + UploadImage: 'Bild hochladen', NewApiKey: 'Neuer API-Schlüssel', NoTournaments: 'In dieser Organisation gibt es aktuell keine Turniere.\nErstellen Sie ein Turner mit der Schaltfläche oben rechts.', NoVenues: @@ -211,6 +215,26 @@ export const de = { NoDescription: 'Keine Beschreibung vorhanden', Open: 'öffnen' }, + Images: { + Preview: 'Vorschau', + Dimensions: '{{w}} x {{h}} px', + CreatedAt: 'Hochgeladen am', + Name: 'Name', + References: { + Header: 'Verwendungen', + Tooltip: 'Gibt an, von wie vielen Turnieren wird dieses Bild verwendet wird', + None: 'keine' + }, + NoImages: 'Es sind aktuell keine Bilder vorhanden.', + Rename: { + Title: 'Bild umbenennen', + EnterNewName: 'Geben Sie den neuen Namen für das Bild ein:' + }, + DeleteToast: { + Title: 'Bild wurde gelöscht', + Message: 'Das Bild wurde gelöscht.' + } + }, ApiKeys: { TableLabel: 'API Schlüssel dieser Organisation', Id: 'ID', @@ -598,7 +622,9 @@ export const de = { } }, EditImages: { - Title: 'Logos & Bilddateien' + Title: 'Logos & Bilddateien', + BannerAspectRatioWarning: + 'Das aktuelle Banner hat eine Auflösung von {{w}} x {{h}} px und ein Seitenverhältnis von {{ratio}}:1. Für eine optimale Darstellung verwenden Sie ein Bild mit einem Seitenverhältnis von mindestens 3:1' }, MoveToAnotherFolder: { Title: 'Turnier verschieben', @@ -1199,6 +1225,20 @@ export const de = { } } }, + UploadImage: { + Title: 'Bild hochladen', + Form: { + File: 'Datei auswählen:', + Name: 'Name:', + NameInvalid: 'Der angegebene Name ist ungültig' + }, + NameTooltip: 'Wenn der Name leergelassen wird, wird der Dateinahme der gewählten Datei als Name verwendet.', + Preview: 'Bildvorschau:', + PreviewAlt: 'Bildvorschau der Datei "{{fileName}}"', + OrganizationNotice: + 'Es wird ein neues Bild in der Organisation {{organizationName}} hochgeladen. Das Bild kann anschließend von allen Turnieren und Turnierplanern innerhalb der Organisation verwendet werden.', + Submit: 'Hochladen' + }, CreateApiKey: { Title: 'Neuen API-Schlüssel erstellen', Form: { @@ -1260,23 +1300,11 @@ export const de = { Banner: 'Banner' }, ImageChooser: { - Title: 'Bild hochladen oder auswählen', + Title: 'Bild auswählen', Remove: 'Bild entfernen', - NoImages: 'Laden Sie Ihr erstes Bild hoch...', - Upload: 'Hochladen', - UploadFailed: 'Das Bild konnte nicht hochgeladen werden. Prüfen Sie die Maße und die maximale Dateigröße.', - Constraints: { - Logo: 'Das Bild muss quadratisch sein mit einer Auflösung zwischen 50x50 und 3000x3000 Pixel. Die maximale Dateigröße beträgt 8 MB.', - Banner: - 'Das Bild muss mindestens 50px hoch sein und darf maximal 3000px breit sein. Das Seitenverhältnis muss zwischen 3:1 und 5:1 liegen. Die maximale Dateigröße beträgt 8 MB.' - }, - DetailView: { - Title: 'Hier sehen Sie die Detailinformationen zu folgendem Bild:', - Name: 'Dateiname: {{value}}', - CreatedAt: 'Hochgeladen am: {{value}}', - FileSize: 'Dateigröße: {{value}} KB', - Resolution: 'Auflösung: {{width}}x{{height}} px' - } + NoImages: 'In dieser Organisation wurden bisher noch keine Bilder hochgeladen.', + UploadViaOrgPage: 'Neue Bilder können Sie auf der Seite der Organisation hochladen.', + Dimensions: '{{w}} x {{h}} px' }, MultiSelectFilter: { All: 'alle', @@ -1352,6 +1380,10 @@ export const de = { Tooltip: 'Ordner', NotInherited: 'Zuweisung liegt auf diesem Ordner' }, + Image: { + Tooltip: 'Bild', + NotInherited: 'Zuweisung liegt auf diesem Bild' + }, Organization: { Tooltip: 'Organisation', NotInherited: 'Zuweisung liegt auf dieser Organisation' diff --git a/src/Turnierplan.App/Client/src/app/portal/components/image-chooser/image-chooser.component.html b/src/Turnierplan.App/Client/src/app/portal/components/image-chooser/image-chooser.component.html index 7505ffbd..698a7ac6 100644 --- a/src/Turnierplan.App/Client/src/app/portal/components/image-chooser/image-chooser.component.html +++ b/src/Turnierplan.App/Client/src/app/portal/components/image-chooser/image-chooser.component.html @@ -8,52 +8,14 @@
| + | + | + |
+
+ |
+ + | + | |
|---|---|---|---|---|---|---|
|
+ {{ image.fileSize | fileSize: 'de' }}
+ |
+ {{ image.createdAt | translateDate }} | +
+ @if (image.isUpdatingName) {
+ |
+ + {{ image.name }} + | ++ @if (image.referenceCount === 0) { + + } @else if (image.referenceCount !== undefined) { + {{ image.referenceCount }} + } + | +
+ |
+
+ @if (writeAllowed) {
+ |
+
| + | ||||||
-/// export enum ImageType {
+/// export enum MatchType {
/// A = 'A',
/// B = 'B',
/// C = 'C'
diff --git a/src/Turnierplan.App/Options/TurnierplanOptions.cs b/src/Turnierplan.App/Options/TurnierplanOptions.cs
index aac4dcb2..6ad9ecff 100644
--- a/src/Turnierplan.App/Options/TurnierplanOptions.cs
+++ b/src/Turnierplan.App/Options/TurnierplanOptions.cs
@@ -12,6 +12,10 @@ internal sealed record TurnierplanOptions
public string? PrivacyUrl { get; init; }
+ public int? ImageMaxSize { get; init; }
+
+ public int? ImageQuality { get; init; }
+
public string? InitialUserName { get; init; }
public string? InitialUserPassword { get; init; }
diff --git a/src/Turnierplan.App/Security/AccessValidator.cs b/src/Turnierplan.App/Security/AccessValidator.cs
index 85338670..e3077d84 100644
--- a/src/Turnierplan.App/Security/AccessValidator.cs
+++ b/src/Turnierplan.App/Security/AccessValidator.cs
@@ -82,15 +82,6 @@ public void AddRolesToResponseHeader(IEntityWithRoleAssignments target)
var rolesHeaderValue = $"{targetPublicId}={rolesList}";
_httpContext.Response.Headers.Append(RolesHeaderName, rolesHeaderValue);
-
- if (target is IEntityWithOrganization entityWithOrganization)
- {
- // TODO: Evaluate if this is still necessary after #309 / #324
- // Always add the organization-level roles, too. This is necessary because for example, the tournament
- // page allows the user to upload images. But for uploading images, the authorization check is done
- // against the organization.
- AddRolesToResponseHeader(entityWithOrganization.Organization);
- }
}
internal static bool IsActionAllowed(IEntityWithRoleAssignments target, Actions.Action action, Principal principal)
diff --git a/src/Turnierplan.App/appsettings.json b/src/Turnierplan.App/appsettings.json
index 868a4bad..78852a72 100644
--- a/src/Turnierplan.App/appsettings.json
+++ b/src/Turnierplan.App/appsettings.json
@@ -27,6 +27,8 @@
"Turnierplan": {
"ApplicationUrl": "",
"ImprintUrl": "",
- "PrivacyUrl": ""
+ "PrivacyUrl": "",
+ "ImageMaxSize": 8388608,
+ "ImageQuality": 80
}
}
diff --git a/src/Turnierplan.Core/Image/Image.cs b/src/Turnierplan.Core/Image/Image.cs
index ec8a173b..2dd0149d 100644
--- a/src/Turnierplan.Core/Image/Image.cs
+++ b/src/Turnierplan.Core/Image/Image.cs
@@ -1,5 +1,4 @@
using Turnierplan.Core.Entity;
-using Turnierplan.Core.Exceptions;
using Turnierplan.Core.RoleAssignment;
namespace Turnierplan.Core.Image;
@@ -8,10 +7,8 @@ public sealed class Image : Entity, IEntityWithRoleAssignments, IEn
{
internal readonly List> _roleAssignments = [];
- public Image(Organization.Organization organization, string name, ImageType type, string fileType, long fileSize, ushort width, ushort height)
+ public Image(Organization.Organization organization, string name, string fileExtension, long fileSize, ushort width, ushort height)
{
- ValidateImageSize(type, width, height);
-
organization._images.Add(this);
Id = 0;
@@ -20,22 +17,20 @@ public Image(Organization.Organization organization, string name, ImageType type
Organization = organization;
CreatedAt = DateTime.UtcNow;
Name = name;
- Type = type;
- FileType = fileType;
+ FileExtension = fileExtension;
FileSize = fileSize;
Width = width;
Height = height;
}
- internal Image(long id, Guid resourceIdentifier, PublicId.PublicId publicId, DateTime createdAt, string name, ImageType type, string fileType, long fileSize, ushort width, ushort height)
+ internal Image(long id, Guid resourceIdentifier, PublicId.PublicId publicId, DateTime createdAt, string name, string fileExtension, long fileSize, ushort width, ushort height)
{
Id = id;
ResourceIdentifier = resourceIdentifier;
PublicId = publicId;
CreatedAt = createdAt;
Name = name;
- Type = type;
- FileType = fileType;
+ FileExtension = fileExtension;
FileSize = fileSize;
Width = width;
Height = height;
@@ -55,9 +50,7 @@ internal Image(long id, Guid resourceIdentifier, PublicId.PublicId publicId, Dat
public string Name { get; set; }
- public ImageType Type { get; }
-
- public string FileType { get; }
+ public string FileExtension { get; }
public long FileSize { get; }
@@ -77,14 +70,4 @@ public void RemoveRoleAssignment(RoleAssignment roleAssignment)
{
_roleAssignments.Remove(roleAssignment);
}
-
- private static void ValidateImageSize(ImageType type, ushort width, ushort height)
- {
- var constraints = ImageConstraints.GetImageConstraints(type);
-
- if (!constraints.IsSizeValid(width, height))
- {
- throw new TurnierplanException($"Image with size {width}x{height} does not meet criteria of type {type}: {constraints}");
- }
- }
}
diff --git a/src/Turnierplan.Core/Image/ImageConstraints.cs b/src/Turnierplan.Core/Image/ImageConstraints.cs
deleted file mode 100644
index bdbfcbd3..00000000
--- a/src/Turnierplan.Core/Image/ImageConstraints.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-namespace Turnierplan.Core.Image;
-
-public sealed class ImageConstraints
-{
- private static readonly ImageConstraints __logoConstraints = new(true);
- private static readonly ImageConstraints __bannerConstraints = new(false, 3, 5);
-
- private readonly bool _mustBeSquare;
- private readonly float? _minimumAspectRatio;
- private readonly float? _maximumAspectRatio;
-
- private ImageConstraints(bool mustBeSquare, float? minimumAspectRatio = null, float? maximumAspectRatio = null)
- {
- if (mustBeSquare)
- {
- if (minimumAspectRatio is not null || maximumAspectRatio is not null)
- {
- throw new ArgumentException("Aspect ratio cannot be set if mustBeSquare is true.");
- }
-
- _mustBeSquare = true;
- }
- else
- {
- _mustBeSquare = false;
- _minimumAspectRatio = minimumAspectRatio ?? throw new ArgumentNullException(nameof(minimumAspectRatio));
- _maximumAspectRatio = maximumAspectRatio ?? throw new ArgumentNullException(nameof(maximumAspectRatio));
- }
- }
-
- public bool IsSizeValid(ushort width, ushort height)
- {
- if (width == 0 || height == 0)
- {
- return false;
- }
-
- if (_mustBeSquare)
- {
- return width == height;
- }
-
- var aspectRatio = (float)width / height;
-
- return aspectRatio >= _minimumAspectRatio && aspectRatio <= _maximumAspectRatio;
- }
-
- public override string ToString()
- {
- return _mustBeSquare
- ? "Must be square"
- : $"Must have aspect ratio between {_minimumAspectRatio:F2} and {_maximumAspectRatio:F2}";
- }
-
- public static ImageConstraints GetImageConstraints(ImageType imageType)
- {
- return imageType switch
- {
- ImageType.Logo => __logoConstraints,
- ImageType.Banner => __bannerConstraints,
- _ => throw new ArgumentOutOfRangeException(nameof(imageType), imageType, "Invalid image type specified.")
- };
- }
-}
diff --git a/src/Turnierplan.Core/Image/ImageType.cs b/src/Turnierplan.Core/Image/ImageType.cs
deleted file mode 100644
index b91cf30b..00000000
--- a/src/Turnierplan.Core/Image/ImageType.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace Turnierplan.Core.Image;
-
-public enum ImageType
-{
- // Note: Don't change enum values (DB serialization)
-
- Logo = 1,
- Banner = 2
-}
diff --git a/src/Turnierplan.Core/PlanningRealm/InvitationLink.cs b/src/Turnierplan.Core/PlanningRealm/InvitationLink.cs
index 126df461..d31eb4e8 100644
--- a/src/Turnierplan.Core/PlanningRealm/InvitationLink.cs
+++ b/src/Turnierplan.Core/PlanningRealm/InvitationLink.cs
@@ -1,7 +1,6 @@
using System.Text.Json.Serialization;
using Turnierplan.Core.Entity;
using Turnierplan.Core.Exceptions;
-using Turnierplan.Core.Image;
namespace Turnierplan.Core.PlanningRealm;
@@ -85,12 +84,12 @@ public void RemoveEntry(InvitationLinkEntry entry)
public void SetPrimaryLogo(Image.Image? primaryLogo)
{
- CheckImageTypeAndSetImage(primaryLogo, () => PrimaryLogo = primaryLogo);
+ ValidateAndSetImage(primaryLogo, () => PrimaryLogo = primaryLogo);
}
public void SetSecondaryLogo(Image.Image? secondaryLogo)
{
- CheckImageTypeAndSetImage(secondaryLogo, () => SecondaryLogo = secondaryLogo);
+ ValidateAndSetImage(secondaryLogo, () => SecondaryLogo = secondaryLogo);
}
public bool IsValidUntilSurpassed()
@@ -98,7 +97,7 @@ public bool IsValidUntilSurpassed()
return ValidUntil.HasValue && ValidUntil.Value < DateTime.UtcNow;
}
- private void CheckImageTypeAndSetImage(Image.Image? provided, Action apply)
+ private void ValidateAndSetImage(Image.Image? provided, Action apply)
{
if (provided is null)
{
@@ -111,11 +110,6 @@ private void CheckImageTypeAndSetImage(Image.Image? provided, Action apply)
throw new TurnierplanException("Cannot assign an image from another organization.");
}
- if (provided.Type != ImageType.Logo)
- {
- throw new TurnierplanException($"Cannot assign image because the image's type is not the expected type '{ImageType.Logo}'.");
- }
-
apply();
}
diff --git a/src/Turnierplan.Core/Tournament/Tournament.cs b/src/Turnierplan.Core/Tournament/Tournament.cs
index e1e140a1..6b8cb1fa 100644
--- a/src/Turnierplan.Core/Tournament/Tournament.cs
+++ b/src/Turnierplan.Core/Tournament/Tournament.cs
@@ -1,7 +1,6 @@
using Turnierplan.Core.Entity;
using Turnierplan.Core.Exceptions;
using Turnierplan.Core.Extensions;
-using Turnierplan.Core.Image;
using Turnierplan.Core.RoleAssignment;
using Turnierplan.Core.Tournament.Comparers;
using Turnierplan.Core.Tournament.Definitions;
@@ -304,17 +303,17 @@ public void SetVenue(Venue.Venue? venue)
public void SetPrimaryLogo(Image.Image? primaryLogo)
{
- CheckImageTypeAndSetImage(primaryLogo, ImageType.Logo, () => PrimaryLogo = primaryLogo);
+ ValidateAndSetImage(primaryLogo, () => PrimaryLogo = primaryLogo);
}
public void SetSecondaryLogo(Image.Image? secondaryLogo)
{
- CheckImageTypeAndSetImage(secondaryLogo, ImageType.Logo, () => SecondaryLogo = secondaryLogo);
+ ValidateAndSetImage(secondaryLogo, () => SecondaryLogo = secondaryLogo);
}
public void SetBannerImage(Image.Image? bannerImage)
{
- CheckImageTypeAndSetImage(bannerImage, ImageType.Banner, () => BannerImage = bannerImage);
+ ValidateAndSetImage(bannerImage, () => BannerImage = bannerImage);
}
public void ShiftToTimezone(TimeZoneInfo timeZone)
@@ -1075,7 +1074,7 @@ private int[] GetGroupIdsForConvertingAbstractTeamSelector()
.ToArray();
}
- private void CheckImageTypeAndSetImage(Image.Image? provided, ImageType expectedType, Action apply)
+ private void ValidateAndSetImage(Image.Image? provided, Action apply)
{
if (provided is null)
{
@@ -1088,11 +1087,6 @@ private void CheckImageTypeAndSetImage(Image.Image? provided, ImageType expected
throw new TurnierplanException("Cannot assign an image from another organization.");
}
- if (provided.Type != expectedType)
- {
- throw new TurnierplanException($"Cannot assign image because the image's type is not the expected type '{expectedType}'.");
- }
-
apply();
}
diff --git a/src/Turnierplan.Dal/EntityConfigurations/ImageEntityTypeConfiguration.cs b/src/Turnierplan.Dal/EntityConfigurations/ImageEntityTypeConfiguration.cs
index a2027120..67b50ff1 100644
--- a/src/Turnierplan.Dal/EntityConfigurations/ImageEntityTypeConfiguration.cs
+++ b/src/Turnierplan.Dal/EntityConfigurations/ImageEntityTypeConfiguration.cs
@@ -40,10 +40,7 @@ public void Configure(EntityTypeBuilder builder)
builder.Property(x => x.Name)
.IsRequired();
- builder.Property(x => x.Type)
- .IsRequired();
-
- builder.Property(x => x.FileType)
+ builder.Property(x => x.FileExtension)
.IsRequired();
builder.Property(x => x.FileSize)
diff --git a/src/Turnierplan.Dal/Migrations/20260201142441_Remove_ImageType.Designer.cs b/src/Turnierplan.Dal/Migrations/20260201142441_Remove_ImageType.Designer.cs
new file mode 100644
index 00000000..737fee25
--- /dev/null
+++ b/src/Turnierplan.Dal/Migrations/20260201142441_Remove_ImageType.Designer.cs
@@ -0,0 +1,1881 @@
+//
+using System;
+using System.Collections.Generic;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+using Turnierplan.Dal;
+
+#nullable disable
+
+namespace Turnierplan.Dal.Migrations
+{
+ [DbContext(typeof(TurnierplanContext))]
+ [Migration("20260201142441_Remove_ImageType")]
+ partial class Remove_ImageType
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "10.0.0")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("ApplicationTeamLabel", b =>
+ {
+ b.Property("ApplicationTeamId")
+ .HasColumnType("bigint");
+
+ b.Property("LabelsId")
+ .HasColumnType("bigint");
+
+ b.HasKey("ApplicationTeamId", "LabelsId");
+
+ b.HasIndex("LabelsId");
+
+ b.ToTable("ApplicationTeamLabel", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.ApiKey.ApiKey", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ExpiryDate")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("IsActive")
+ .HasColumnType("boolean");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("OrganizationId")
+ .HasColumnType("bigint");
+
+ b.Property("PrincipalId")
+ .HasColumnType("uuid");
+
+ b.Property("PublicId")
+ .HasColumnType("bigint");
+
+ b.Property("SecretHash")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("OrganizationId");
+
+ b.HasIndex("PrincipalId")
+ .IsUnique();
+
+ b.HasIndex("PublicId")
+ .IsUnique();
+
+ b.ToTable("ApiKeys", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.ApiKey.ApiKeyRequest", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ApiKeyId")
+ .HasColumnType("bigint");
+
+ b.Property("Path")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Timestamp")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ApiKeyId");
+
+ b.HasIndex("Timestamp");
+
+ b.ToTable("ApiKeyRequests", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.Document.Document", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Configuration")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("GenerationCount")
+ .HasColumnType("integer");
+
+ b.Property("LastGeneration")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("LastModifiedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("PublicId")
+ .HasColumnType("bigint");
+
+ b.Property("TournamentId")
+ .HasColumnType("bigint");
+
+ b.Property("Type")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("PublicId")
+ .IsUnique();
+
+ b.HasIndex("TournamentId");
+
+ b.ToTable("Documents", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.Folder.Folder", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("OrganizationId")
+ .HasColumnType("bigint");
+
+ b.Property("PublicId")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("OrganizationId");
+
+ b.HasIndex("PublicId")
+ .IsUnique();
+
+ b.ToTable("Folders", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.Image.Image", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("FileSize")
+ .HasColumnType("bigint");
+
+ b.Property("FileType")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Height")
+ .HasColumnType("integer");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("OrganizationId")
+ .HasColumnType("bigint");
+
+ b.Property("PublicId")
+ .HasColumnType("bigint");
+
+ b.Property("ResourceIdentifier")
+ .HasColumnType("uuid");
+
+ b.Property("Width")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("OrganizationId");
+
+ b.HasIndex("PublicId")
+ .IsUnique();
+
+ b.HasIndex("ResourceIdentifier")
+ .IsUnique();
+
+ b.ToTable("Images", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.Organization.Organization", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("PublicId")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("PublicId")
+ .IsUnique();
+
+ b.ToTable("Organizations", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.PlanningRealm.Application", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Comment")
+ .HasColumnType("text");
+
+ b.Property("Contact")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ContactEmail")
+ .HasColumnType("text");
+
+ b.Property("ContactTelephone")
+ .HasColumnType("text");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("FormSession")
+ .HasColumnType("uuid");
+
+ b.Property("Notes")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("PlanningRealmId")
+ .HasColumnType("bigint");
+
+ b.Property("SourceLinkId")
+ .HasColumnType("bigint");
+
+ b.Property("Tag")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("FormSession")
+ .IsUnique();
+
+ b.HasIndex("PlanningRealmId");
+
+ b.HasIndex("SourceLinkId");
+
+ b.ToTable("Applications", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.PlanningRealm.ApplicationChangeLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ApplicationId")
+ .HasColumnType("bigint");
+
+ b.Property("Timestamp")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Type")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ApplicationId");
+
+ b.ToTable("ApplicationChangeLogs", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.PlanningRealm.ApplicationTeam", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ApplicationId")
+ .HasColumnType("bigint");
+
+ b.Property("ClassId")
+ .HasColumnType("bigint");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ApplicationId");
+
+ b.HasIndex("ClassId");
+
+ b.ToTable("ApplicationTeams", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.PlanningRealm.InvitationLink", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ColorCode")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ContactEmail")
+ .HasColumnType("text");
+
+ b.Property("ContactPerson")
+ .HasColumnType("text");
+
+ b.Property("ContactTelephone")
+ .HasColumnType("text");
+
+ b.Property("Description")
+ .HasColumnType("text");
+
+ b.Property("IsActive")
+ .HasColumnType("boolean");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("PlanningRealmId")
+ .HasColumnType("bigint");
+
+ b.Property("PrimaryLogoId")
+ .HasColumnType("bigint");
+
+ b.Property("PublicId")
+ .HasColumnType("bigint");
+
+ b.Property("SecondaryLogoId")
+ .HasColumnType("bigint");
+
+ b.Property("Title")
+ .HasColumnType("text");
+
+ b.Property("ValidUntil")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.HasIndex("PlanningRealmId");
+
+ b.HasIndex("PrimaryLogoId");
+
+ b.HasIndex("PublicId")
+ .IsUnique();
+
+ b.HasIndex("SecondaryLogoId");
+
+ b.ToTable("InvitationLinks", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.PlanningRealm.InvitationLinkEntry", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("AllowNewRegistrations")
+ .HasColumnType("boolean");
+
+ b.Property("ClassId")
+ .HasColumnType("bigint");
+
+ b.Property("InvitationLinkId")
+ .HasColumnType("bigint");
+
+ b.Property("MaxTeamsPerRegistration")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ClassId");
+
+ b.HasIndex("InvitationLinkId");
+
+ b.ToTable("InvitationLinkEntries", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.PlanningRealm.Label", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ColorCode")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("PlanningRealmId")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("PlanningRealmId");
+
+ b.ToTable("Labels", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.PlanningRealm.PlanningRealm", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("OrganizationId")
+ .HasColumnType("bigint");
+
+ b.Property("PublicId")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("OrganizationId");
+
+ b.HasIndex("PublicId")
+ .IsUnique();
+
+ b.ToTable("PlanningRealms", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.PlanningRealm.TeamLink", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ApplicationTeamId")
+ .HasColumnType("bigint");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("TeamId")
+ .HasColumnType("integer");
+
+ b.Property("TeamTournamentId")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ApplicationTeamId")
+ .IsUnique();
+
+ b.HasIndex("TeamTournamentId", "TeamId")
+ .IsUnique();
+
+ b.ToTable("TeamLinks", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.PlanningRealm.TournamentClass", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("PlanningRealmId")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("PlanningRealmId");
+
+ b.ToTable("TournamentClasses", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.RoleAssignment.RoleAssignment", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("ApiKeyId")
+ .HasColumnType("bigint");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Principal")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Role")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ApiKeyId");
+
+ b.ToTable("IAM_ApiKey", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.RoleAssignment.RoleAssignment", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("FolderId")
+ .HasColumnType("bigint");
+
+ b.Property("Principal")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Role")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("FolderId");
+
+ b.ToTable("IAM_Folder", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.RoleAssignment.RoleAssignment", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ImageId")
+ .HasColumnType("bigint");
+
+ b.Property("Principal")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Role")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ImageId");
+
+ b.ToTable("IAM_Image", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.RoleAssignment.RoleAssignment", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("OrganizationId")
+ .HasColumnType("bigint");
+
+ b.Property("Principal")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Role")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("OrganizationId");
+
+ b.ToTable("IAM_Organization", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.RoleAssignment.RoleAssignment", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("PlanningRealmId")
+ .HasColumnType("bigint");
+
+ b.Property("Principal")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Role")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("PlanningRealmId");
+
+ b.ToTable("IAM_PlanningRealm", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.RoleAssignment.RoleAssignment", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Principal")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Role")
+ .HasColumnType("integer");
+
+ b.Property("TournamentId")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TournamentId");
+
+ b.ToTable("IAM_Tournament", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.RoleAssignment.RoleAssignment", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Principal")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Role")
+ .HasColumnType("integer");
+
+ b.Property("VenueId")
+ .HasColumnType("bigint");
+
+ b.HasKey("Id");
+
+ b.HasIndex("VenueId");
+
+ b.ToTable("IAM_Venue", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.Tournament.Group", b =>
+ {
+ b.Property("TournamentId")
+ .HasColumnType("bigint");
+
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("AlphabeticalId")
+ .HasColumnType("character(1)");
+
+ b.Property("DisplayName")
+ .HasColumnType("text");
+
+ b.HasKey("TournamentId", "Id");
+
+ b.ToTable("Groups", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.Tournament.GroupParticipant", b =>
+ {
+ b.Property("TournamentId")
+ .HasColumnType("bigint");
+
+ b.Property("GroupId")
+ .HasColumnType("integer");
+
+ b.Property("TeamId")
+ .HasColumnType("integer");
+
+ b.Property("Order")
+ .HasColumnType("integer");
+
+ b.Property("Priority")
+ .HasColumnType("integer");
+
+ b.HasKey("TournamentId", "GroupId", "TeamId");
+
+ b.HasIndex("TournamentId", "TeamId");
+
+ b.ToTable("GroupParticipants", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.Tournament.Match", b =>
+ {
+ b.Property("TournamentId")
+ .HasColumnType("bigint");
+
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("Court")
+ .HasColumnType("smallint");
+
+ b.Property("FinalsRound")
+ .HasColumnType("integer");
+
+ b.Property("GroupId")
+ .HasColumnType("integer");
+
+ b.Property("Index")
+ .HasColumnType("integer");
+
+ b.Property("IsCurrentlyPlaying")
+ .HasColumnType("boolean");
+
+ b.Property("Kickoff")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("OutcomeType")
+ .HasColumnType("integer");
+
+ b.Property("PlayoffPosition")
+ .HasColumnType("integer");
+
+ b.Property("ScoreA")
+ .HasColumnType("integer");
+
+ b.Property("ScoreB")
+ .HasColumnType("integer");
+
+ b.Property("TeamSelectorA")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("TeamSelectorB")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("TournamentId", "Id");
+
+ b.HasIndex("TournamentId", "GroupId");
+
+ b.ToTable("Matches", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.Tournament.RankingOverwrite", b =>
+ {
+ b.Property("TournamentId")
+ .HasColumnType("bigint");
+
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("AssignTeamId")
+ .HasColumnType("integer");
+
+ b.Property("AssignTeamTournamentId")
+ .HasColumnType("bigint");
+
+ b.Property("HideRanking")
+ .HasColumnType("boolean");
+
+ b.Property("PlacementRank")
+ .HasColumnType("integer");
+
+ b.HasKey("TournamentId", "Id");
+
+ b.HasIndex("AssignTeamTournamentId", "AssignTeamId");
+
+ b.ToTable("RankingOverwrites", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.Tournament.Team", b =>
+ {
+ b.Property("TournamentId")
+ .HasColumnType("bigint");
+
+ b.Property("Id")
+ .HasColumnType("integer");
+
+ b.Property("EntryFeePaidAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("OutOfCompetition")
+ .HasColumnType("boolean");
+
+ b.HasKey("TournamentId", "Id");
+
+ b.ToTable("Teams", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.Tournament.Tournament", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("BannerImageId")
+ .HasColumnType("bigint");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("FolderId")
+ .HasColumnType("bigint");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("OrganizationId")
+ .HasColumnType("bigint");
+
+ b.Property("PrimaryLogoId")
+ .HasColumnType("bigint");
+
+ b.Property("PublicId")
+ .HasColumnType("bigint");
+
+ b.Property("PublicPageViews")
+ .HasColumnType("integer");
+
+ b.Property("SecondaryLogoId")
+ .HasColumnType("bigint");
+
+ b.Property("VenueId")
+ .HasColumnType("bigint");
+
+ b.Property("Visibility")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("BannerImageId");
+
+ b.HasIndex("FolderId");
+
+ b.HasIndex("OrganizationId");
+
+ b.HasIndex("PrimaryLogoId");
+
+ b.HasIndex("PublicId")
+ .IsUnique();
+
+ b.HasIndex("SecondaryLogoId");
+
+ b.HasIndex("VenueId");
+
+ b.ToTable("Tournaments", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.User.User", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreatedAt")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("EMail")
+ .HasColumnType("text");
+
+ b.Property("FullName")
+ .HasColumnType("text");
+
+ b.Property("IsAdministrator")
+ .HasColumnType("boolean");
+
+ b.Property("LastLogin")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("LastPasswordChange")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("NormalizedEMail")
+ .HasColumnType("text");
+
+ b.Property("NormalizedUserName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("PasswordHash")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("PrincipalId")
+ .HasColumnType("uuid");
+
+ b.Property("SecurityStamp")
+ .HasColumnType("uuid");
+
+ b.Property("UserName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedEMail")
+ .IsUnique();
+
+ b.HasIndex("NormalizedUserName")
+ .IsUnique();
+
+ b.HasIndex("PrincipalId")
+ .IsUnique();
+
+ b.ToTable("Users", "turnierplan");
+ });
+
+ modelBuilder.Entity("Turnierplan.Core.Venue.Venue", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("bigint");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property