diff --git a/MareSynchronosServer/MareSynchronos.API/API.cs b/MareSynchronosServer/MareSynchronos.API/API.cs index 78e1a65..50f9166 100644 --- a/MareSynchronosServer/MareSynchronos.API/API.cs +++ b/MareSynchronosServer/MareSynchronos.API/API.cs @@ -8,7 +8,7 @@ namespace MareSynchronos.API { public class API { - public const int Version = 3; + public const int Version = 4; } public class FilesHubAPI diff --git a/MareSynchronosServer/MareSynchronosServer/Authentication/SecretKeyAuthenticationHandler.cs b/MareSynchronosServer/MareSynchronosServer/Authentication/SecretKeyAuthenticationHandler.cs index 401117f..a946db1 100644 --- a/MareSynchronosServer/MareSynchronosServer/Authentication/SecretKeyAuthenticationHandler.cs +++ b/MareSynchronosServer/MareSynchronosServer/Authentication/SecretKeyAuthenticationHandler.cs @@ -1,6 +1,6 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; +using System.Data; using System.Linq; using System.Security.Claims; using System.Security.Cryptography; @@ -17,71 +17,41 @@ namespace MareSynchronosServer.Authentication { public class SecretKeyAuthenticationHandler : AuthenticationHandler { - public static ConcurrentDictionary IdentificationLocks = new(); private readonly MareDbContext _mareDbContext; public const string AuthScheme = "SecretKeyAuth"; protected override async Task HandleAuthenticateAsync() { - if (!Request.Headers.ContainsKey("Authorization") || !Request.Headers.ContainsKey("CharacterNameHash")) + if (!Request.Headers.ContainsKey("Authorization")) return AuthenticateResult.Fail("Failed Authorization"); var authHeader = Request.Headers["Authorization"].ToString(); - var charNameHeader = Request.Headers["CharacterNameHash"].ToString(); - if (string.IsNullOrEmpty(authHeader) || string.IsNullOrEmpty(charNameHeader) || charNameHeader == "--") + if (string.IsNullOrEmpty(authHeader)) return AuthenticateResult.Fail("Failed Authorization"); - var isBanned = await _mareDbContext.BannedUsers.AnyAsync(u => u.CharacterIdentification == charNameHeader); - if (isBanned) - { - return AuthenticateResult.Fail("Banned"); - } - using var sha256 = SHA256.Create(); var hashedHeader = BitConverter.ToString(sha256.ComputeHash(Encoding.UTF8.GetBytes(authHeader))).Replace("-", ""); - var user = _mareDbContext.Users.SingleOrDefault(m => m.SecretKey == hashedHeader); + var user = await _mareDbContext.Users.AsNoTracking().SingleOrDefaultAsync(m => m.SecretKey == hashedHeader); if (user == null) { return AuthenticateResult.Fail("Failed Authorization"); } - if (!IdentificationLocks.TryGetValue(charNameHeader, out var lockObject)) - { - lockObject = new(); - IdentificationLocks[charNameHeader] = lockObject; - } - - if (user.CharacterIdentification != charNameHeader) - { - lock (lockObject) - { - try - { - user.CharacterIdentification = charNameHeader; - _mareDbContext.Users.Update(user); - _mareDbContext.SaveChanges(); - } - catch (DbUpdateConcurrencyException) - { - } - } - } - var claims = new List { - new Claim(ClaimTypes.Name, user.CharacterIdentification), new Claim(ClaimTypes.NameIdentifier, user.UID) }; var identity = new ClaimsIdentity(claims, nameof(SecretKeyAuthenticationHandler)); var principal = new ClaimsPrincipal(identity); - var ticket = new AuthenticationTicket(principal, this.Scheme.Name); + var ticket = new AuthenticationTicket(principal, Scheme.Name); return AuthenticateResult.Success(ticket); } - public SecretKeyAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, MareDbContext mareDbContext) : base(options, logger, encoder, clock) + public SecretKeyAuthenticationHandler(IOptionsMonitor options, + MareDbContext mareDbContext, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { _mareDbContext = mareDbContext; } diff --git a/MareSynchronosServer/MareSynchronosServer/FileCleanupService.cs b/MareSynchronosServer/MareSynchronosServer/FileCleanupService.cs index 9e9ee7e..578aac8 100644 --- a/MareSynchronosServer/MareSynchronosServer/FileCleanupService.cs +++ b/MareSynchronosServer/MareSynchronosServer/FileCleanupService.cs @@ -1,9 +1,11 @@ using System; +using System.Data; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using MareSynchronosServer.Data; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -43,35 +45,38 @@ namespace MareSynchronosServer _logger.LogInformation($"Cleaning up files older than {filesOlderThanDays} days"); - using var scope = _services.CreateScope(); - var dbContext = scope.ServiceProvider.GetService()!; - - var prevTime = DateTime.Now.Subtract(TimeSpan.FromDays(filesOlderThanDays)); - var filesToDelete = - dbContext.Files.Where(f => f.LastAccessTime < prevTime); - dbContext.RemoveRange(filesToDelete); - dbContext.SaveChanges(); - foreach (var file in filesToDelete) + try { - var fileName = Path.Combine(_configuration["CacheDirectory"], file.Hash); - if (File.Exists(fileName)) - { - _logger.LogInformation("Deleting: " + fileName); - File.Delete(fileName); - } - } - var allFiles = dbContext.Files.Where(f => f.Uploaded); - foreach (var file in allFiles) - { - var fileName = Path.Combine(_configuration["CacheDirectory"], file.Hash); - if (!File.Exists(fileName)) - { - _logger.LogInformation("File does not exist anymore: " + fileName); - dbContext.Files.Remove(file); - } - } + using var scope = _services.CreateScope(); + var dbContext = scope.ServiceProvider.GetService()!; - dbContext.SaveChanges(); + var prevTime = DateTime.Now.Subtract(TimeSpan.FromDays(filesOlderThanDays)); + + dbContext.Database.BeginTransaction(IsolationLevel.Snapshot); + var allFiles = dbContext.Files.Where(f => f.Uploaded); + foreach (var file in allFiles) + { + var fileName = Path.Combine(_configuration["CacheDirectory"], file.Hash); + if (!File.Exists(fileName)) + { + _logger.LogInformation("File does not exist anymore: " + fileName); + dbContext.Files.Remove(file); + } else if (new FileInfo(fileName).LastAccessTime < prevTime) + { + _logger.LogInformation("File outdated: " + fileName); + dbContext.Files.Remove(file); + File.Delete(fileName); + } + } + + _logger.LogInformation($"Cleanup complete"); + + dbContext.SaveChanges(); + dbContext.Database.CommitTransaction(); + } + catch + { + } } public Task StopAsync(CancellationToken cancellationToken) diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/AdminHub.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/AdminHub.cs index f0bafae..7f42708 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/AdminHub.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/AdminHub.cs @@ -81,7 +81,7 @@ namespace MareSynchronosServer.Hubs { if (!IsModerator) return null; - return await DbContext.BannedUsers.Select(b => new BannedUserDto() + return await DbContext.BannedUsers.AsNoTracking().Select(b => new BannedUserDto() { CharacterHash = b.CharacterIdentification, Reason = b.Reason @@ -94,7 +94,7 @@ namespace MareSynchronosServer.Hubs { if (!IsModerator) return null; - return await DbContext.ForbiddenUploadEntries.Select(b => new ForbiddenFileDto() + return await DbContext.ForbiddenUploadEntries.AsNoTracking().Select(b => new ForbiddenFileDto() { Hash = b.Hash, ForbiddenBy = b.ForbiddenBy @@ -107,7 +107,7 @@ namespace MareSynchronosServer.Hubs { if (!IsModerator) return null; - return await DbContext.Users.Where(b => !string.IsNullOrEmpty(b.CharacterIdentification)).Select(b => new OnlineUserDto + return await DbContext.Users.AsNoTracking().Where(b => !string.IsNullOrEmpty(b.CharacterIdentification)).Select(b => new OnlineUserDto { CharacterNameHash = b.CharacterIdentification, UID = b.UID, diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/BaseHub.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/BaseHub.cs index 88c44a9..7ff32fa 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/BaseHub.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/BaseHub.cs @@ -3,6 +3,7 @@ using System.Security.Claims; using System.Security.Cryptography; using MareSynchronosServer.Data; using Microsoft.AspNetCore.SignalR; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; namespace MareSynchronosServer.Hubs @@ -20,9 +21,9 @@ namespace MareSynchronosServer.Hubs protected string AuthenticatedUserId => Context.User?.Claims?.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value ?? "Unknown"; - protected Models.User? GetAuthenticatedUser() + protected Models.User GetAuthenticatedUserUntracked() { - return DbContext.Users.Single(u => u.UID == AuthenticatedUserId); + return DbContext.Users.AsNoTrackingWithIdentityResolution().Single(u => u.UID == AuthenticatedUserId); } protected Models.User? GetUserFromCID(string cid) diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/ConnectionHub.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/ConnectionHub.cs index 69b9e10..ca283a7 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/ConnectionHub.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/ConnectionHub.cs @@ -22,16 +22,22 @@ namespace MareSynchronosServer.Hubs } [HubMethodName(ConnectionHubAPI.InvokeHeartbeat)] - public async Task Heartbeat() + public async Task Heartbeat(string? characterIdentification) { var userId = Context.User!.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value; + Logger.LogInformation("Connection from " + userId + ", CI: " + characterIdentification); + await Clients.Caller.SendAsync(ConnectionHubAPI.OnUpdateSystemInfo, _systemInfoService.SystemInfoDto); - if (userId != null) + var isBanned = await DbContext.BannedUsers.AsNoTracking().AnyAsync(u => u.CharacterIdentification == characterIdentification); + + if (userId != null && !isBanned && !string.IsNullOrEmpty(characterIdentification)) { Logger.LogInformation("Connection from " + userId); var user = (await DbContext.Users.SingleAsync(u => u.UID == userId)); + user.CharacterIdentification = characterIdentification; + await DbContext.SaveChangesAsync(); return new ConnectionDto { ServerVersion = API.Version, diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/FilesHub.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/FilesHub.cs index 3ac2934..686d957 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/FilesHub.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/FilesHub.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Data; using System.Diagnostics; using System.IO; using System.Linq; @@ -62,11 +63,9 @@ namespace MareSynchronosServer.Hubs { Logger.LogInformation("User " + AuthenticatedUserId + " downloading file: " + hash); - var file = DbContext.Files.SingleOrDefault(f => f.Hash == hash); + var file = DbContext.Files.AsNoTracking() + .SingleOrDefault(f => f.Hash == hash); if (file == null) yield break; - file.LastAccessTime = DateTime.Now; - DbContext.Update(file); - await DbContext.SaveChangesAsync(ct); var chunkSize = 1024 * 512; // 512kb int readByteCount; var buffer = new byte[chunkSize]; @@ -85,8 +84,9 @@ namespace MareSynchronosServer.Hubs [HubMethodName(FilesHubAPI.InvokeGetFileSize)] public async Task GetFileSize(string hash) { - var file = await DbContext.Files.SingleOrDefaultAsync(f => f.Hash == hash); - var forbidden = DbContext.ForbiddenUploadEntries.SingleOrDefault(f => f.Hash == hash); + var file = await DbContext.Files.AsNoTracking().SingleOrDefaultAsync(f => f.Hash == hash); + var forbidden = DbContext.ForbiddenUploadEntries.AsNoTracking(). + SingleOrDefault(f => f.Hash == hash); var fileInfo = new FileInfo(Path.Combine(BasePath, hash)); long fileSize = 0; try @@ -122,7 +122,8 @@ namespace MareSynchronosServer.Hubs public async Task IsUploadFinished() { var userUid = AuthenticatedUserId; - return await DbContext.Files.AnyAsync(f => f.Uploader.UID == userUid && !f.Uploaded); + return await DbContext.Files.AsNoTracking() + .AnyAsync(f => f.Uploader.UID == userUid && !f.Uploaded); } public override Task OnDisconnectedAsync(Exception exception) @@ -140,7 +141,7 @@ namespace MareSynchronosServer.Hubs { fileListHashes = fileListHashes.Where(f => !string.IsNullOrEmpty(f)).Distinct().ToList(); Logger.LogInformation("User " + AuthenticatedUserId + " sending files"); - var forbiddenFiles = DbContext.ForbiddenUploadEntries.Where(f => fileListHashes.Contains(f.Hash)); + var forbiddenFiles = DbContext.ForbiddenUploadEntries.AsNoTracking().Where(f => fileListHashes.Contains(f.Hash)); var filesToUpload = new List(); filesToUpload.AddRange(forbiddenFiles.Select(f => new UploadFileDto() { @@ -217,6 +218,7 @@ namespace MareSynchronosServer.Hubs Logger.LogWarning($"Computed file hash was not expected file hash. Computed: {computedHashString}, Expected {hash}"); DbContext.Remove(relatedFile); await DbContext.SaveChangesAsync(); + return; } @@ -233,6 +235,7 @@ namespace MareSynchronosServer.Hubs DbContext.Remove(relatedFile); await DbContext.SaveChangesAsync(); } + } } } diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/UserHub.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/UserHub.cs index 95b83e6..0a3298a 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/UserHub.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/UserHub.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Data; using System.Linq; using System.Security.Cryptography; using System.Text; @@ -54,14 +55,16 @@ namespace MareSynchronosServer.Hubs { Logger.LogInformation("User " + AuthenticatedUserId + " requested online characters"); - var ownUser = DbContext.Users.Single(u => u.UID == AuthenticatedUserId); - var otherUsers = await DbContext.ClientPairs + var ownUser = GetAuthenticatedUserUntracked(); + + var otherUsers = await DbContext.ClientPairs.AsNoTracking() + .Include(u => u.User) + .Include(u => u.OtherUser) + .Where(w => w.User.UID == ownUser.UID && !w.IsPaused) + .Where(w => !string.IsNullOrEmpty(w.OtherUser.CharacterIdentification)) + .Select(e => e.OtherUser).ToListAsync(); + var otherEntries = await DbContext.ClientPairs.AsNoTracking() .Include(u => u.User) - .Include(u => u.OtherUser) - .Where(w => w.User == ownUser && !w.IsPaused) - .Where(w => !string.IsNullOrEmpty(w.OtherUser.CharacterIdentification)) - .Select(e => e.OtherUser).ToListAsync(); - var otherEntries = await DbContext.ClientPairs.Include(u => u.User) .Where(u => otherUsers.Any(e => e == u.User) && u.OtherUser == ownUser && !u.IsPaused).ToListAsync(); await Clients.Users(otherEntries.Select(e => e.User.UID)).SendAsync(UserHubAPI.OnAddOnlinePairedPlayer, ownUser.CharacterIdentification); @@ -79,8 +82,7 @@ namespace MareSynchronosServer.Hubs public async Task> GetPairedClients() { string userid = AuthenticatedUserId; - var user = GetAuthenticatedUser(); - var pairs = await DbContext.ClientPairs + var pairs = await DbContext.ClientPairs.AsNoTracking() .Include(u => u.OtherUser) .Include(u => u.User) .Where(w => w.User.UID == userid) @@ -100,22 +102,23 @@ namespace MareSynchronosServer.Hubs public override async Task OnDisconnectedAsync(Exception exception) { - var user = DbContext.Users.SingleOrDefault(u => u.UID == AuthenticatedUserId); - if (user != null) + var user = DbContext.Users.AsNoTracking().SingleOrDefault(u => u.UID == AuthenticatedUserId); + if (user != null && !string.IsNullOrEmpty(user.CharacterIdentification)) { Logger.LogInformation("Disconnect from " + AuthenticatedUserId); - var otherUsers = DbContext.ClientPairs + var otherUsers = DbContext.ClientPairs.AsNoTracking() .Include(u => u.User) .Include(u => u.OtherUser) - .Where(w => w.User == user && !w.IsPaused) + .Where(w => w.User.UID == user.UID && !w.IsPaused) .Where(w => !string.IsNullOrEmpty(w.OtherUser.CharacterIdentification)) .Select(e => e.OtherUser).ToList(); - var otherEntries = DbContext.ClientPairs.Include(u => u.User) - .Where(u => otherUsers.Any(e => e == u.User) && u.OtherUser == user && !u.IsPaused).ToList(); + var otherEntries = DbContext.ClientPairs.AsNoTracking().Include(u => u.User) + .Where(u => otherUsers.Any(e => e == u.User) && u.OtherUser.UID == user.UID && !u.IsPaused).ToList(); await Clients.Users(otherEntries.Select(e => e.User.UID)).SendAsync(UserHubAPI.OnRemoveOnlinePairedPlayer, user.CharacterIdentification); - user.CharacterIdentification = null; + + DbContext.Users.Single(u => u.UID == AuthenticatedUserId).CharacterIdentification = null; await DbContext.SaveChangesAsync(); await Clients.All.SendAsync("UsersOnline", @@ -132,7 +135,7 @@ namespace MareSynchronosServer.Hubs Logger.LogInformation("User " + AuthenticatedUserId + " pushing character data to " + visibleCharacterIds.Count + " visible clients"); var uid = AuthenticatedUserId; - var entriesHavingThisUser = DbContext.ClientPairs + var entriesHavingThisUser = DbContext.ClientPairs.AsNoTracking() .Include(w => w.User) .Include(w => w.OtherUser) .Where(w => w.OtherUser.UID == uid && !w.IsPaused @@ -140,7 +143,8 @@ namespace MareSynchronosServer.Hubs foreach (var pair in entriesHavingThisUser) { - var ownEntry = DbContext.ClientPairs.SingleOrDefault(w => + var ownEntry = DbContext.ClientPairs.AsNoTracking() + .SingleOrDefault(w => w.User.UID == uid && w.OtherUser.UID == pair.User.UID); if (ownEntry == null || ownEntry.IsPaused) continue; await Clients.User(pair.User.UID).SendAsync(UserHubAPI.OnReceiveCharacterData, characterCache, @@ -189,10 +193,13 @@ namespace MareSynchronosServer.Hubs { if (uid == AuthenticatedUserId) return; uid = uid.Trim(); - var user = await DbContext.Users.SingleAsync(u => u.UID == AuthenticatedUserId); - var otherUser = await DbContext.Users.SingleOrDefaultAsync(u => u.UID == uid); + var user = DbContext.Users.Single(u => u.UID == AuthenticatedUserId); + + var otherUser = await DbContext.Users + .SingleOrDefaultAsync(u => u.UID == uid); var existingEntry = - await DbContext.ClientPairs.SingleOrDefaultAsync(p => + await DbContext.ClientPairs.AsNoTracking() + .SingleOrDefaultAsync(p => p.User.UID == AuthenticatedUserId && p.OtherUser.UID == uid); if (otherUser == null || existingEntry != null) return; Logger.LogInformation("User " + AuthenticatedUserId + " adding " + uid + " to whitelist"); @@ -239,8 +246,10 @@ namespace MareSynchronosServer.Hubs public async Task SendPairedClientPauseChange(string uid, bool isPaused) { if (uid == AuthenticatedUserId) return; - var user = DbContext.Users.Single(u => u.UID == AuthenticatedUserId); - var otherUser = await DbContext.Users.SingleOrDefaultAsync(u => u.UID == uid); + var user = DbContext.Users.AsNoTracking() + .Single(u => u.UID == AuthenticatedUserId); + var otherUser = await DbContext.Users.AsNoTracking() + .SingleOrDefaultAsync(u => u.UID == uid); if (otherUser == null) return; Logger.LogInformation("User " + AuthenticatedUserId + " changed pause status with " + uid + " to " + isPaused); ClientPair wl = @@ -312,6 +321,6 @@ namespace MareSynchronosServer.Hubs } private ClientPair OppositeEntry(string otherUID) => - DbContext.ClientPairs.SingleOrDefault(w => w.User.UID == otherUID && w.OtherUser.UID == AuthenticatedUserId); + DbContext.ClientPairs.AsNoTracking().SingleOrDefault(w => w.User.UID == otherUID && w.OtherUser.UID == AuthenticatedUserId); } } diff --git a/MareSynchronosServer/MareSynchronosServer/Migrations/20220710104014_AddTimestampToAllTables.Designer.cs b/MareSynchronosServer/MareSynchronosServer/Migrations/20220710104014_AddTimestampToAllTables.Designer.cs new file mode 100644 index 0000000..7a30d66 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServer/Migrations/20220710104014_AddTimestampToAllTables.Designer.cs @@ -0,0 +1,176 @@ +// +using System; +using MareSynchronosServer.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace MareSynchronosServer.Migrations +{ + [DbContext(typeof(MareDbContext))] + [Migration("20220710104014_AddTimestampToAllTables")] + partial class AddTimestampToAllTables + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + + modelBuilder.Entity("MareSynchronosServer.Models.Banned", b => + { + b.Property("CharacterIdentification") + .HasColumnType("nvarchar(450)"); + + b.Property("Reason") + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.HasKey("CharacterIdentification"); + + b.ToTable("BannedUsers", (string)null); + }); + + modelBuilder.Entity("MareSynchronosServer.Models.ClientPair", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("AllowReceivingMessages") + .HasColumnType("bit"); + + b.Property("IsPaused") + .HasColumnType("bit"); + + b.Property("OtherUserUID") + .HasColumnType("nvarchar(450)"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.Property("UserUID") + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("OtherUserUID"); + + b.HasIndex("UserUID"); + + b.ToTable("ClientPairs", (string)null); + }); + + modelBuilder.Entity("MareSynchronosServer.Models.FileCache", b => + { + b.Property("Hash") + .HasColumnType("nvarchar(450)"); + + b.Property("LastAccessTime") + .HasColumnType("datetime2"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.Property("Uploaded") + .HasColumnType("bit"); + + b.Property("UploaderUID") + .HasColumnType("nvarchar(450)"); + + b.HasKey("Hash"); + + b.HasIndex("UploaderUID"); + + b.ToTable("FileCaches", (string)null); + }); + + modelBuilder.Entity("MareSynchronosServer.Models.ForbiddenUploadEntry", b => + { + b.Property("Hash") + .HasColumnType("nvarchar(450)"); + + b.Property("ForbiddenBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.HasKey("Hash"); + + b.ToTable("ForbiddenUploadEntries", (string)null); + }); + + modelBuilder.Entity("MareSynchronosServer.Models.User", b => + { + b.Property("UID") + .HasColumnType("nvarchar(450)"); + + b.Property("CharacterIdentification") + .HasColumnType("nvarchar(max)"); + + b.Property("IsAdmin") + .HasColumnType("bit"); + + b.Property("IsModerator") + .HasColumnType("bit"); + + b.Property("SecretKey") + .HasColumnType("nvarchar(max)"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.HasKey("UID"); + + b.ToTable("Users", (string)null); + }); + + modelBuilder.Entity("MareSynchronosServer.Models.ClientPair", b => + { + b.HasOne("MareSynchronosServer.Models.User", "OtherUser") + .WithMany() + .HasForeignKey("OtherUserUID"); + + b.HasOne("MareSynchronosServer.Models.User", "User") + .WithMany() + .HasForeignKey("UserUID"); + + b.Navigation("OtherUser"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("MareSynchronosServer.Models.FileCache", b => + { + b.HasOne("MareSynchronosServer.Models.User", "Uploader") + .WithMany() + .HasForeignKey("UploaderUID"); + + b.Navigation("Uploader"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/MareSynchronosServer/MareSynchronosServer/Migrations/20220710104014_AddTimestampToAllTables.cs b/MareSynchronosServer/MareSynchronosServer/Migrations/20220710104014_AddTimestampToAllTables.cs new file mode 100644 index 0000000..01836c3 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServer/Migrations/20220710104014_AddTimestampToAllTables.cs @@ -0,0 +1,38 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MareSynchronosServer.Migrations +{ + public partial class AddTimestampToAllTables : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Timestamp", + table: "ForbiddenUploadEntries", + type: "rowversion", + rowVersion: true, + nullable: true); + + migrationBuilder.AddColumn( + name: "Timestamp", + table: "BannedUsers", + type: "rowversion", + rowVersion: true, + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Timestamp", + table: "ForbiddenUploadEntries"); + + migrationBuilder.DropColumn( + name: "Timestamp", + table: "BannedUsers"); + } + } +} diff --git a/MareSynchronosServer/MareSynchronosServer/Migrations/MareDbContextModelSnapshot.cs b/MareSynchronosServer/MareSynchronosServer/Migrations/MareDbContextModelSnapshot.cs index e3b0c59..ebc28e7 100644 --- a/MareSynchronosServer/MareSynchronosServer/Migrations/MareDbContextModelSnapshot.cs +++ b/MareSynchronosServer/MareSynchronosServer/Migrations/MareDbContextModelSnapshot.cs @@ -30,6 +30,11 @@ namespace MareSynchronosServer.Migrations b.Property("Reason") .HasColumnType("nvarchar(max)"); + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + b.HasKey("CharacterIdentification"); b.ToTable("BannedUsers", (string)null); @@ -103,6 +108,11 @@ namespace MareSynchronosServer.Migrations b.Property("ForbiddenBy") .HasColumnType("nvarchar(max)"); + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + b.HasKey("Hash"); b.ToTable("ForbiddenUploadEntries", (string)null); diff --git a/MareSynchronosServer/MareSynchronosServer/Models/Banned.cs b/MareSynchronosServer/MareSynchronosServer/Models/Banned.cs index 7939a9f..32b3405 100644 --- a/MareSynchronosServer/MareSynchronosServer/Models/Banned.cs +++ b/MareSynchronosServer/MareSynchronosServer/Models/Banned.cs @@ -7,5 +7,7 @@ namespace MareSynchronosServer.Models [Key] public string CharacterIdentification { get; set; } public string Reason { get; set; } + [Timestamp] + public byte[] Timestamp { get; set; } } } diff --git a/MareSynchronosServer/MareSynchronosServer/Models/ForbiddenUploadEntry.cs b/MareSynchronosServer/MareSynchronosServer/Models/ForbiddenUploadEntry.cs index fc2f817..c5cf461 100644 --- a/MareSynchronosServer/MareSynchronosServer/Models/ForbiddenUploadEntry.cs +++ b/MareSynchronosServer/MareSynchronosServer/Models/ForbiddenUploadEntry.cs @@ -7,5 +7,7 @@ namespace MareSynchronosServer.Models [Key] public string Hash { get; set; } public string ForbiddenBy { get; set; } + [Timestamp] + public byte[] Timestamp { get; set; } } } diff --git a/MareSynchronosServer/MareSynchronosServer/Startup.cs b/MareSynchronosServer/MareSynchronosServer/Startup.cs index 07fbcc0..1522baf 100644 --- a/MareSynchronosServer/MareSynchronosServer/Startup.cs +++ b/MareSynchronosServer/MareSynchronosServer/Startup.cs @@ -41,10 +41,12 @@ namespace MareSynchronosServer services.AddSingleton(); services.AddSingleton(); - services.AddDbContext(options => + services.AddDbContextPool(options => { - options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")); - }); + options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), builder => + { + }); + }, 32000); services.AddHostedService(); services.AddHostedService(provider => provider.GetService());