diff --git a/backend/.editorconfig b/backend/.editorconfig index aaf358673..07d258271 100644 --- a/backend/.editorconfig +++ b/backend/.editorconfig @@ -180,5 +180,8 @@ dotnet_diagnostic.SA1615.severity = none # SA1623: Property summary documentation should match accessors dotnet_diagnostic.SA1623.severity = none +# SYSLIB1045: Convert to 'GeneratedRegexAttribute'. +dotnet_diagnostic.SYSLIB1045.severity = none + # xUnit1033: Test classes decorated with 'Xunit.IClassFixture' or 'Xunit.ICollectionFixture' should add a constructor argument of type TFixture dotnet_diagnostic.xUnit1033.severity = none \ No newline at end of file diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/ConnectionStringParser.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/ConnectionStringParser.cs new file mode 100644 index 000000000..3f19a3a2c --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/ConnectionStringParser.cs @@ -0,0 +1,52 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Data.Common; + +namespace Squidex.Infrastructure.Migrations; + +public class ConnectionStringParser +{ + public string? GetHostName(string? source) + { + if (string.IsNullOrEmpty(source)) + { + return null; + } + + try + { + return GetProviderSpecificHostName(source); + } + catch + { + try + { + var builder = new DbConnectionStringBuilder + { + ConnectionString = source + }; + + if (builder.TryGetValue("Server", out var server)) + { + return server?.ToString(); + } + + return null; + } + catch + { + return null; + } + } + } + + protected virtual string? GetProviderSpecificHostName(string source) + { + throw new NotFiniteNumberException(); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/DatabaseMigrator.cs b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/DatabaseMigrator.cs index 8d4861c99..003dbf98d 100644 --- a/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/DatabaseMigrator.cs +++ b/backend/src/Squidex.Data.EntityFramework/Infrastructure/Migrations/DatabaseMigrator.cs @@ -12,7 +12,7 @@ using Squidex.Hosting; namespace Squidex.Infrastructure.Migrations; -public sealed class DatabaseMigrator(IDbContextFactory dbContextFactory) : IInitializable +public sealed class DatabaseMigrator(IDbContextFactory dbContextFactory, ConnectionStringParser parser) : IInitializable where TContext : DbContext { private static readonly TimeSpan WaitTime = TimeSpan.FromSeconds(30); @@ -24,10 +24,28 @@ public sealed class DatabaseMigrator(IDbContextFactory dbCon { await using var dbContext = await dbContextFactory.CreateDbContextAsync(ct); - using var cts = new CancellationTokenSource(WaitTime); - while (!await dbContext.Database.CanConnectAsync(cts.Token)) + try { - await Task.Delay(100, cts.Token); + using var cts = new CancellationTokenSource(WaitTime); + while (!await dbContext.Database.CanConnectAsync(cts.Token)) + { + await Task.Delay(100, cts.Token); + } + + cts.Token.ThrowIfCancellationRequested(); + } + catch (OperationCanceledException) + { + var connectionString = dbContext.Database.GetConnectionString(); + + var hostName = parser.GetHostName(connectionString); + + if (string.IsNullOrWhiteSpace(hostName)) + { + hostName = "Unknown"; + } + + throw new InvalidOperationException($"Failed to connect to database <{hostName}>"); } await dbContext.Database.MigrateAsync(ct); diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/MySql/App/Migrations/20250517203912_MakeAssetPropertiesNullable.Designer.cs b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/App/Migrations/20250517203912_MakeAssetPropertiesNullable.Designer.cs new file mode 100644 index 000000000..f69c3cf73 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/App/Migrations/20250517203912_MakeAssetPropertiesNullable.Designer.cs @@ -0,0 +1,1584 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; +using Squidex.Providers.MySql.App; + +#nullable disable + +namespace Squidex.Providers.MySql.App.Migrations +{ + [DbContext(typeof(MySqlAppDbContext))] + [Migration("20250517203912_MakeAssetPropertiesNullable")] + partial class MakeAssetPropertiesNullable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.14") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("longtext"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("RoleId") + .HasColumnType("varchar(255)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("varchar(255)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("varchar(255)"); + + b.Property("ApplicationId") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("Properties") + .HasColumnType("longtext"); + + b.Property("Scopes") + .HasColumnType("longtext"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("varchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("varchar(255)"); + + b.Property("ApplicationId") + .HasColumnType("varchar(255)"); + + b.Property("AuthorizationId") + .HasColumnType("varchar(255)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("CreationDate") + .HasColumnType("datetime(6)"); + + b.Property("ExpirationDate") + .HasColumnType("datetime(6)"); + + b.Property("Payload") + .HasColumnType("longtext"); + + b.Property("Properties") + .HasColumnType("longtext"); + + b.Property("RedemptionDate") + .HasColumnType("datetime(6)"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("varchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("varchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", (string)null); + }); + + modelBuilder.Entity("Squidex.AI.Mongo.EFChatEntity", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("LastUpdated") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("LastUpdated"); + + b.ToTable("Chats", (string)null); + }); + + modelBuilder.Entity("Squidex.Assets.EntityFramework.EFAssetKeyValueEntity", b => + { + b.Property("Key") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Expires") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Key"); + + b.HasIndex("Expires"); + + b.ToTable("AssetKeyValueStore_TusMetadata", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Apps.EFAppEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("IndexedCreated") + .HasColumnType("datetime(6)") + .HasColumnName("Created"); + + b.Property("IndexedDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("Deleted"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("longtext") + .HasColumnName("Name"); + + b.Property("IndexedTeamId") + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("TeamId"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("longtext") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_App", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("FileHash") + .HasColumnType("longtext"); + + b.Property("FileName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("FileVersion") + .HasColumnType("bigint"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); + + b.Property("IsProtected") + .HasColumnType("tinyint(1)"); + + b.Property("LastModified") + .HasColumnType("datetime(6)"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Metadata") + .IsRequired() + .HasColumnType("json"); + + b.Property("MimeType") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Slug") + .HasColumnType("longtext"); + + b.Property("Tags") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("varchar(1000)"); + + b.Property("TotalSize") + .HasColumnType("bigint"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("Assets"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetFolderEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("FolderName") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); + + b.Property("LastModified") + .HasColumnType("datetime(6)"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("AssetFolders"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentCompleteEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("json"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); + + b.Property("LastModified") + .HasColumnType("datetime(6)"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NewData") + .HasColumnType("json"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ScheduleJob") + .HasColumnType("json"); + + b.Property("ScheduledAt") + .HasColumnType("datetime(6)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("TranslationStatus") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentPublishedEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("json"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)"); + + b.Property("LastModified") + .HasColumnType("datetime(6)"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("NewData") + .HasColumnType("json"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("ScheduleJob") + .HasColumnType("json"); + + b.Property("ScheduledAt") + .HasColumnType("datetime(6)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("TranslationStatus") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentTableEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("AppId", "SchemaId") + .IsUnique(); + + b.ToTable("ContentTables", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferenceCompleteEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("FromSchema") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasDefaultValue("00000000-0000-0000-0000-000000000000"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferencePublishedEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("FromSchema") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasDefaultValue("00000000-0000-0000-0000-000000000000"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.EFTextIndexGeoEntity", b => + { + b.Property("Id") + .HasMaxLength(400) + .HasColumnType("varchar(400)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ContentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("GeoField") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("GeoObject") + .IsRequired() + .HasColumnType("geometry"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ServeAll") + .HasColumnType("tinyint(1)"); + + b.Property("ServePublished") + .HasColumnType("tinyint(1)"); + + b.Property("Stage") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.ToTable("Geos", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.EFTextIndexTextEntity", b => + { + b.Property("Id") + .HasMaxLength(400) + .HasColumnType("varchar(400)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ContentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ServeAll") + .HasColumnType("tinyint(1)"); + + b.Property("ServePublished") + .HasColumnType("tinyint(1)"); + + b.Property("Stage") + .HasColumnType("tinyint unsigned"); + + b.Property("Texts") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("Texts", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.State.TextContentState", b => + { + b.Property("UniqueContentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("State") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.HasKey("UniqueContentId"); + + b.ToTable("TextState", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.History.HistoryEvent", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)"); + + b.Property("Channel") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("OwnerId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Parameters") + .IsRequired() + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("HistoryEvent"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Rules.EFRuleEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("Id"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Rule", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Schemas.EFSchemaEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("Id"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("longtext") + .HasColumnName("Name"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Schema", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Teams.EFTeamEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("IndexedAuthDomain") + .HasColumnType("longtext") + .HasColumnName("AuthDomain"); + + b.Property("IndexedDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("Deleted"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("longtext") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Team", (string)null); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFEventCommit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("EventStream") + .IsRequired() + .HasMaxLength(750) + .HasColumnType("varchar(750)"); + + b.Property("EventStreamOffset") + .HasColumnType("bigint"); + + b.Property("Events") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("EventsCount") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("EventStream", "EventStreamOffset") + .IsUnique(); + + b.HasIndex("EventStream", "Position"); + + b.HasIndex("EventStream", "Timestamp"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFPosition", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("EventPosition"); + }); + + modelBuilder.Entity("Squidex.Flows.EntityFramework.EFCronJobEntity", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("DueTime") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("DueTime"); + + b.ToTable("CronJobs", (string)null); + }); + + modelBuilder.Entity("Squidex.Flows.EntityFramework.EFFlowStateEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Created") + .HasColumnType("datetime(6)"); + + b.Property("DefinitionId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("DueTime") + .HasColumnType("datetime(6)"); + + b.Property("OwnerId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("SchedulePartition") + .HasColumnType("int"); + + b.Property("State") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("DueTime", "SchedulePartition"); + + b.ToTable("Flows", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Caching.EFCacheEntity", b => + { + b.Property("Key") + .HasColumnType("varchar(255)"); + + b.Property("Expires") + .HasColumnType("datetime(6)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("longblob"); + + b.HasKey("Key"); + + b.ToTable("Cache", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Log.EFRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Key") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("Properties") + .IsRequired() + .HasColumnType("json"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("Key"); + + b.ToTable("Requests", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Migrations.EFMigrationEntity", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("IsLocked") + .HasColumnType("tinyint(1)"); + + b.Property("Version") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Migrations", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UISettings", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_TagHistory", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageNotifications", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Counters", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_JobsState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageTracker", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_Tags", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Keys", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Xml", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_EventConsumerState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Document") + .HasColumnType("json"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Names", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.UsageTracking.EFUsageCounterEntity", b => + { + b.Property("Key") + .HasColumnType("varchar(255)"); + + b.Property("Date") + .HasColumnType("datetime(6)"); + + b.Property("Category") + .HasColumnType("varchar(255)"); + + b.Property("CounterKey") + .HasColumnType("varchar(255)"); + + b.Property("CounterValue") + .HasColumnType("double"); + + b.HasKey("Key", "Date", "Category", "CounterKey"); + + b.ToTable("Counter", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessage", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("ChannelName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("MessageData") + .IsRequired() + .HasColumnType("longblob"); + + b.Property("MessageHeaders") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("QueueName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("TimeHandled") + .HasColumnType("datetime(6)"); + + b.Property("TimeToLive") + .HasColumnType("datetime(6)"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("ChannelName", "TimeHandled"); + + b.ToTable("Messages", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessagingDataEntity", b => + { + b.Property("Group") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Key") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("Expiration") + .HasColumnType("datetime(6)"); + + b.Property("ValueData") + .IsRequired() + .HasColumnType("longblob"); + + b.Property("ValueFormat") + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("ValueType") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.HasKey("Group", "Key"); + + b.HasIndex("Expiration"); + + b.ToTable("MessagingData", (string)null); + }); + + modelBuilder.Entity("YDotNet.Server.EntityFramework.YDotNetDocument", b => + { + b.Property("Id") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("longblob"); + + b.Property("Expiration") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.ToTable("YDotNetDocument", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/MySql/App/Migrations/20250517203912_MakeAssetPropertiesNullable.cs b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/App/Migrations/20250517203912_MakeAssetPropertiesNullable.cs new file mode 100644 index 000000000..880d2f148 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/App/Migrations/20250517203912_MakeAssetPropertiesNullable.cs @@ -0,0 +1,74 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Squidex.Providers.MySql.App.Migrations +{ + /// + public partial class MakeAssetPropertiesNullable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Slug", + table: "Assets", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext") + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AlterColumn( + name: "FileHash", + table: "Assets", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "longtext") + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.UpdateData( + table: "Assets", + keyColumn: "Slug", + keyValue: null, + column: "Slug", + value: ""); + + migrationBuilder.AlterColumn( + name: "Slug", + table: "Assets", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.UpdateData( + table: "Assets", + keyColumn: "FileHash", + keyValue: null, + column: "FileHash", + value: ""); + + migrationBuilder.AlterColumn( + name: "FileHash", + table: "Assets", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/MySql/App/Migrations/MySqlDbContextModelSnapshot.cs b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/App/Migrations/MySqlDbContextModelSnapshot.cs index 1e061c61d..e6dd9f393 100644 --- a/backend/src/Squidex.Data.EntityFramework/Providers/MySql/App/Migrations/MySqlDbContextModelSnapshot.cs +++ b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/App/Migrations/MySqlDbContextModelSnapshot.cs @@ -426,7 +426,6 @@ namespace Squidex.Providers.MySql.Migrations .HasColumnType("varchar(100)"); b.Property("FileHash") - .IsRequired() .HasColumnType("longtext"); b.Property("FileName") @@ -477,7 +476,6 @@ namespace Squidex.Providers.MySql.Migrations .HasColumnType("varchar(255)"); b.Property("Slug") - .IsRequired() .HasColumnType("longtext"); b.Property("Tags") diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/MySql/MySqlConnectionStringParser.cs b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/MySqlConnectionStringParser.cs new file mode 100644 index 000000000..67b4e016f --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/MySqlConnectionStringParser.cs @@ -0,0 +1,21 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using MySqlConnector; +using Squidex.Infrastructure.Migrations; + +namespace Squidex.Providers.MySql; + +public sealed class MySqlConnectionStringParser : ConnectionStringParser +{ + protected override string? GetProviderSpecificHostName(string source) + { + var builder = new MySqlConnectionStringBuilder(source); + + return builder.Server; + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/MySql/MySqlDiagnostics.cs b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/MySqlDiagnostics.cs new file mode 100644 index 000000000..3f68cdf83 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/MySql/MySqlDiagnostics.cs @@ -0,0 +1,19 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using OpenTelemetry.Trace; +using Squidex.Infrastructure; + +namespace Squidex.Providers.MySql; + +public sealed class MySqlDiagnostics : ITelemetryConfigurator +{ + public void Configure(TracerProviderBuilder builder) + { + builder.AddSource("MySqlConnector"); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/App/Migrations/20250517203918_MakeAssetPropertiesNullable.Designer.cs b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/App/Migrations/20250517203918_MakeAssetPropertiesNullable.Designer.cs new file mode 100644 index 000000000..da4d14d7f --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/App/Migrations/20250517203918_MakeAssetPropertiesNullable.Designer.cs @@ -0,0 +1,1585 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Squidex.Providers.Postgres.App; + +#nullable disable + +namespace Squidex.Providers.Postgres.App.Migrations +{ + [DbContext(typeof(PostgresAppDbContext))] + [Migration("20250517203918_MakeAssetPropertiesNullable")] + partial class MakeAssetPropertiesNullable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.14") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", (string)null); + }); + + modelBuilder.Entity("Squidex.AI.Mongo.EFChatEntity", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("LastUpdated") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("LastUpdated"); + + b.ToTable("Chats", (string)null); + }); + + modelBuilder.Entity("Squidex.Assets.EntityFramework.EFAssetKeyValueEntity", b => + { + b.Property("Key") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Expires") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Key"); + + b.HasIndex("Expires"); + + b.ToTable("AssetKeyValueStore_TusMetadata", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Apps.EFAppEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("IndexedCreated") + .HasColumnType("timestamp with time zone") + .HasColumnName("Created"); + + b.Property("IndexedDeleted") + .HasColumnType("boolean") + .HasColumnName("Deleted"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Name"); + + b.Property("IndexedTeamId") + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("TeamId"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("text") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_App", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("FileHash") + .HasColumnType("text"); + + b.Property("FileName") + .IsRequired() + .HasColumnType("text"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("FileVersion") + .HasColumnType("bigint"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsProtected") + .HasColumnType("boolean"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Metadata") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("MimeType") + .IsRequired() + .HasColumnType("text"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Slug") + .HasColumnType("text"); + + b.Property("Tags") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("TotalSize") + .HasColumnType("bigint"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("Assets"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetFolderEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("FolderName") + .IsRequired() + .HasColumnType("text"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("AssetFolders"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentCompleteEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("NewData") + .HasColumnType("jsonb"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ScheduleJob") + .HasColumnType("jsonb"); + + b.Property("ScheduledAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TranslationStatus") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentPublishedEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("NewData") + .HasColumnType("jsonb"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ScheduleJob") + .HasColumnType("jsonb"); + + b.Property("ScheduledAt") + .HasColumnType("timestamp with time zone"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("TranslationStatus") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentTableEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Id"); + + b.HasIndex("AppId", "SchemaId") + .IsUnique(); + + b.ToTable("ContentTables", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferenceCompleteEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("FromSchema") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasDefaultValue("00000000-0000-0000-0000-000000000000"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferencePublishedEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("FromSchema") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasDefaultValue("00000000-0000-0000-0000-000000000000"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.EFTextIndexGeoEntity", b => + { + b.Property("Id") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ContentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("GeoField") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("GeoObject") + .IsRequired() + .HasColumnType("geometry"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ServeAll") + .HasColumnType("boolean"); + + b.Property("ServePublished") + .HasColumnType("boolean"); + + b.Property("Stage") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.ToTable("Geos", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.EFTextIndexTextEntity", b => + { + b.Property("Id") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ContentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ServeAll") + .HasColumnType("boolean"); + + b.Property("ServePublished") + .HasColumnType("boolean"); + + b.Property("Stage") + .HasColumnType("smallint"); + + b.Property("Texts") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Texts", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.State.TextContentState", b => + { + b.Property("UniqueContentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("State") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.HasKey("UniqueContentId"); + + b.ToTable("TextState", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.History.HistoryEvent", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Channel") + .IsRequired() + .HasColumnType("text"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("text"); + + b.Property("OwnerId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Parameters") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("HistoryEvent"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Rules.EFRuleEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("boolean") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Id"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Rule", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Schemas.EFSchemaEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("boolean") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("Id"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Name"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Schema", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Teams.EFTeamEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("IndexedAuthDomain") + .HasColumnType("text") + .HasColumnName("AuthDomain"); + + b.Property("IndexedDeleted") + .HasColumnType("boolean") + .HasColumnName("Deleted"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("text") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Team", (string)null); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFEventCommit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("EventStream") + .IsRequired() + .HasMaxLength(750) + .HasColumnType("character varying(750)"); + + b.Property("EventStreamOffset") + .HasColumnType("bigint"); + + b.Property("Events") + .IsRequired() + .HasColumnType("text[]"); + + b.Property("EventsCount") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("EventStream", "EventStreamOffset") + .IsUnique(); + + b.HasIndex("EventStream", "Position"); + + b.HasIndex("EventStream", "Timestamp"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFPosition", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("EventPosition"); + }); + + modelBuilder.Entity("Squidex.Flows.EntityFramework.EFCronJobEntity", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("text"); + + b.Property("DueTime") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("DueTime"); + + b.ToTable("CronJobs", (string)null); + }); + + modelBuilder.Entity("Squidex.Flows.EntityFramework.EFFlowStateEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("DefinitionId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("DueTime") + .HasColumnType("timestamp with time zone"); + + b.Property("OwnerId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("SchedulePartition") + .HasColumnType("integer"); + + b.Property("State") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DueTime", "SchedulePartition"); + + b.ToTable("Flows", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Caching.EFCacheEntity", b => + { + b.Property("Key") + .HasColumnType("text"); + + b.Property("Expires") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .IsRequired() + .HasColumnType("bytea"); + + b.HasKey("Key"); + + b.ToTable("Cache", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Log.EFRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Key") + .IsRequired() + .HasColumnType("text"); + + b.Property("Properties") + .IsRequired() + .HasColumnType("jsonb"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Key"); + + b.ToTable("Requests", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Migrations.EFMigrationEntity", b => + { + b.Property("Id") + .HasColumnType("integer"); + + b.Property("IsLocked") + .HasColumnType("boolean"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("Migrations", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UISettings", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_TagHistory", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageNotifications", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Counters", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_JobsState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageTracker", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_Tags", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Keys", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Xml", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_EventConsumerState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Document") + .HasColumnType("jsonb"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Names", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.UsageTracking.EFUsageCounterEntity", b => + { + b.Property("Key") + .HasColumnType("text"); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("Category") + .HasColumnType("text"); + + b.Property("CounterKey") + .HasColumnType("text"); + + b.Property("CounterValue") + .HasColumnType("double precision"); + + b.HasKey("Key", "Date", "Category", "CounterKey"); + + b.ToTable("Counter", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessage", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ChannelName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("MessageData") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("MessageHeaders") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("QueueName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("TimeHandled") + .HasColumnType("timestamp with time zone"); + + b.Property("TimeToLive") + .HasColumnType("timestamp with time zone"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ChannelName", "TimeHandled"); + + b.ToTable("Messages", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessagingDataEntity", b => + { + b.Property("Group") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Key") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.Property("ValueData") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("ValueFormat") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("ValueType") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.HasKey("Group", "Key"); + + b.HasIndex("Expiration"); + + b.ToTable("MessagingData", (string)null); + }); + + modelBuilder.Entity("YDotNet.Server.EntityFramework.YDotNetDocument", b => + { + b.Property("Id") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("YDotNetDocument", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/App/Migrations/20250517203918_MakeAssetPropertiesNullable.cs b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/App/Migrations/20250517203918_MakeAssetPropertiesNullable.cs new file mode 100644 index 000000000..26477d542 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/App/Migrations/20250517203918_MakeAssetPropertiesNullable.cs @@ -0,0 +1,54 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Squidex.Providers.Postgres.App.Migrations +{ + /// + public partial class MakeAssetPropertiesNullable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Slug", + table: "Assets", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "text"); + + migrationBuilder.AlterColumn( + name: "FileHash", + table: "Assets", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "text"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Slug", + table: "Assets", + type: "text", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "FileHash", + table: "Assets", + type: "text", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/App/Migrations/PostgresDbContextModelSnapshot.cs b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/App/Migrations/PostgresDbContextModelSnapshot.cs index 6d3c6b162..ee2599db2 100644 --- a/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/App/Migrations/PostgresDbContextModelSnapshot.cs +++ b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/App/Migrations/PostgresDbContextModelSnapshot.cs @@ -427,7 +427,6 @@ namespace Squidex.Providers.Postgres.Migrations .HasColumnType("character varying(100)"); b.Property("FileHash") - .IsRequired() .HasColumnType("text"); b.Property("FileName") @@ -478,7 +477,6 @@ namespace Squidex.Providers.Postgres.Migrations .HasColumnType("character varying(255)"); b.Property("Slug") - .IsRequired() .HasColumnType("text"); b.Property("Tags") diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/PostgresConnectionStringParser.cs b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/PostgresConnectionStringParser.cs new file mode 100644 index 000000000..48399178f --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/PostgresConnectionStringParser.cs @@ -0,0 +1,21 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Npgsql; +using Squidex.Infrastructure.Migrations; + +namespace Squidex.Providers.Postgres; + +public class PostgresConnectionStringParser : ConnectionStringParser +{ + protected override string? GetProviderSpecificHostName(string source) + { + var builder = new NpgsqlConnectionStringBuilder(source); + + return builder.Host; + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/PostgresDiagnostics.cs b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/PostgresDiagnostics.cs new file mode 100644 index 000000000..4f6baef6b --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/Postgres/PostgresDiagnostics.cs @@ -0,0 +1,20 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Npgsql; +using OpenTelemetry.Trace; +using Squidex.Infrastructure; + +namespace Squidex.Providers.Postgres; + +public sealed class PostgresDiagnostics : ITelemetryConfigurator +{ + public void Configure(TracerProviderBuilder builder) + { + builder.AddNpgsql(); + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/App/Migrations/20250517203925_MakeAssetPropertiesNullable.Designer.cs b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/App/Migrations/20250517203925_MakeAssetPropertiesNullable.Designer.cs new file mode 100644 index 000000000..8cf5d8ed9 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/App/Migrations/20250517203925_MakeAssetPropertiesNullable.Designer.cs @@ -0,0 +1,1587 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; +using Squidex.Providers.SqlServer.App; + +#nullable disable + +namespace Squidex.Providers.SqlServer.App.Migrations +{ + [DbContext(typeof(SqlServerAppDbContext))] + [Migration("20250517203925_MakeAssetPropertiesNullable")] + partial class MakeAssetPropertiesNullable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.14") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("nvarchar(450)"); + + b.Property("ApplicationId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("Scopes") + .HasColumnType("nvarchar(max)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", (string)null); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("nvarchar(450)"); + + b.Property("ApplicationId") + .HasColumnType("nvarchar(450)"); + + b.Property("AuthorizationId") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("ExpirationDate") + .HasColumnType("datetime2"); + + b.Property("Payload") + .HasColumnType("nvarchar(max)"); + + b.Property("Properties") + .HasColumnType("nvarchar(max)"); + + b.Property("RedemptionDate") + .HasColumnType("datetime2"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique() + .HasFilter("[ReferenceId] IS NOT NULL"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", (string)null); + }); + + modelBuilder.Entity("Squidex.AI.Mongo.EFChatEntity", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("LastUpdated") + .HasColumnType("datetime2"); + + b.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("LastUpdated"); + + b.ToTable("Chats", (string)null); + }); + + modelBuilder.Entity("Squidex.Assets.EntityFramework.EFAssetKeyValueEntity", b => + { + b.Property("Key") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Expires") + .HasColumnType("datetimeoffset"); + + b.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Key"); + + b.HasIndex("Expires"); + + b.ToTable("AssetKeyValueStore_TusMetadata", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Apps.EFAppEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("IndexedCreated") + .HasColumnType("datetimeoffset") + .HasColumnName("Created"); + + b.Property("IndexedDeleted") + .HasColumnType("bit") + .HasColumnName("Deleted"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("Name"); + + b.Property("IndexedTeamId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("TeamId"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_App", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("FileHash") + .HasColumnType("nvarchar(max)"); + + b.Property("FileName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("FileVersion") + .HasColumnType("bigint"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("IsProtected") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Metadata") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("MimeType") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Slug") + .HasColumnType("nvarchar(max)"); + + b.Property("Tags") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)"); + + b.Property("TotalSize") + .HasColumnType("bigint"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("Assets"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Assets.EFAssetFolderEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("FolderName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ParentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.HasIndex("IndexedAppId", "Id"); + + b.ToTable("AssetFolders"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentCompleteEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("NewData") + .HasColumnType("nvarchar(max)"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ScheduleJob") + .HasColumnType("nvarchar(max)"); + + b.Property("ScheduledAt") + .HasColumnType("datetimeoffset"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("TranslationStatus") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentPublishedEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Id") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IndexedSchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("IsDeleted") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("NewData") + .HasColumnType("nvarchar(max)"); + + b.Property("NewStatus") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("ScheduleJob") + .HasColumnType("nvarchar(max)"); + + b.Property("ScheduledAt") + .HasColumnType("datetimeoffset"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Status") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("TranslationStatus") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("ContentsPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFContentTableEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("AppId", "SchemaId") + .IsUnique(); + + b.ToTable("ContentTables", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferenceCompleteEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("FromSchema") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasDefaultValue("00000000-0000-0000-0000-000000000000"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesAll", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.EFReferencePublishedEntity", b => + { + b.Property("AppId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("FromKey") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ToId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("FromSchema") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasDefaultValue("00000000-0000-0000-0000-000000000000"); + + b.HasKey("AppId", "FromKey", "ToId"); + + b.ToTable("ContentReferencesPublished", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.EFTextIndexGeoEntity", b => + { + b.Property("Id") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ContentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("GeoField") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("GeoObject") + .IsRequired() + .HasColumnType("geography"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ServeAll") + .HasColumnType("bit"); + + b.Property("ServePublished") + .HasColumnType("bit"); + + b.Property("Stage") + .HasColumnType("tinyint"); + + b.HasKey("Id"); + + b.ToTable("Geos", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.EFTextIndexTextEntity", b => + { + b.Property("Id") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ContentId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("SchemaId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ServeAll") + .HasColumnType("bit"); + + b.Property("ServePublished") + .HasColumnType("bit"); + + b.Property("Stage") + .HasColumnType("tinyint"); + + b.Property("Texts") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Texts", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Contents.Text.State.TextContentState", b => + { + b.Property("UniqueContentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("State") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("UniqueContentId"); + + b.ToTable("TextState", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.History.HistoryEvent", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Actor") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Channel") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("EventType") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("OwnerId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Parameters") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("HistoryEvent"); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Rules.EFRuleEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("bit") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Id"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Rule", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Schemas.EFSchemaEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("IndexedAppId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("AppId"); + + b.Property("IndexedDeleted") + .HasColumnType("bit") + .HasColumnName("Deleted"); + + b.Property("IndexedId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("Id"); + + b.Property("IndexedName") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("Name"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Schema", (string)null); + }); + + modelBuilder.Entity("Squidex.Domain.Apps.Entities.Teams.EFTeamEntity", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("IndexedAuthDomain") + .HasColumnType("nvarchar(max)") + .HasColumnName("AuthDomain"); + + b.Property("IndexedDeleted") + .HasColumnType("bit") + .HasColumnName("Deleted"); + + b.Property("IndexedUserIds") + .IsRequired() + .HasColumnType("nvarchar(max)") + .HasColumnName("UserIds"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Team", (string)null); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFEventCommit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("EventStream") + .IsRequired() + .HasMaxLength(750) + .HasColumnType("nvarchar(750)"); + + b.Property("EventStreamOffset") + .HasColumnType("bigint"); + + b.Property("Events") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("EventsCount") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.Property("Timestamp") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("EventStream", "EventStreamOffset") + .IsUnique(); + + b.HasIndex("EventStream", "Position"); + + b.HasIndex("EventStream", "Timestamp"); + + b.ToTable("Events"); + }); + + modelBuilder.Entity("Squidex.Events.EntityFramework.EFPosition", b => + { + b.Property("Id") + .HasColumnType("bigint"); + + b.Property("Position") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.ToTable("EventPosition"); + }); + + modelBuilder.Entity("Squidex.Flows.EntityFramework.EFCronJobEntity", b => + { + b.Property("Id") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DueTime") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("DueTime"); + + b.ToTable("CronJobs", (string)null); + }); + + modelBuilder.Entity("Squidex.Flows.EntityFramework.EFFlowStateEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("DefinitionId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("DueTime") + .HasColumnType("datetimeoffset"); + + b.Property("OwnerId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("SchedulePartition") + .HasColumnType("int"); + + b.Property("State") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("DueTime", "SchedulePartition"); + + b.ToTable("Flows", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Caching.EFCacheEntity", b => + { + b.Property("Key") + .HasColumnType("nvarchar(450)"); + + b.Property("Expires") + .HasColumnType("datetime2"); + + b.Property("Value") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.HasKey("Key"); + + b.ToTable("Cache", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Log.EFRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Key") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("Properties") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("Key"); + + b.ToTable("Requests", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.Migrations.EFMigrationEntity", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("IsLocked") + .HasColumnType("bit"); + + b.Property("Version") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.ToTable("Migrations", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UISettings", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_TagHistory", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageNotifications", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Counters", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_JobsState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_UsageTracker", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Index_Tags", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Keys", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Identity_Xml", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_EventConsumerState", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.States.EFState", b => + { + b.Property("DocumentId") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Document") + .HasColumnType("nvarchar(max)"); + + b.Property("Version") + .HasColumnType("bigint"); + + b.HasKey("DocumentId"); + + b.ToTable("States_Names", (string)null); + }); + + modelBuilder.Entity("Squidex.Infrastructure.UsageTracking.EFUsageCounterEntity", b => + { + b.Property("Key") + .HasColumnType("nvarchar(450)"); + + b.Property("Date") + .HasColumnType("datetime2"); + + b.Property("Category") + .HasColumnType("nvarchar(450)"); + + b.Property("CounterKey") + .HasColumnType("nvarchar(450)"); + + b.Property("CounterValue") + .HasColumnType("float"); + + b.HasKey("Key", "Date", "Category", "CounterKey"); + + b.ToTable("Counter", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessage", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ChannelName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("MessageData") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("MessageHeaders") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("nvarchar(2000)"); + + b.Property("QueueName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("TimeHandled") + .HasColumnType("datetime2"); + + b.Property("TimeToLive") + .HasColumnType("datetime2"); + + b.Property("Version") + .IsConcurrencyToken() + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("ChannelName", "TimeHandled"); + + b.ToTable("Messages", (string)null); + }); + + modelBuilder.Entity("Squidex.Messaging.EntityFramework.EFMessagingDataEntity", b => + { + b.Property("Group") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Key") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("Expiration") + .HasColumnType("datetime2"); + + b.Property("ValueData") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("ValueFormat") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.Property("ValueType") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.HasKey("Group", "Key"); + + b.HasIndex("Expiration"); + + b.ToTable("MessagingData", (string)null); + }); + + modelBuilder.Entity("YDotNet.Server.EntityFramework.YDotNetDocument", b => + { + b.Property("Id") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Data") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("Expiration") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.ToTable("YDotNetDocument", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/App/Migrations/20250517203925_MakeAssetPropertiesNullable.cs b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/App/Migrations/20250517203925_MakeAssetPropertiesNullable.cs new file mode 100644 index 000000000..b3ef3b621 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/App/Migrations/20250517203925_MakeAssetPropertiesNullable.cs @@ -0,0 +1,54 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Squidex.Providers.SqlServer.App.Migrations +{ + /// + public partial class MakeAssetPropertiesNullable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Slug", + table: "Assets", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "FileHash", + table: "Assets", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Slug", + table: "Assets", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "FileHash", + table: "Assets", + type: "nvarchar(max)", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + } + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/App/Migrations/SqlServerDbContextModelSnapshot.cs b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/App/Migrations/SqlServerDbContextModelSnapshot.cs index 972a4b948..7fd48ad17 100644 --- a/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/App/Migrations/SqlServerDbContextModelSnapshot.cs +++ b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/App/Migrations/SqlServerDbContextModelSnapshot.cs @@ -429,7 +429,6 @@ namespace Squidex.Providers.SqlServer.Migrations .HasColumnType("nvarchar(100)"); b.Property("FileHash") - .IsRequired() .HasColumnType("nvarchar(max)"); b.Property("FileName") @@ -480,7 +479,6 @@ namespace Squidex.Providers.SqlServer.Migrations .HasColumnType("nvarchar(255)"); b.Property("Slug") - .IsRequired() .HasColumnType("nvarchar(max)"); b.Property("Tags") diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/SqlServerConnectionStringParser.cs b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/SqlServerConnectionStringParser.cs new file mode 100644 index 000000000..ce1618416 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/SqlServerConnectionStringParser.cs @@ -0,0 +1,21 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Microsoft.Data.SqlClient; +using Squidex.Infrastructure.Migrations; + +namespace Squidex.Providers.SqlServer; + +public sealed class SqlServerConnectionStringParser : ConnectionStringParser +{ + protected override string? GetProviderSpecificHostName(string source) + { + var builder = new SqlConnectionStringBuilder(source); + + return builder.DataSource; + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/SqlServerDiagnostics.cs b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/SqlServerDiagnostics.cs new file mode 100644 index 000000000..45c7d98a1 --- /dev/null +++ b/backend/src/Squidex.Data.EntityFramework/Providers/SqlServer/SqlServerDiagnostics.cs @@ -0,0 +1,18 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using OpenTelemetry.Trace; +using Squidex.Infrastructure; + +namespace Squidex.Providers.SqlServer; + +public sealed class SqlServerDiagnostics : ITelemetryConfigurator +{ + public void Configure(TracerProviderBuilder builder) + { + } +} diff --git a/backend/src/Squidex.Data.EntityFramework/ServiceExtensions.cs b/backend/src/Squidex.Data.EntityFramework/ServiceExtensions.cs index 6ed6c7ecc..fd0a300e5 100644 --- a/backend/src/Squidex.Data.EntityFramework/ServiceExtensions.cs +++ b/backend/src/Squidex.Data.EntityFramework/ServiceExtensions.cs @@ -43,10 +43,13 @@ using Squidex.Infrastructure.Migrations; using Squidex.Infrastructure.States; using Squidex.Infrastructure.UsageTracking; using Squidex.Messaging; +using Squidex.Providers.MySql; using Squidex.Providers.MySql.App; using Squidex.Providers.MySql.Content; +using Squidex.Providers.Postgres; using Squidex.Providers.Postgres.App; using Squidex.Providers.Postgres.Content; +using Squidex.Providers.SqlServer; using Squidex.Providers.SqlServer.App; using Squidex.Providers.SqlServer.Content; using YDotNet.Server.EntityFramework; @@ -87,6 +90,12 @@ public static class ServiceExtensions services.AddSingleton(typeof(ISnapshotStore<>), typeof(MySqlSnapshotStore<>)); services.AddSquidexEntityFramework(config); + + services.AddSingletonAs() + .As(); + + services.AddSingletonAs() + .As(); }, ["Postgres"] = () => { @@ -106,6 +115,12 @@ public static class ServiceExtensions services.AddSingleton(typeof(ISnapshotStore<>), typeof(PostgresSnapshotStore<>)); services.AddSquidexEntityFramework(config); + + services.AddSingletonAs() + .As(); + + services.AddSingletonAs() + .As(); }, ["SqlServer"] = () => { @@ -125,6 +140,12 @@ public static class ServiceExtensions services.AddSingleton(typeof(ISnapshotStore<>), typeof(SqlServerSnapshotStore<>)); services.AddSquidexEntityFramework(config); + + services.AddSingletonAs() + .As(); + + services.AddSingletonAs() + .As(); }, }); } diff --git a/backend/src/Squidex.Data.EntityFramework/Squidex.Data.EntityFramework.csproj b/backend/src/Squidex.Data.EntityFramework/Squidex.Data.EntityFramework.csproj index dd7cfec2d..0a1665702 100644 --- a/backend/src/Squidex.Data.EntityFramework/Squidex.Data.EntityFramework.csproj +++ b/backend/src/Squidex.Data.EntityFramework/Squidex.Data.EntityFramework.csproj @@ -32,21 +32,22 @@ + - - - + + + - - - - + + + + diff --git a/backend/src/Squidex.Data.MongoDb/Squidex.Data.MongoDb.csproj b/backend/src/Squidex.Data.MongoDb/Squidex.Data.MongoDb.csproj index 10ee3e6b9..9557b8c59 100644 --- a/backend/src/Squidex.Data.MongoDb/Squidex.Data.MongoDb.csproj +++ b/backend/src/Squidex.Data.MongoDb/Squidex.Data.MongoDb.csproj @@ -25,12 +25,12 @@ - - - - - - + + + + + + diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Assets/Asset.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Assets/Asset.cs index 772833fb1..a6d8c4185 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Assets/Asset.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Assets/Asset.cs @@ -14,11 +14,11 @@ public record Asset : AssetItem { public string FileName { get; set; } - public string FileHash { get; set; } + public string? FileHash { get; set; } public string MimeType { get; set; } - public string Slug { get; set; } + public string? Slug { get; set; } public long FileSize { get; set; } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Squidex.Domain.Apps.Core.Model.csproj b/backend/src/Squidex.Domain.Apps.Core.Model/Squidex.Domain.Apps.Core.Model.csproj index 0d380107e..76be3ff4c 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Squidex.Domain.Apps.Core.Model.csproj +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Squidex.Domain.Apps.Core.Model.csproj @@ -20,7 +20,7 @@ - + diff --git a/backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj b/backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj index baf6f0447..228de9d00 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj +++ b/backend/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj @@ -29,8 +29,8 @@ - - + + diff --git a/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreJob.cs b/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreJob.cs index 073ede539..3b37c1a8e 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreJob.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Backup/RestoreJob.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System.Diagnostics; using System.Runtime.CompilerServices; using Microsoft.Extensions.Logging; using Squidex.Domain.Apps.Core.Apps; @@ -106,7 +107,6 @@ public sealed class RestoreJob( await context.LogAsync(" * Restore all objects like app, schemas and contents"); await context.LogAsync(" * Complete the restore operation for all objects"); await context.FlushAsync(); - log.LogInformation("Backup with job id {backupId} with from URL '{url}' started.", context.Job.Id, state.Url); state.Reader = await DownloadAsync(context, state, ct); @@ -145,7 +145,6 @@ public sealed class RestoreJob( // Add the current user to the app, so that the admin can see it and verify integrity. await AssignContributorAsync(context, state); - await context.LogAsync("Completed, Yeah!"); log.LogInformation("Backup with job id {backupId} from URL '{url}' completed.", context.Job.Id, state.Url); @@ -239,9 +238,7 @@ public sealed class RestoreJob( using (Telemetry.Activities.StartActivity("Download")) { await run.LogAsync("Downloading Backup"); - var reader = await backupArchiveLocation.OpenReaderAsync(state.Url, run.Job.Id, ct); - await run.LogAsync("Downloaded Backup"); return reader; @@ -264,15 +261,27 @@ public sealed class RestoreJob( }, async (batch, ct) => { - var commits = - batch.Select(item => - EventCommitBuilder.Create( - item.Stream, - item.Offset, - item.Event, - eventFormatter)); + using (var activity = Telemetry.Activities.StartActivity("StoreEvents")) + { + var commits = + batch.Select(item => + EventCommitBuilder.Create( + item.Stream, + item.Offset, + item.Event, + eventFormatter)) + .ToList(); + + activity?.SetTag("totalCommits", commits.Count); + activity?.SetTag("totalEvents", commits.Sum(x => x.Events.Count)); + + if (commits.Any(x => x.StreamName.Contains("46b2fb05-3438-4b99-8c1d-bac8925a33dd"))) + { + Debugger.Break(); + } - await eventStore.AppendUnsafeAsync(commits, ct); + await eventStore.AppendUnsafeAsync(commits, ct); + } // Just in case we use parallel inserts later. Interlocked.Add(ref handled, batch.Count); @@ -359,9 +368,7 @@ public sealed class RestoreJob( using (Telemetry.Activities.StartActivity("CreateUsers")) { await run.LogAsync("Creating Users"); - await userMapping.RestoreAsync(state.Reader, userResolver, ct); - await run.LogAsync("Created Users"); } diff --git a/backend/src/Squidex.Domain.Apps.Entities/Jobs/JobProcessor.cs b/backend/src/Squidex.Domain.Apps.Entities/Jobs/JobProcessor.cs index 2a183ba81..601371d3b 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Jobs/JobProcessor.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Jobs/JobProcessor.cs @@ -205,6 +205,7 @@ public sealed class JobProcessor { try { + using var activity = Telemetry.Activities.StartActivity($"Job {runner.Name}"); await SetStatusAsync(context, JobStatus.Started); using (localCache.StartContext()) diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/DomainObject/Guards/GuardRule.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/DomainObject/Guards/GuardRule.cs index ffa671f90..2aede0595 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/DomainObject/Guards/GuardRule.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/DomainObject/Guards/GuardRule.cs @@ -5,7 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Entities.Rules.Commands; using Squidex.Infrastructure; using Squidex.Infrastructure.Validation; diff --git a/backend/src/Squidex.Domain.Apps.Entities/Rules/IRuleEnqueuer.cs b/backend/src/Squidex.Domain.Apps.Entities/Rules/IRuleEnqueuer.cs index 3865fe2d7..6b2591f09 100644 --- a/backend/src/Squidex.Domain.Apps.Entities/Rules/IRuleEnqueuer.cs +++ b/backend/src/Squidex.Domain.Apps.Entities/Rules/IRuleEnqueuer.cs @@ -6,7 +6,6 @@ // ========================================================================== using Squidex.Domain.Apps.Core.Rules; -using Squidex.Infrastructure; using Squidex.Infrastructure.EventSourcing; namespace Squidex.Domain.Apps.Entities.Rules; diff --git a/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj b/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj index 836f289f8..508c50768 100644 --- a/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj +++ b/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj @@ -24,13 +24,13 @@ - - - - - - - + + + + + + + diff --git a/backend/src/Squidex.Infrastructure/States/Persistence.cs b/backend/src/Squidex.Infrastructure/States/Persistence.cs index e562b9e53..ccf58ccee 100644 --- a/backend/src/Squidex.Infrastructure/States/Persistence.cs +++ b/backend/src/Squidex.Infrastructure/States/Persistence.cs @@ -8,8 +8,6 @@ using Squidex.Events; using Squidex.Infrastructure.EventSourcing; -#pragma warning disable RECS0012 // 'if' statement can be re-written as 'switch' statement - namespace Squidex.Infrastructure.States; internal sealed class Persistence( @@ -54,8 +52,10 @@ internal sealed class Persistence( { if (UseSnapshots) { - using (Telemetry.Activities.StartActivity("Persistence/ReadState")) + using (var activity = Telemetry.Activities.StartActivity("Persistence/ReadState")) { + activity?.SetTag("ownerType", ownerType.Name); + activity?.SetTag("ownerKey", ownerKey); await snapshotStore.RemoveAsync(ownerKey, ct); } @@ -64,8 +64,10 @@ internal sealed class Persistence( if (UseEventSourcing) { - using (Telemetry.Activities.StartActivity("Persistence/ReadEvents")) + using (var activity = Telemetry.Activities.StartActivity("Persistence/ReadEvents")) { + activity?.SetTag("ownerType", ownerType.Name); + activity?.SetTag("ownerKey", ownerKey); await eventStore.DeleteAsync(StreamFilter.Name(streamName.Value), ct); } @@ -172,8 +174,11 @@ internal sealed class Persistence( return; } - using (Telemetry.Activities.StartActivity("Persistence/WriteState")) + using (var activity = Telemetry.Activities.StartActivity("Persistence/WriteState")) { + activity?.SetTag("ownerType", ownerType.Name); + activity?.SetTag("ownerKey", ownerKey); + var job = new SnapshotWriteJob(ownerKey, state, newVersion) { OldVersion = oldVersion, @@ -204,8 +209,11 @@ internal sealed class Persistence( try { - using (Telemetry.Activities.StartActivity("Persistence/WriteEvents")) + using (var activity = Telemetry.Activities.StartActivity("Persistence/WriteEvents")) { + activity?.SetTag("ownerType", ownerType.Name); + activity?.SetTag("ownerKey", ownerKey); + await eventStore.AppendAsync(eventCommitId, streamName.Value, oldVersion, eventData, ct); } } diff --git a/backend/src/Squidex/Areas/Api/Config/OpenApi/DiscriminatorProcessor.cs b/backend/src/Squidex/Areas/Api/Config/OpenApi/DiscriminatorProcessor.cs index d098d8c7d..f12564d08 100644 --- a/backend/src/Squidex/Areas/Api/Config/OpenApi/DiscriminatorProcessor.cs +++ b/backend/src/Squidex/Areas/Api/Config/OpenApi/DiscriminatorProcessor.cs @@ -8,7 +8,6 @@ using GraphQL.Utilities; using NJsonSchema; using NJsonSchema.Generation; -using Squidex.Domain.Apps.Core.Rules.EnrichedEvents; using Squidex.Infrastructure.Reflection; namespace Squidex.Areas.Api.Config.OpenApi; diff --git a/backend/src/Squidex/Config/Startup/LogConfigurationHost.cs b/backend/src/Squidex/Config/Startup/LogConfigurationHost.cs index c7d81ecdd..fc224a4ad 100644 --- a/backend/src/Squidex/Config/Startup/LogConfigurationHost.cs +++ b/backend/src/Squidex/Config/Startup/LogConfigurationHost.cs @@ -12,9 +12,11 @@ namespace Squidex.Config.Startup; public sealed class LogConfigurationHost(IConfiguration configuration, ISemanticLog log) : IHostedService { + private static readonly string RedactedValue = "*****"; private static readonly Regex[] SensitivePatterns = [ // Authentication and API keys +#pragma warning disable MA0110 // Use the Regex source generator new Regex(@"(?i)(secret|token|key|password|credential|auth|api[_-]?key)$"), new Regex(@"(?i)^(aws|azure|google|microsoft|github)[_-]"), new Regex(@"(?i)(jwt|bearer|oauth|saml)"), @@ -31,10 +33,9 @@ public sealed class LogConfigurationHost(IConfiguration configuration, ISemantic // Database specific new Regex(@"(?i)(mongodb|sqlserver|postgres|mysql)://.*"), new Regex(@"(?i)(database|db|server|host|port|user|pass)="), +#pragma warning restore MA0110 // Use the Regex source generator ]; - private static readonly string RedactedValue = "*****"; - public Task StartAsync( CancellationToken cancellationToken) { @@ -44,16 +45,22 @@ public sealed class LogConfigurationHost(IConfiguration configuration, ISemantic { var logged = new HashSet(StringComparer.OrdinalIgnoreCase); - var orderedConfigs = configuration.AsEnumerable() - .Where(kvp => kvp.Value != null) - .OrderBy(x => x.Key, StringComparer.OrdinalIgnoreCase); + var orderedConfigs = + configuration.AsEnumerable() + .OrderBy(x => x.Key, StringComparer.OrdinalIgnoreCase); - foreach (var (key, val) in orderedConfigs) + foreach (var (key, value) in orderedConfigs) { - if (logged.Add(key)) + if (string.IsNullOrWhiteSpace(value)) { - var keyLower = key.ToLowerInvariant(); - var value = IsSensitiveKey(keyLower) || IsSensitiveValue(val) ? RedactedValue : val; + continue; + } + + var keyLower = key.ToLowerInvariant(); + + if (logged.Add(keyLower)) + { + var formattedValue = IsSensitiveKey(keyLower) || IsSensitiveValue(value) ? RedactedValue : value; c.WriteProperty(keyLower, value); } @@ -70,17 +77,16 @@ public sealed class LogConfigurationHost(IConfiguration configuration, ISemantic private static bool IsSensitiveValue(string? value) { - if (string.IsNullOrEmpty(value)) + // Check for connection strings and URLs with credentials + if (string.IsNullOrEmpty(value) || !value.Contains("://", StringComparison.Ordinal)) { return false; } - // Check for connection strings and URLs with credentials - return value.Contains("://") && ( - value.Contains("@") || // Contains username/password in URL - value.Contains(";") || // Contains connection string parameters - value.Contains("=") // Contains key-value pairs - ); + // Contains username/password, connection string parameters or query strings. + return value.Contains('@', StringComparison.Ordinal) + || value.Contains(';', StringComparison.Ordinal) + || value.Contains('=', StringComparison.Ordinal); } public Task StopAsync( diff --git a/backend/src/Squidex/Squidex.csproj b/backend/src/Squidex/Squidex.csproj index fa1669552..09759e1ed 100644 --- a/backend/src/Squidex/Squidex.csproj +++ b/backend/src/Squidex/Squidex.csproj @@ -59,17 +59,17 @@ - - - - - - + + + + + + - - - - + + + + @@ -83,11 +83,11 @@ - + - + diff --git a/backend/tests/RunCoverage.ps1 b/backend/tests/RunCoverage.ps1 index b0a6c5716..1295a76fd 100644 --- a/backend/tests/RunCoverage.ps1 +++ b/backend/tests/RunCoverage.ps1 @@ -1,86 +1,105 @@ Param( - [switch]$infrastructure, - [switch]$appsCore, - [switch]$appsEntities, - [switch]$users, - [switch]$web, - [switch]$all + [switch]$testInfrastructure, + [switch]$testAppsCore, + [switch]$testAppsEntities, + [switch]$testUsers, + [switch]$testWeb, + [switch]$testAll, + [switch]$noClean ) $ErrorActionPreference = "Stop" $folderReports = ".\_test-output" -$folderHome = $env:USERPROFILE $folderWorking = Get-Location +$versionOpenCover = "4.7.1221" +$versionReportGenerator = "5.4.1" -if (Test-Path $folderReports) { - Remove-Item $folderReports -recurse +if ($testAll) { + $testInfrastructure = $true + $testAppsCore = $true + $testAppsEntities = $true + $testUsers = $true + $testWeb = $true } -Write-Host "Recreated '$folderReports' folder" +Write-Host "Test Infrastructure: $testInfrastructure" +Write-Host "Test Apps Core: $testAppsCore" +Write-Host "Test Apps Entities: $testAppsEntities" +Write-Host "Test Users: $testUsers" +Write-Host "Test Web: $testWeb" -New-Item -ItemType directory -Path $folderReports +if (!$noClean) { + if (Test-Path $folderReports) { + Remove-Item $folderReports -recurse -if ($all -Or $infrastructure) { - &"$folderHome\.nuget\packages\OpenCover\4.7.1221\tools\OpenCover.Console.exe" ` - -register:user ` - -target:"C:\Program Files\dotnet\dotnet.exe" ` - -targetargs:"test --filter Category!=Dependencies $folderWorking\Squidex.Infrastructure.Tests\Squidex.Infrastructure.Tests.csproj" ` - -filter:"+[Squidex.*]* -[*.Tests]* -[Squidex.*]*CodeGen*" ` - -excludebyattribute:*.ExcludeFromCodeCoverage* ` - -skipautoprops ` - -output:"$folderWorking\$folderReports\Infrastructure.xml" ` - -oldStyle + Write-Host "Recreated '$folderReports' folder" + } } -if ($all -Or $appsCore) { - &"$folderHome\.nuget\packages\OpenCover\4.7.1221\tools\OpenCover.Console.exe" ` - -register:user ` - -target:"C:\Program Files\dotnet\dotnet.exe" ` - -targetargs:"test --filter Category!=Dependencies $folderWorking\Squidex.Domain.Apps.Core.Tests\Squidex.Domain.Apps.Core.Tests.csproj" ` - -filter:"+[Squidex.*]* -[*.Tests]* -[Squidex.*]*CodeGen*" ` - -excludebyattribute:*.ExcludeFromCodeCoverage* ` - -skipautoprops ` - -output:"$folderWorking\$folderReports\Core.xml" ` - -oldStyle +if (!(Test-Path $folderReports)) { + New-Item -ItemType directory -Path $folderReports } -if ($all -Or $appsEntities) { - &"$folderHome\.nuget\packages\OpenCover\4.7.1221\tools\OpenCover.Console.exe" ` - -register:user ` - -target:"C:\Program Files\dotnet\dotnet.exe" ` - -targetargs:"test --filter Category!=Dependencies $folderWorking\Squidex.Domain.Apps.Entities.Tests\Squidex.Domain.Apps.Entities.Tests.csproj" ` - -filter:"+[Squidex.*]* -[*.Tests]* -[Squidex.*]*CodeGen*" ` - -excludebyattribute:*.ExcludeFromCodeCoverage* ` - -skipautoprops ` - -output:"$folderWorking\$folderReports\Entities.xml" ` - -oldStyle +if ($testInfrastructure) { + $projectName = "Squidex.Infrastructure.Tests" + + dotnet test "$folderWorking\$projectName\$projectName.csproj" ` + --no-restore ` + --filter "Category!=Dependencies & Category!=TestContainer" ` + --collect "XPlat Code Coverage" ` + --results-directory "$folderReports" ` + --settings "$folderWorking\coverlet.runsettings.xml" +} + +if ($testAppsCore) { + $projectName = "Squidex.Domain.Apps.Core.Tests" + + dotnet test "$folderWorking\$projectName\$projectName.csproj" ` + --no-restore ` + --filter "Category!=Dependencies & Category!=TestContainer" ` + --collect "XPlat Code Coverage" ` + --results-directory "$folderReports" ` + --settings "$folderWorking\coverlet.runsettings.xml" } -if ($all -Or $users) { - &"$folderHome\.nuget\packages\OpenCover\4.7.1221\tools\OpenCover.Console.exe" ` - -register:user ` - -target:"C:\Program Files\dotnet\dotnet.exe" ` - -targetargs:"test --filter Category!=Dependencies $folderWorking\Squidex.Domain.Users.Tests\Squidex.Domain.Users.Tests.csproj" ` - -filter:"+[Squidex.*]* -[*.Tests]* -[Squidex.*]*CodeGen*" ` - -excludebyattribute:*.ExcludeFromCodeCoverage* ` - -skipautoprops ` - -output:"$folderWorking\$folderReports\Users.xml" ` - -oldStyle +if ($testAppsEntities) { + $projectName = "Squidex.Domain.Apps.Entities.Tests" + + dotnet test "$folderWorking\$projectName\$projectName.csproj" ` + --no-restore ` + --filter "Category!=Dependencies & Category!=TestContainer" ` + --collect "XPlat Code Coverage" ` + --results-directory "$folderReports" ` + --settings "$folderWorking\coverlet.runsettings.xml" } -if ($all -Or $web) { - &"$folderHome\.nuget\packages\OpenCover\4.7.1221\tools\OpenCover.Console.exe" ` - -register:user ` - -target:"C:\Program Files\dotnet\dotnet.exe" ` - -targetargs:"test --filter Category!=Dependencies $folderWorking\Squidex.Web.Tests\Squidex.Web.Tests.csproj" ` - -filter:"+[Squidex.*]* -[*.Tests]* -[Squidex.*]*CodeGen*" ` - -excludebyattribute:*.ExcludeFromCodeCoverage* ` - -skipautoprops ` - -output:"$folderWorking\$folderReports\Web.xml" ` - -oldStyle +if ($testUsers) { + $projectName = "Squidex.Domain.Users.Tests" + + dotnet test "$folderWorking\$projectName\$projectName.csproj" ` + --no-restore ` + --filter "Category!=Dependencies & Category!=TestContainer" ` + --collect "XPlat Code Coverage" ` + --results-directory "$folderReports" ` + --settings "$folderWorking\coverlet.runsettings.xml" } -&"$folderHome\.nuget\packages\ReportGenerator\5.1.9\tools\net47\ReportGenerator.exe" ` --reports:"$folderWorking\$folderReports\*.xml" ` --targetdir:"$folderWorking\$folderReports\Output" \ No newline at end of file +if ($testWeb) { + $projectName = "Squidex.Web.Tests" + + dotnet test "$folderWorking\$projectName\$projectName.csproj" ` + --no-restore ` + --filter "Category!=Dependencies & Category!=TestContainer" ` + --collect "XPlat Code Coverage" ` + --results-directory "$folderReports" ` + --settings "$folderWorking\coverlet.runsettings.xml" +} + + +dotnet tool install -g dotnet-reportgenerator-globaltool + +reportgenerator ` + -reports:"$folderReports\**\coverage.cobertura.xml" ` + -targetdir:"$folderReports\report" ` + -reporttypes:Html \ No newline at end of file diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Migrations/MySqlConnectionStringParserTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Migrations/MySqlConnectionStringParserTests.cs new file mode 100644 index 000000000..0150f88aa --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Migrations/MySqlConnectionStringParserTests.cs @@ -0,0 +1,23 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Providers.MySql; + +namespace Squidex.EntityFramework.Infrastructure.Migrations; + +public class MySqlConnectionStringParserTests +{ + [Fact] + public void Should_parse_host_name() + { + var sut = new MySqlConnectionStringParser(); + + var result = sut.GetHostName("Server=localhost;Port=33060;Database=test;User=mysql;Password=mysql"); + + Assert.Equal("localhost", result); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Migrations/PostgresConnectionStringParserTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Migrations/PostgresConnectionStringParserTests.cs new file mode 100644 index 000000000..b13da56c1 --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Migrations/PostgresConnectionStringParserTests.cs @@ -0,0 +1,23 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Providers.Postgres; + +namespace Squidex.EntityFramework.Infrastructure.Migrations; + +public class PostgresConnectionStringParserTests +{ + [Fact] + public void Should_parse_host_name() + { + var sut = new PostgresConnectionStringParser(); + + var result = sut.GetHostName("Server=localhost;Port=54320;Database=test;User=postgres;Password=postgres"); + + Assert.Equal("localhost", result); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Migrations/SqlServerConnectionStringParserTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Migrations/SqlServerConnectionStringParserTests.cs new file mode 100644 index 000000000..21f95334a --- /dev/null +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Infrastructure/Migrations/SqlServerConnectionStringParserTests.cs @@ -0,0 +1,23 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Providers.SqlServer; + +namespace Squidex.EntityFramework.Infrastructure.Migrations; + +public class SqlServerConnectionStringParserTests +{ + [Fact] + public void Should_parse_host_name() + { + var sut = new SqlServerConnectionStringParser(); + + var result = sut.GetHostName("Server=localhost;Port=14330;Database=test;User=sa;Password=sqlserver"); + + Assert.Equal("localhost", result); + } +} diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/MySqlMigrationTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/MySqlMigrationTests.cs index b1d51b5e5..c514efbd6 100644 --- a/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/MySqlMigrationTests.cs +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/MySqlMigrationTests.cs @@ -9,6 +9,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Infrastructure.Migrations; +using Squidex.Providers.MySql; using Squidex.Providers.MySql.App; using Testcontainers.MySql; @@ -34,19 +35,20 @@ public class MySqlMigrationTests : IAsyncLifetime { var services = new ServiceCollection() - .AddDbContextFactory(b => - { - var connectionString = mysql.GetConnectionString(); - - b.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString), options => - { - options.UseNetTopologySuite(); - options.UseMicrosoftJson(MySqlCommonJsonChangeTrackingOptions.FullHierarchyOptimizedSemantically); - }); - }) - .AddSingleton(TestUtils.DefaultSerializer) - .AddSingleton>() - .BuildServiceProvider(); + .AddDbContextFactory(b => + { + var connectionString = mysql.GetConnectionString(); + + b.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString), options => + { + options.UseNetTopologySuite(); + options.UseMicrosoftJson(MySqlCommonJsonChangeTrackingOptions.FullHierarchyOptimizedSemantically); + }); + }) + .AddSingleton() + .AddSingleton(TestUtils.DefaultSerializer) + .AddSingleton>() + .BuildServiceProvider(); var databaseMigrator = services.GetRequiredService>(); var databaseFactory = services.GetRequiredService>(); diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/PostgresMigrationTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/PostgresMigrationTests.cs index 0ed78b4eb..1893160ce 100644 --- a/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/PostgresMigrationTests.cs +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/PostgresMigrationTests.cs @@ -9,6 +9,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Infrastructure.Migrations; +using Squidex.Providers.Postgres; using Squidex.Providers.Postgres.App; using Testcontainers.PostgreSql; @@ -37,16 +38,17 @@ public class PostgresMigrationTests : IAsyncLifetime { var services = new ServiceCollection() - .AddDbContextFactory(b => - { - b.UseNpgsql(postgreSql.GetConnectionString(), options => - { - options.UseNetTopologySuite(); - }); - }) - .AddSingleton(TestUtils.DefaultSerializer) - .AddSingleton>() - .BuildServiceProvider(); + .AddDbContextFactory(b => + { + b.UseNpgsql(postgreSql.GetConnectionString(), options => + { + options.UseNetTopologySuite(); + }); + }) + .AddSingleton() + .AddSingleton(TestUtils.DefaultSerializer) + .AddSingleton>() + .BuildServiceProvider(); var databaseMigrator = services.GetRequiredService>(); var databaseFactory = services.GetRequiredService>(); diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/SqlServerMigrationTests.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/SqlServerMigrationTests.cs index f4146b60a..bd2c55658 100644 --- a/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/SqlServerMigrationTests.cs +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/Migrations/SqlServerMigrationTests.cs @@ -9,6 +9,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Infrastructure.Migrations; +using Squidex.Providers.SqlServer; using Squidex.Providers.SqlServer.App; using Testcontainers.MsSql; @@ -34,16 +35,17 @@ public class SqlServerMigrationTests : IAsyncLifetime { var services = new ServiceCollection() - .AddDbContextFactory(b => - { - b.UseSqlServer(sqlServer.GetConnectionString(), options => - { - options.UseNetTopologySuite(); - }); - }) - .AddSingleton(TestUtils.DefaultSerializer) - .AddSingleton>() - .BuildServiceProvider(); + .AddDbContextFactory(b => + { + b.UseSqlServer(sqlServer.GetConnectionString(), options => + { + options.UseNetTopologySuite(); + }); + }) + .AddSingleton() + .AddSingleton(TestUtils.DefaultSerializer) + .AddSingleton>() + .BuildServiceProvider(); var databaseMigrator = services.GetRequiredService>(); var databaseFactory = services.GetRequiredService>(); diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/MySqlFixture.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/MySqlFixture.cs index 108ff0e53..111edd4d7 100644 --- a/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/MySqlFixture.cs +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/MySqlFixture.cs @@ -11,6 +11,7 @@ using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Hosting; using Squidex.Infrastructure; using Squidex.Infrastructure.Migrations; +using Squidex.Providers.MySql; using Squidex.Providers.MySql.Content; using Testcontainers.MySql; @@ -58,6 +59,7 @@ public class MySqlFixture(string? reuseId = null) : IAsyncLifetime, ISqlContentF { return new MySqlContentDbContext(name, connectionString, null, jsonSerializer); }) + .AddSingleton() .AddSingletonAs>().Done() .AddSingleton(TestUtils.DefaultSerializer) .BuildServiceProvider(); diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/PostgresFixture.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/PostgresFixture.cs index cb0af6c80..5c6a9f983 100644 --- a/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/PostgresFixture.cs +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/PostgresFixture.cs @@ -11,6 +11,7 @@ using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Hosting; using Squidex.Infrastructure; using Squidex.Infrastructure.Migrations; +using Squidex.Providers.Postgres; using Squidex.Providers.Postgres.Content; using Testcontainers.PostgreSql; @@ -57,6 +58,7 @@ public class PostgresFixture(string? reuseId) : IAsyncLifetime, ISqlContentFixtu { return new PostgresContentDbContext(name, connectionString, jsonSerializer); }) + .AddSingleton() .AddSingletonAs>().Done() .AddSingleton(TestUtils.DefaultSerializer) .BuildServiceProvider(); diff --git a/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/SqlServerFixture.cs b/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/SqlServerFixture.cs index a97cb16a2..b2ecf901f 100644 --- a/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/SqlServerFixture.cs +++ b/backend/tests/Squidex.Data.Tests/EntityFramework/TestHelpers/SqlServerFixture.cs @@ -12,6 +12,7 @@ using Squidex.Domain.Apps.Core.TestHelpers; using Squidex.Hosting; using Squidex.Infrastructure; using Squidex.Infrastructure.Migrations; +using Squidex.Providers.SqlServer; using Squidex.Providers.SqlServer.Content; using Testcontainers.MsSql; @@ -59,6 +60,7 @@ public class SqlServerFixture(string? reuseId = null) : IAsyncLifetime, ISqlCont { return new SqlServerContentDbContext(name, connectionString, jsonSerializer); }) + .AddSingleton() .AddSingletonAs>().Done() .AddSingleton(TestUtils.DefaultSerializer) .BuildServiceProvider(); diff --git a/backend/tests/Squidex.Data.Tests/Squidex.Data.Tests.csproj b/backend/tests/Squidex.Data.Tests/Squidex.Data.Tests.csproj index 4f52a8b50..c569ef665 100644 --- a/backend/tests/Squidex.Data.Tests/Squidex.Data.Tests.csproj +++ b/backend/tests/Squidex.Data.Tests/Squidex.Data.Tests.csproj @@ -9,11 +9,6 @@ true SA0001;NETSDK1206 - - - - - @@ -24,6 +19,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj b/backend/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj index dd6fad11c..4b60b6398 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Squidex.Domain.Apps.Core.Tests.csproj @@ -13,6 +13,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/Guards/GuardRuleTests.cs b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/Guards/GuardRuleTests.cs index 98bc0a586..17e46ba07 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/Guards/GuardRuleTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Rules/DomainObject/Guards/GuardRuleTests.cs @@ -8,7 +8,6 @@ using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.Rules.Triggers; using Squidex.Domain.Apps.Core.TestHelpers; -using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Rules.Commands; using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Flows; diff --git a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj index 5769e7877..e41d71c85 100644 --- a/backend/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj +++ b/backend/tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj @@ -20,6 +20,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/backend/tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj b/backend/tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj index 9aec53626..264a9a577 100644 --- a/backend/tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj +++ b/backend/tests/Squidex.Domain.Users.Tests/Squidex.Domain.Users.Tests.csproj @@ -13,6 +13,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/backend/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj b/backend/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj index 9a4f4cc38..b0b665b08 100644 --- a/backend/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj +++ b/backend/tests/Squidex.Infrastructure.Tests/Squidex.Infrastructure.Tests.csproj @@ -12,6 +12,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/backend/tests/Squidex.Web.Tests/Squidex.Web.Tests.csproj b/backend/tests/Squidex.Web.Tests/Squidex.Web.Tests.csproj index b39db2a7a..f5b48f41b 100644 --- a/backend/tests/Squidex.Web.Tests/Squidex.Web.Tests.csproj +++ b/backend/tests/Squidex.Web.Tests/Squidex.Web.Tests.csproj @@ -14,6 +14,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/backend/tests/coverlet.runsettings.xml b/backend/tests/coverlet.runsettings.xml new file mode 100644 index 000000000..a3591594f --- /dev/null +++ b/backend/tests/coverlet.runsettings.xml @@ -0,0 +1,16 @@ + + + + + + + MissingAll + [*.Tests]*,[Squidex.Extensions]* + Obsolete,GeneratedCode,CompilerGenerated,ExcludeFromCodeCoverage + *.g.cs,**/Migrations/*.cs + false + + + + + \ No newline at end of file diff --git a/backend/tests/tests.sln b/backend/tests/tests.sln new file mode 100644 index 000000000..deea5137e --- /dev/null +++ b/backend/tests/tests.sln @@ -0,0 +1,60 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.2.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Squidex.Data.Tests", "Squidex.Data.Tests\Squidex.Data.Tests.csproj", "{36FAF7D1-52B7-F128-1967-AC4160DE4528}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Squidex.Data.Tests.CodeGenerator", "Squidex.Data.Tests.CodeGenerator\Squidex.Data.Tests.CodeGenerator.csproj", "{F724DB40-7C2F-4C19-7B97-77AA2268F1B5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Squidex.Domain.Apps.Core.Tests", "Squidex.Domain.Apps.Core.Tests\Squidex.Domain.Apps.Core.Tests.csproj", "{F631204E-94A4-4F1A-5011-E3255AD95AFC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Squidex.Domain.Apps.Entities.Tests", "Squidex.Domain.Apps.Entities.Tests\Squidex.Domain.Apps.Entities.Tests.csproj", "{EEC6B0AB-FA33-D18F-5620-A2CDDDA184A1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Squidex.Domain.Users.Tests", "Squidex.Domain.Users.Tests\Squidex.Domain.Users.Tests.csproj", "{F16187EF-3B10-BABF-4BEF-674DDF9177AC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Squidex.Infrastructure.Tests", "Squidex.Infrastructure.Tests\Squidex.Infrastructure.Tests.csproj", "{9C47E947-D228-B141-2B67-0D2348052F23}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Squidex.Web.Tests", "Squidex.Web.Tests\Squidex.Web.Tests.csproj", "{0811601F-4F56-C9D8-3DDE-334B32B6BF7D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {36FAF7D1-52B7-F128-1967-AC4160DE4528}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {36FAF7D1-52B7-F128-1967-AC4160DE4528}.Debug|Any CPU.Build.0 = Debug|Any CPU + {36FAF7D1-52B7-F128-1967-AC4160DE4528}.Release|Any CPU.ActiveCfg = Release|Any CPU + {36FAF7D1-52B7-F128-1967-AC4160DE4528}.Release|Any CPU.Build.0 = Release|Any CPU + {F724DB40-7C2F-4C19-7B97-77AA2268F1B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F724DB40-7C2F-4C19-7B97-77AA2268F1B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F724DB40-7C2F-4C19-7B97-77AA2268F1B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F724DB40-7C2F-4C19-7B97-77AA2268F1B5}.Release|Any CPU.Build.0 = Release|Any CPU + {F631204E-94A4-4F1A-5011-E3255AD95AFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F631204E-94A4-4F1A-5011-E3255AD95AFC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F631204E-94A4-4F1A-5011-E3255AD95AFC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F631204E-94A4-4F1A-5011-E3255AD95AFC}.Release|Any CPU.Build.0 = Release|Any CPU + {EEC6B0AB-FA33-D18F-5620-A2CDDDA184A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EEC6B0AB-FA33-D18F-5620-A2CDDDA184A1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EEC6B0AB-FA33-D18F-5620-A2CDDDA184A1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EEC6B0AB-FA33-D18F-5620-A2CDDDA184A1}.Release|Any CPU.Build.0 = Release|Any CPU + {F16187EF-3B10-BABF-4BEF-674DDF9177AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F16187EF-3B10-BABF-4BEF-674DDF9177AC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F16187EF-3B10-BABF-4BEF-674DDF9177AC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F16187EF-3B10-BABF-4BEF-674DDF9177AC}.Release|Any CPU.Build.0 = Release|Any CPU + {9C47E947-D228-B141-2B67-0D2348052F23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9C47E947-D228-B141-2B67-0D2348052F23}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9C47E947-D228-B141-2B67-0D2348052F23}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9C47E947-D228-B141-2B67-0D2348052F23}.Release|Any CPU.Build.0 = Release|Any CPU + {0811601F-4F56-C9D8-3DDE-334B32B6BF7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0811601F-4F56-C9D8-3DDE-334B32B6BF7D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0811601F-4F56-C9D8-3DDE-334B32B6BF7D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0811601F-4F56-C9D8-3DDE-334B32B6BF7D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A39F744B-33DA-4770-9FF1-0E73834C34ED} + EndGlobalSection +EndGlobal