Settings for the server are now combined with the settings from the SDK. This introduces a large breaking change and a migration should be created. This refactor utilizes .NET Configuration and the Options pattern. This will allow for the overriding of any setting using envionment variables. It also means that Settings.yml can be used to override any .NET configuration. A SettingsProvider implementation was created, and any updating of settings was changed from SettingsService.SaveSettings() to SettingsProvider.Update(s => { ... })
489 lines
No EOL
18 KiB
C#
489 lines
No EOL
18 KiB
C#
using LANCommander.Server.Data.Enums;
|
|
using LANCommander.Server.Data.Interceptors;
|
|
using LANCommander.Server.Data.Models;
|
|
using LANCommander.Server.Settings.Enums;
|
|
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace LANCommander.Server.Data
|
|
{
|
|
public sealed class DatabaseContext(
|
|
DbContextOptions<DatabaseContext> options) : IdentityDbContext<User, Role, Guid, UserClaim, UserRole, UserLogin, RoleClaim, UserToken>(options)
|
|
{
|
|
public static DatabaseProvider Provider = DatabaseProvider.Unknown;
|
|
public static string ConnectionString = "";
|
|
|
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
|
{
|
|
optionsBuilder.AddInterceptors(new GameSaveChangesInterceptor());
|
|
|
|
optionsBuilder.EnableDetailedErrors();
|
|
optionsBuilder.EnableSensitiveDataLogging();
|
|
|
|
switch (Provider)
|
|
{
|
|
case DatabaseProvider.SQLite:
|
|
optionsBuilder.UseSqlite(ConnectionString, options => options.MigrationsAssembly("LANCommander.Server.Data.SQLite"));
|
|
break;
|
|
|
|
case DatabaseProvider.MySQL:
|
|
optionsBuilder.UseMySql(ConnectionString, ServerVersion.AutoDetect(ConnectionString), options => options.MigrationsAssembly("LANCommander.Server.Data.MySQL"));
|
|
break;
|
|
|
|
case DatabaseProvider.PostgreSQL:
|
|
optionsBuilder.UseNpgsql(ConnectionString, options => options.MigrationsAssembly("LANCommander.Server.Data.PostgreSQL"));
|
|
break;
|
|
}
|
|
|
|
base.OnConfiguring(optionsBuilder);
|
|
}
|
|
|
|
protected override void OnModelCreating(ModelBuilder builder)
|
|
{
|
|
base.OnModelCreating(builder);
|
|
|
|
builder.ConfigureBaseRelationships<Models.Action>();
|
|
builder.ConfigureBaseRelationships<Archive>();
|
|
builder.ConfigureBaseRelationships<Category>();
|
|
builder.ConfigureBaseRelationships<ChatMessage>();
|
|
builder.ConfigureBaseRelationships<ChatThread>();
|
|
builder.ConfigureBaseRelationships<Collection>();
|
|
builder.ConfigureBaseRelationships<Company>();
|
|
builder.ConfigureBaseRelationships<Game>();
|
|
builder.ConfigureBaseRelationships<GameSave>();
|
|
builder.ConfigureBaseRelationships<Genre>();
|
|
builder.ConfigureBaseRelationships<Key>();
|
|
builder.ConfigureBaseRelationships<Library>();
|
|
builder.ConfigureBaseRelationships<Media>();
|
|
builder.ConfigureBaseRelationships<MultiplayerMode>();
|
|
builder.ConfigureBaseRelationships<Platform>();
|
|
builder.ConfigureBaseRelationships<PlaySession>();
|
|
builder.ConfigureBaseRelationships<Redistributable>();
|
|
builder.ConfigureBaseRelationships<SavePath>();
|
|
builder.ConfigureBaseRelationships<Script>();
|
|
builder.ConfigureBaseRelationships<Models.Server>();
|
|
builder.ConfigureBaseRelationships<ServerConsole>();
|
|
builder.ConfigureBaseRelationships<ServerHttpPath>();
|
|
builder.ConfigureBaseRelationships<Tag>();
|
|
builder.ConfigureBaseRelationships<Issue>();
|
|
builder.ConfigureBaseRelationships<Page>();
|
|
builder.ConfigureBaseRelationships<Role>();
|
|
builder.ConfigureBaseRelationships<User>();
|
|
builder.ConfigureBaseRelationships<UserCustomField>();
|
|
|
|
builder.Entity<Genre>()
|
|
.HasMany(g => g.Games)
|
|
.WithMany(g => g.Genres);
|
|
|
|
builder.Entity<Category>()
|
|
.HasMany(c => c.Games)
|
|
.WithMany(g => g.Categories);
|
|
|
|
builder.Entity<Category>()
|
|
.HasMany(c => c.Children)
|
|
.WithOne(c => c.Parent)
|
|
.IsRequired(false);
|
|
|
|
builder.Entity<ChatMessage>()
|
|
.HasOne(cm => cm.Thread)
|
|
.WithMany(ct => ct.Messages)
|
|
.IsRequired(true);
|
|
|
|
builder.Entity<ChatThread>()
|
|
.HasMany(ct => ct.Participants)
|
|
.WithMany(u => u.ChatThreads);
|
|
|
|
builder.Entity<Tag>()
|
|
.HasMany(t => t.Games)
|
|
.WithMany(g => g.Tags);
|
|
|
|
builder.Entity<Platform>()
|
|
.HasMany(p => p.Games)
|
|
.WithMany(g => g.Platforms);
|
|
|
|
builder.Entity<Library>()
|
|
.HasOne(l => l.User)
|
|
.WithOne(u => u.Library)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<Library>()
|
|
.HasMany(l => l.Games)
|
|
.WithMany(l => l.Libraries)
|
|
.UsingEntity<Dictionary<string, object>>(
|
|
"LibraryGame",
|
|
lg => lg.HasOne<Game>().WithMany().HasForeignKey("GameId").OnDelete(DeleteBehavior.Cascade),
|
|
lg => lg.HasOne<Library>().WithMany().HasForeignKey("LibraryId").OnDelete(DeleteBehavior.Cascade)
|
|
);
|
|
|
|
#region Game Relationships
|
|
builder.Entity<Game>()
|
|
.HasMany(g => g.Archives)
|
|
.WithOne(g => g.Game)
|
|
.IsRequired(false)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<Game>()
|
|
.HasMany(g => g.Scripts)
|
|
.WithOne(s => s.Game)
|
|
.IsRequired(false)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<Game>()
|
|
.HasMany(g => g.Keys)
|
|
.WithOne(g => g.Game)
|
|
.IsRequired(true)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<Game>()
|
|
.HasMany(g => g.Actions)
|
|
.WithOne(g => g.Game)
|
|
.IsRequired(false)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<Game>()
|
|
.HasMany(g => g.MultiplayerModes)
|
|
.WithOne(m => m.Game)
|
|
.IsRequired(true)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<Game>()
|
|
.HasMany(g => g.Media)
|
|
.WithOne(m => m.Game)
|
|
.IsRequired(false)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<Game>()
|
|
.HasMany(g => g.SavePaths)
|
|
.WithOne(p => p.Game)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<Game>()
|
|
.HasMany(g => g.PlaySessions)
|
|
.WithOne(ps => ps.Game)
|
|
.IsRequired(false)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<Game>()
|
|
.HasMany(g => g.GameSaves)
|
|
.WithOne(gs => gs.Game)
|
|
.IsRequired(false)
|
|
.OnDelete(DeleteBehavior.SetNull);
|
|
|
|
builder.Entity<Game>()
|
|
.HasMany(g => g.Developers)
|
|
.WithMany(c => c.DevelopedGames)
|
|
.UsingEntity<Dictionary<string, object>>(
|
|
"GameDeveloper",
|
|
g => g.HasOne<Company>()
|
|
.WithMany()
|
|
.HasForeignKey("DeveloperId")
|
|
.OnDelete(DeleteBehavior.Cascade),
|
|
g => g.HasOne<Game>()
|
|
.WithMany()
|
|
.HasForeignKey("GameId")
|
|
.OnDelete(DeleteBehavior.Cascade),
|
|
j => j.HasKey("GameId", "DeveloperId")
|
|
);
|
|
|
|
builder.Entity<Game>()
|
|
.HasMany(g => g.Publishers)
|
|
.WithMany(c => c.PublishedGames)
|
|
.UsingEntity<Dictionary<string, object>>(
|
|
"GamePublisher",
|
|
g => g.HasOne<Company>()
|
|
.WithMany()
|
|
.HasForeignKey("PublisherId")
|
|
.OnDelete(DeleteBehavior.Cascade),
|
|
g => g.HasOne<Game>()
|
|
.WithMany()
|
|
.HasForeignKey("GameId")
|
|
.OnDelete(DeleteBehavior.Cascade),
|
|
j => j.HasKey("GameId", "PublisherId")
|
|
);
|
|
|
|
builder.Entity<Game>()
|
|
.HasMany(g => g.Redistributables)
|
|
.WithMany(r => r.Games)
|
|
.UsingEntity<Dictionary<string, object>>(
|
|
"GameRedistributable",
|
|
gr => gr.HasOne<Redistributable>().WithMany().HasForeignKey("RedistributableId").OnDelete(DeleteBehavior.Cascade),
|
|
gr => gr.HasOne<Game>().WithMany().HasForeignKey("GameId").OnDelete(DeleteBehavior.Cascade)
|
|
);
|
|
|
|
builder.Entity<Game>()
|
|
.HasMany(g => g.DependentGames)
|
|
.WithOne(g => g.BaseGame)
|
|
.IsRequired(false)
|
|
.OnDelete(DeleteBehavior.SetNull);
|
|
|
|
builder.Entity<Game>()
|
|
.HasOne(g => g.Engine)
|
|
.WithMany(e => e.Games)
|
|
.IsRequired(false)
|
|
.OnDelete(DeleteBehavior.SetNull);
|
|
#endregion
|
|
|
|
#region Media Relationships
|
|
builder.Entity<Media>()
|
|
.HasOne(m => m.Thumbnail)
|
|
.WithOne(m => m.Parent)
|
|
.IsRequired(false)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<Media>()
|
|
.HasOne(m => m.StorageLocation)
|
|
.WithMany(l => l.Media)
|
|
.IsRequired(true)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<Media>()
|
|
.Navigation(m => m.StorageLocation)
|
|
.AutoInclude();
|
|
#endregion
|
|
|
|
#region Archive Relationships
|
|
builder.Entity<Archive>()
|
|
.Navigation(a => a.StorageLocation)
|
|
.AutoInclude();
|
|
#endregion
|
|
|
|
#region Game Save Relationships
|
|
builder.Entity<GameSave>()
|
|
.Navigation(gs => gs.StorageLocation)
|
|
.AutoInclude();
|
|
#endregion
|
|
|
|
#region User Relationships
|
|
builder.Entity<User>()
|
|
.HasMany(u => u.GameSaves)
|
|
.WithOne(gs => gs.User)
|
|
.IsRequired(true)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<User>()
|
|
.HasMany(u => u.PlaySessions)
|
|
.WithOne(ps => ps.User)
|
|
.IsRequired(true)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<User>()
|
|
.HasMany(u => u.Media)
|
|
.WithOne(m => m.User)
|
|
.IsRequired(false)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<User>()
|
|
.HasMany(u => u.CustomFields)
|
|
.WithOne(cf => cf.User)
|
|
.IsRequired(true)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<User>(b =>
|
|
{
|
|
b.ToTable("Users");
|
|
});
|
|
|
|
builder.Entity<UserRole>(b =>
|
|
{
|
|
b.ToTable("UserRoles");
|
|
});
|
|
|
|
builder.Entity<UserRole>()
|
|
.HasOne(ur => ur.User)
|
|
.WithMany(u => u.UserRoles)
|
|
.HasForeignKey(ur => ur.UserId).IsRequired()
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<UserRole>()
|
|
.HasOne(ur => ur.Role)
|
|
.WithMany(r => r.UserRoles)
|
|
.HasForeignKey(ur => ur.RoleId).IsRequired()
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<UserClaim>(b =>
|
|
{
|
|
b.ToTable("UserClaims");
|
|
});
|
|
|
|
builder.Entity<UserLogin>(b =>
|
|
{
|
|
b.ToTable("UserLogins");
|
|
});
|
|
|
|
builder.Entity<UserToken>(b =>
|
|
{
|
|
b.ToTable("UserTokens");
|
|
});
|
|
|
|
builder.Entity<Role>(b =>
|
|
{
|
|
b.ToTable("Roles");
|
|
});
|
|
|
|
builder.Entity<RoleClaim>(b =>
|
|
{
|
|
b.ToTable("RoleClaims");
|
|
});
|
|
#endregion
|
|
|
|
#region Server Relationships
|
|
builder.Entity<Data.Models.Server>()
|
|
.HasOne(s => s.Game)
|
|
.WithMany(g => g.Servers)
|
|
.IsRequired(false)
|
|
.OnDelete(DeleteBehavior.SetNull);
|
|
|
|
builder.Entity<Data.Models.Server>()
|
|
.HasMany(s => s.ServerConsoles)
|
|
.WithOne(sl => sl.Server)
|
|
.HasForeignKey(sc => sc.ServerId)
|
|
.IsRequired(true)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<Data.Models.Server>()
|
|
.HasMany(s => s.HttpPaths)
|
|
.WithOne(s => s.Server)
|
|
.HasForeignKey(sc => sc.ServerId)
|
|
.IsRequired(true)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<Data.Models.Server>()
|
|
.HasMany(s => s.Scripts)
|
|
.WithOne(s => s.Server)
|
|
.IsRequired(false)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<Data.Models.Server>()
|
|
.HasMany(s => s.Actions)
|
|
.WithOne(s => s.Server)
|
|
.IsRequired(false)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
#endregion
|
|
|
|
#region Redistributable Relationships
|
|
builder.Entity<Redistributable>()
|
|
.HasMany(r => r.Archives)
|
|
.WithOne(a => a.Redistributable)
|
|
.IsRequired(false)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<Redistributable>()
|
|
.HasMany(r => r.Scripts)
|
|
.WithOne(s => s.Redistributable)
|
|
.IsRequired(false)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
#endregion
|
|
|
|
#region Collection Relationships
|
|
builder.Entity<Collection>()
|
|
.HasMany(c => c.Games)
|
|
.WithMany(g => g.Collections)
|
|
.UsingEntity<Dictionary<string, object>>(
|
|
"CollectionGame",
|
|
cg => cg.HasOne<Game>().WithMany().HasForeignKey("GameId").OnDelete(DeleteBehavior.Cascade),
|
|
cg => cg.HasOne<Collection>().WithMany().HasForeignKey("CollectionId").OnDelete(DeleteBehavior.Cascade)
|
|
);
|
|
#endregion
|
|
|
|
#region Role Relationships
|
|
builder.Entity<Role>()
|
|
.HasMany(r => r.Collections)
|
|
.WithMany(c => c.Roles)
|
|
.UsingEntity<Dictionary<string, object>>(
|
|
"RoleCollection",
|
|
rc => rc.HasOne<Collection>().WithMany().HasForeignKey("CollectionId").OnDelete(DeleteBehavior.Cascade),
|
|
rc => rc.HasOne<Role>().WithMany().HasForeignKey("RoleId").OnDelete(DeleteBehavior.Cascade)
|
|
);
|
|
#endregion
|
|
|
|
#region Issue Relationships
|
|
builder.Entity<Issue>()
|
|
.HasOne(i => i.Game)
|
|
.WithMany(g => g.Issues)
|
|
.IsRequired(true)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
#endregion
|
|
|
|
#region Page Relationships
|
|
builder.Entity<Page>()
|
|
.HasOne(p => p.Parent)
|
|
.WithMany(p => p.Children)
|
|
.IsRequired(false)
|
|
.OnDelete(DeleteBehavior.Cascade);
|
|
|
|
builder.Entity<Page>()
|
|
.HasMany(p => p.Games)
|
|
.WithMany(g => g.Pages)
|
|
.UsingEntity<Dictionary<string, object>>(
|
|
"PageGame",
|
|
pg => pg.HasOne<Game>().WithMany().HasForeignKey("GameId").OnDelete(DeleteBehavior.Cascade),
|
|
pg => pg.HasOne<Page>().WithMany().HasForeignKey("PageId").OnDelete(DeleteBehavior.Cascade)
|
|
);
|
|
|
|
builder.Entity<Page>()
|
|
.HasMany(p => p.Redistributables)
|
|
.WithMany(g => g.Pages)
|
|
.UsingEntity<Dictionary<string, object>>(
|
|
"PageRedistributable",
|
|
pg => pg.HasOne<Redistributable>().WithMany().HasForeignKey("RedistributableId").OnDelete(DeleteBehavior.Cascade),
|
|
pg => pg.HasOne<Page>().WithMany().HasForeignKey("PageId").OnDelete(DeleteBehavior.Cascade)
|
|
);
|
|
|
|
builder.Entity<Page>()
|
|
.HasMany(p => p.Servers)
|
|
.WithMany(g => g.Pages)
|
|
.UsingEntity<Dictionary<string, object>>(
|
|
"PageServer",
|
|
pg => pg.HasOne<Models.Server>().WithMany().HasForeignKey("ServerId").OnDelete(DeleteBehavior.Cascade),
|
|
pg => pg.HasOne<Page>().WithMany().HasForeignKey("PageId").OnDelete(DeleteBehavior.Cascade)
|
|
);
|
|
#endregion
|
|
|
|
#region Chat Relationships
|
|
|
|
builder.Entity<ChatThreadReadStatus>()
|
|
.HasNoKey();
|
|
|
|
builder.Entity<ChatThreadReadStatus>()
|
|
.HasOne(rs => rs.Thread)
|
|
.WithMany();
|
|
|
|
builder.Entity<ChatThreadReadStatus>()
|
|
.HasOne(rs => rs.User)
|
|
.WithMany();
|
|
#endregion
|
|
}
|
|
|
|
public DbSet<Game>? Games { get; set; }
|
|
|
|
public DbSet<Genre>? Genres { get; set; }
|
|
|
|
public DbSet<Category>? Categories { get; set; }
|
|
|
|
public DbSet<Tag>? Tags { get; set; }
|
|
|
|
public DbSet<Company>? Companies { get; set; }
|
|
|
|
public DbSet<Key>? Keys { get; set; }
|
|
|
|
public DbSet<GameSave>? GameSaves { get; set; }
|
|
|
|
public DbSet<PlaySession>? PlaySessions { get; set; }
|
|
|
|
public DbSet<Data.Models.Server>? Servers { get; set; }
|
|
|
|
public DbSet<ServerConsole>? ServerConsoles { get; set; }
|
|
|
|
public DbSet<Redistributable>? Redistributables { get; set; }
|
|
|
|
public DbSet<Media>? Media { get; set; }
|
|
public DbSet<Issue>? Issues { get; set; }
|
|
public DbSet<Page>? Pages { get; set; }
|
|
public DbSet<StorageLocation>? StorageLocations { get; set; }
|
|
public DbSet<Role>? Roles { get; set; }
|
|
public DbSet<User>? Users { get; set; }
|
|
public DbSet<ChatThread>? ChatThreads { get; set; }
|
|
public DbSet<ChatMessage>? ChatMessages { get; set; }
|
|
public DbSet<ChatThreadReadStatus>? ChatThreadReadStatuses { get; set; }
|
|
}
|
|
} |