From 12ea935ce0b44bb38e1d05cdb1e3fef21dc2c5a4 Mon Sep 17 00:00:00 2001 From: GZTime Date: Sun, 31 Dec 2023 15:12:29 +0800 Subject: [PATCH] fix(logger): update database sink --- src/GZCTF/Extensions/DatabaseSinkExtension.cs | 70 +- .../20231231065800_UpdateLogModel.Designer.cs | 1388 +++++++++++++++++ .../20231231065800_UpdateLogModel.cs | 40 + .../Migrations/AppDbContextModelSnapshot.cs | 14 +- src/GZCTF/Models/Data/LogModel.cs | 2 +- src/GZCTF/Services/FlagChecker.cs | 3 +- 6 files changed, 1484 insertions(+), 33 deletions(-) create mode 100644 src/GZCTF/Migrations/20231231065800_UpdateLogModel.Designer.cs create mode 100644 src/GZCTF/Migrations/20231231065800_UpdateLogModel.cs diff --git a/src/GZCTF/Extensions/DatabaseSinkExtension.cs b/src/GZCTF/Extensions/DatabaseSinkExtension.cs index 8a251cf77..35c929e22 100644 --- a/src/GZCTF/Extensions/DatabaseSinkExtension.cs +++ b/src/GZCTF/Extensions/DatabaseSinkExtension.cs @@ -1,3 +1,4 @@ +using System.Collections.Concurrent; using Serilog; using Serilog.Configuration; using Serilog.Core; @@ -12,22 +13,30 @@ public static LoggerConfiguration Database(this LoggerSinkConfiguration loggerCo loggerConfiguration.Sink(new DatabaseSink(serviceProvider)); } -public class DatabaseSink : ILogEventSink +public class DatabaseSink : ILogEventSink, IDisposable { readonly IServiceProvider _serviceProvider; - static DateTimeOffset LastFlushTime = DateTimeOffset.FromUnixTimeSeconds(0); - static readonly List LockedLogBuffer = new(); - static readonly List LogBuffer = new(); + DateTimeOffset _lastFlushTime = DateTimeOffset.FromUnixTimeSeconds(0); + readonly CancellationTokenSource _tokenSource = new(); + readonly ConcurrentQueue _logBuffer = new(); public DatabaseSink(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; + Task.Run(() => WriteToDatabase(_tokenSource.Token), _tokenSource.Token); + } + + public void Dispose() + { + _tokenSource.Cancel(); + GC.SuppressFinalize(this); } public void Emit(LogEvent logEvent) { - if (logEvent.Level < LogEventLevel.Information) return; + if (logEvent.Level < LogEventLevel.Information) + return; LogModel logModel = new() { @@ -41,28 +50,41 @@ public void Emit(LogEvent logEvent) Exception = logEvent.Exception?.ToString() }; - lock (LogBuffer) - { - LogBuffer.Add(logModel); + _logBuffer.Enqueue(logModel); + } - var needFlush = DateTimeOffset.Now - LastFlushTime > TimeSpan.FromSeconds(10); - if (!needFlush && LogBuffer.Count < 100) return; + async Task WriteToDatabase(CancellationToken token = default) + { + List lockedLogBuffer = new(); - LockedLogBuffer.AddRange(LogBuffer); - LogBuffer.Clear(); + try + { + while (!token.IsCancellationRequested) + { + while (_logBuffer.TryDequeue(out LogModel? logModel)) + lockedLogBuffer.Add(logModel); - Task.Run(Flush); - } - } + if (lockedLogBuffer.Count > 50 || DateTimeOffset.Now - _lastFlushTime > TimeSpan.FromSeconds(10)) + { + await using AsyncServiceScope scope = _serviceProvider.CreateAsyncScope(); - async Task Flush() - { - using var scope = _serviceProvider.CreateScope(); - var dbContext = scope.ServiceProvider.GetRequiredService(); - await dbContext.Logs.AddRangeAsync(LockedLogBuffer); - await dbContext.SaveChangesAsync(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + await dbContext.Logs.AddRangeAsync(lockedLogBuffer, token); - LockedLogBuffer.Clear(); - LastFlushTime = DateTimeOffset.Now; + try + { + await dbContext.SaveChangesAsync(token); + } + finally + { + lockedLogBuffer.Clear(); + _lastFlushTime = DateTimeOffset.Now; + } + } + + await Task.Delay(TimeSpan.FromSeconds(1), token); + } + } + catch (TaskCanceledException) { } } -} +} \ No newline at end of file diff --git a/src/GZCTF/Migrations/20231231065800_UpdateLogModel.Designer.cs b/src/GZCTF/Migrations/20231231065800_UpdateLogModel.Designer.cs new file mode 100644 index 000000000..312f1457d --- /dev/null +++ b/src/GZCTF/Migrations/20231231065800_UpdateLogModel.Designer.cs @@ -0,0 +1,1388 @@ +// +using System; +using GZCTF.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace GZCTF.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20231231065800_UpdateLogModel")] + partial class UpdateLogModel + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.14") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("GZCTF.Models.Data.Attachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("LocalFileId") + .HasColumnType("integer"); + + b.Property("RemoteUrl") + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("LocalFileId"); + + b.ToTable("Attachments"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.Challenge", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AcceptedCount") + .HasColumnType("integer"); + + b.Property("AttachmentId") + .HasColumnType("integer"); + + b.Property("CPUCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("uuid"); + + b.Property("ContainerExposePort") + .HasColumnType("integer"); + + b.Property("ContainerImage") + .HasColumnType("text"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text"); + + b.Property("Difficulty") + .HasColumnType("double precision"); + + b.Property("EnableTrafficCapture") + .HasColumnType("boolean"); + + b.Property("FileName") + .HasColumnType("text"); + + b.Property("FlagTemplate") + .HasColumnType("text"); + + b.Property("GameId") + .HasColumnType("integer"); + + b.Property("Hints") + .HasColumnType("text"); + + b.Property("IsEnabled") + .HasColumnType("boolean"); + + b.Property("MemoryLimit") + .HasColumnType("integer"); + + b.Property("MinScoreRate") + .HasColumnType("double precision"); + + b.Property("OriginalScore") + .HasColumnType("integer"); + + b.Property("StorageLimit") + .HasColumnType("integer"); + + b.Property("SubmissionCount") + .HasColumnType("integer"); + + b.Property("Tag") + .HasColumnType("smallint"); + + b.Property("TestContainerId") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AttachmentId"); + + b.HasIndex("GameId"); + + b.HasIndex("TestContainerId"); + + b.ToTable("Challenges"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.CheatInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("GameId") + .HasColumnType("integer"); + + b.Property("SourceTeamId") + .HasColumnType("integer"); + + b.Property("SubmissionId") + .HasColumnType("integer"); + + b.Property("SubmitTeamId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GameId"); + + b.HasIndex("SourceTeamId"); + + b.HasIndex("SubmissionId"); + + b.HasIndex("SubmitTeamId"); + + b.ToTable("CheatInfo"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.Config", b => + { + b.Property("ConfigKey") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("ConfigKey"); + + b.ToTable("Configs"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.Container", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ContainerId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ExpectStopAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IP") + .IsRequired() + .HasColumnType("text"); + + b.Property("Image") + .IsRequired() + .HasColumnType("text"); + + b.Property("InstanceId") + .HasColumnType("integer"); + + b.Property("IsProxy") + .HasColumnType("boolean"); + + b.Property("Port") + .HasColumnType("integer"); + + b.Property("PublicIP") + .HasColumnType("text"); + + b.Property("PublicPort") + .HasColumnType("integer"); + + b.Property("StartedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("InstanceId"); + + b.ToTable("Containers"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.FlagContext", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AttachmentId") + .HasColumnType("integer"); + + b.Property("ChallengeId") + .HasColumnType("integer"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsOccupied") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("AttachmentId"); + + b.HasIndex("ChallengeId"); + + b.ToTable("FlagContexts"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.Game", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AcceptWithoutReview") + .HasColumnType("boolean"); + + b.Property("BloodBonus") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("ContainerCountLimit") + .HasColumnType("integer"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text"); + + b.Property("EndTimeUTC") + .HasColumnType("timestamp with time zone") + .HasAnnotation("Relational:JsonPropertyName", "end"); + + b.Property("Hidden") + .HasColumnType("boolean"); + + b.Property("InviteCode") + .HasColumnType("text"); + + b.Property("Organizations") + .HasColumnType("text"); + + b.Property("PosterHash") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("PracticeMode") + .HasColumnType("boolean"); + + b.Property("PrivateKey") + .IsRequired() + .HasColumnType("text"); + + b.Property("PublicKey") + .IsRequired() + .HasColumnType("text"); + + b.Property("StartTimeUTC") + .HasColumnType("timestamp with time zone") + .HasAnnotation("Relational:JsonPropertyName", "start"); + + b.Property("Summary") + .IsRequired() + .HasColumnType("text"); + + b.Property("TeamMemberCountLimit") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("WriteupDeadline") + .HasColumnType("timestamp with time zone") + .HasAnnotation("Relational:JsonPropertyName", "wpddl"); + + b.Property("WriteupNote") + .IsRequired() + .HasColumnType("text") + .HasAnnotation("Relational:JsonPropertyName", "wpnote"); + + b.HasKey("Id"); + + b.ToTable("Games"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.GameEvent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Content") + .IsRequired() + .HasColumnType("text"); + + b.Property("GameId") + .HasColumnType("integer"); + + b.Property("PublishTimeUTC") + .HasColumnType("timestamp with time zone") + .HasAnnotation("Relational:JsonPropertyName", "time"); + + b.Property("TeamId") + .HasColumnType("integer"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.Property("UserId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("GameId"); + + b.HasIndex("TeamId"); + + b.HasIndex("UserId"); + + b.ToTable("GameEvents"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.GameNotice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Content") + .IsRequired() + .HasColumnType("text"); + + b.Property("GameId") + .HasColumnType("integer"); + + b.Property("PublishTimeUTC") + .HasColumnType("timestamp with time zone") + .HasAnnotation("Relational:JsonPropertyName", "time"); + + b.Property("Type") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("GameId"); + + b.ToTable("GameNotices"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.Instance", b => + { + b.Property("ChallengeId") + .HasColumnType("integer"); + + b.Property("ParticipationId") + .HasColumnType("integer"); + + b.Property("ContainerId") + .HasColumnType("text"); + + b.Property("FlagId") + .HasColumnType("integer"); + + b.Property("IsLoaded") + .HasColumnType("boolean"); + + b.Property("IsSolved") + .HasColumnType("boolean"); + + b.Property("LastContainerOperation") + .HasColumnType("timestamp with time zone"); + + b.Property("Score") + .HasColumnType("integer"); + + b.HasKey("ChallengeId", "ParticipationId"); + + b.HasIndex("ContainerId") + .IsUnique(); + + b.HasIndex("FlagId") + .IsUnique(); + + b.HasIndex("ParticipationId"); + + b.ToTable("Instances"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.LocalFile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("Hash") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ReferenceCount") + .HasColumnType("bigint"); + + b.Property("UploadTimeUTC") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("Hash"); + + b.ToTable("Files"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.LogModel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Exception") + .HasColumnType("text"); + + b.Property("Level") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Logger") + .IsRequired() + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b.Property("RemoteIP") + .HasMaxLength(40) + .HasColumnType("character varying(40)"); + + b.Property("Status") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("TimeUTC") + .HasColumnType("timestamp with time zone"); + + b.Property("UserName") + .HasMaxLength(25) + .HasColumnType("character varying(25)"); + + b.HasKey("Id"); + + b.ToTable("Logs"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.Participation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("GameId") + .HasColumnType("integer"); + + b.Property("Organization") + .HasColumnType("text"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("TeamId") + .HasColumnType("integer"); + + b.Property("Token") + .IsRequired() + .HasColumnType("text"); + + b.Property("WriteupId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GameId"); + + b.HasIndex("TeamId"); + + b.HasIndex("WriteupId"); + + b.HasIndex("TeamId", "GameId"); + + b.ToTable("Participations"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.Post", b => + { + b.Property("Id") + .HasMaxLength(8) + .HasColumnType("character varying(8)"); + + b.Property("AuthorId") + .HasColumnType("text"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsPinned") + .HasColumnType("boolean"); + + b.Property("Summary") + .IsRequired() + .HasColumnType("text"); + + b.Property("Tags") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdateTimeUTC") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("AuthorId"); + + b.ToTable("Posts"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.Submission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Answer") + .IsRequired() + .HasMaxLength(127) + .HasColumnType("character varying(127)"); + + b.Property("ChallengeId") + .HasColumnType("integer"); + + b.Property("GameId") + .HasColumnType("integer"); + + b.Property("ParticipationId") + .HasColumnType("integer"); + + b.Property("Status") + .IsRequired() + .HasColumnType("text"); + + b.Property("SubmitTimeUTC") + .HasColumnType("timestamp with time zone") + .HasAnnotation("Relational:JsonPropertyName", "time"); + + b.Property("TeamId") + .HasColumnType("integer"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ChallengeId"); + + b.HasIndex("GameId"); + + b.HasIndex("ParticipationId"); + + b.HasIndex("Status"); + + b.HasIndex("UserId"); + + b.HasIndex("TeamId", "ChallengeId", "GameId"); + + b.ToTable("Submissions"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.Team", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AvatarHash") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Bio") + .HasMaxLength(31) + .HasColumnType("character varying(31)"); + + b.Property("CaptainId") + .IsRequired() + .HasColumnType("text"); + + b.Property("InviteToken") + .IsRequired() + .HasColumnType("text"); + + b.Property("Locked") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(15) + .HasColumnType("character varying(15)"); + + b.HasKey("Id"); + + b.HasIndex("CaptainId"); + + b.ToTable("Teams"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.UserInfo", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("AvatarHash") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Bio") + .IsRequired() + .HasMaxLength(63) + .HasColumnType("character varying(63)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("IP") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastSignedInUTC") + .HasColumnType("timestamp with time zone"); + + b.Property("LastVisitedUTC") + .HasColumnType("timestamp with time zone"); + + 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("RealName") + .IsRequired() + .HasMaxLength(7) + .HasColumnType("character varying(7)"); + + b.Property("RegisterTimeUTC") + .HasColumnType("timestamp with time zone"); + + b.Property("Role") + .HasColumnType("integer"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("StdNumber") + .IsRequired() + .HasMaxLength(31) + .HasColumnType("character varying(31)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(16) + .HasColumnType("character varying(16)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("GZCTF.Models.Data.UserParticipation", b => + { + b.Property("GameId") + .HasColumnType("integer"); + + b.Property("TeamId") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("text"); + + b.Property("ParticipationId") + .HasColumnType("integer"); + + b.HasKey("GameId", "TeamId", "UserId"); + + b.HasIndex("ParticipationId"); + + b.HasIndex("TeamId"); + + b.HasIndex("UserId", "GameId") + .IsUnique(); + + b.ToTable("UserParticipations"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys"); + }); + + 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.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("TeamUserInfo", b => + { + b.Property("MembersId") + .HasColumnType("text"); + + b.Property("TeamsId") + .HasColumnType("integer"); + + b.HasKey("MembersId", "TeamsId"); + + b.HasIndex("TeamsId"); + + b.ToTable("TeamUserInfo"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.Attachment", b => + { + b.HasOne("GZCTF.Models.Data.LocalFile", "LocalFile") + .WithMany() + .HasForeignKey("LocalFileId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("LocalFile"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.Challenge", b => + { + b.HasOne("GZCTF.Models.Data.Attachment", "Attachment") + .WithMany() + .HasForeignKey("AttachmentId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("GZCTF.Models.Data.Game", "Game") + .WithMany("Challenges") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GZCTF.Models.Data.Container", "TestContainer") + .WithMany() + .HasForeignKey("TestContainerId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Attachment"); + + b.Navigation("Game"); + + b.Navigation("TestContainer"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.CheatInfo", b => + { + b.HasOne("GZCTF.Models.Data.Game", "Game") + .WithMany() + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GZCTF.Models.Data.Participation", "SourceTeam") + .WithMany() + .HasForeignKey("SourceTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GZCTF.Models.Data.Submission", "Submission") + .WithMany() + .HasForeignKey("SubmissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GZCTF.Models.Data.Participation", "SubmitTeam") + .WithMany() + .HasForeignKey("SubmitTeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Game"); + + b.Navigation("SourceTeam"); + + b.Navigation("Submission"); + + b.Navigation("SubmitTeam"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.FlagContext", b => + { + b.HasOne("GZCTF.Models.Data.Attachment", "Attachment") + .WithMany() + .HasForeignKey("AttachmentId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("GZCTF.Models.Data.Challenge", "Challenge") + .WithMany("Flags") + .HasForeignKey("ChallengeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Attachment"); + + b.Navigation("Challenge"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.GameEvent", b => + { + b.HasOne("GZCTF.Models.Data.Game", "Game") + .WithMany("GameEvents") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GZCTF.Models.Data.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GZCTF.Models.Data.UserInfo", "User") + .WithMany() + .HasForeignKey("UserId"); + + b.Navigation("Game"); + + b.Navigation("Team"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.GameNotice", b => + { + b.HasOne("GZCTF.Models.Data.Game", "Game") + .WithMany("GameNotices") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Game"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.Instance", b => + { + b.HasOne("GZCTF.Models.Data.Challenge", "Challenge") + .WithMany("Instances") + .HasForeignKey("ChallengeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GZCTF.Models.Data.Container", "Container") + .WithOne("Instance") + .HasForeignKey("GZCTF.Models.Data.Instance", "ContainerId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("GZCTF.Models.Data.FlagContext", "FlagContext") + .WithMany() + .HasForeignKey("FlagId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("GZCTF.Models.Data.Participation", "Participation") + .WithMany("Instances") + .HasForeignKey("ParticipationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Challenge"); + + b.Navigation("Container"); + + b.Navigation("FlagContext"); + + b.Navigation("Participation"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.Participation", b => + { + b.HasOne("GZCTF.Models.Data.Game", "Game") + .WithMany("Participations") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GZCTF.Models.Data.Team", "Team") + .WithMany("Participations") + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GZCTF.Models.Data.LocalFile", "Writeup") + .WithMany() + .HasForeignKey("WriteupId"); + + b.Navigation("Game"); + + b.Navigation("Team"); + + b.Navigation("Writeup"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.Post", b => + { + b.HasOne("GZCTF.Models.Data.UserInfo", "Author") + .WithMany() + .HasForeignKey("AuthorId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Author"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.Submission", b => + { + b.HasOne("GZCTF.Models.Data.Challenge", "Challenge") + .WithMany("Submissions") + .HasForeignKey("ChallengeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GZCTF.Models.Data.Game", "Game") + .WithMany("Submissions") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GZCTF.Models.Data.Participation", "Participation") + .WithMany("Submissions") + .HasForeignKey("ParticipationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GZCTF.Models.Data.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GZCTF.Models.Data.UserInfo", "User") + .WithMany("Submissions") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired(); + + b.Navigation("Challenge"); + + b.Navigation("Game"); + + b.Navigation("Participation"); + + b.Navigation("Team"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.Team", b => + { + b.HasOne("GZCTF.Models.Data.UserInfo", "Captain") + .WithMany() + .HasForeignKey("CaptainId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Captain"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.UserParticipation", b => + { + b.HasOne("GZCTF.Models.Data.Game", "Game") + .WithMany() + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GZCTF.Models.Data.Participation", "Participation") + .WithMany("Members") + .HasForeignKey("ParticipationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GZCTF.Models.Data.Team", "Team") + .WithMany() + .HasForeignKey("TeamId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GZCTF.Models.Data.UserInfo", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Game"); + + b.Navigation("Participation"); + + b.Navigation("Team"); + + b.Navigation("User"); + }); + + 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("GZCTF.Models.Data.UserInfo", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("GZCTF.Models.Data.UserInfo", 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("GZCTF.Models.Data.UserInfo", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("GZCTF.Models.Data.UserInfo", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("TeamUserInfo", b => + { + b.HasOne("GZCTF.Models.Data.UserInfo", null) + .WithMany() + .HasForeignKey("MembersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GZCTF.Models.Data.Team", null) + .WithMany() + .HasForeignKey("TeamsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("GZCTF.Models.Data.Challenge", b => + { + b.Navigation("Flags"); + + b.Navigation("Instances"); + + b.Navigation("Submissions"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.Container", b => + { + b.Navigation("Instance"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.Game", b => + { + b.Navigation("Challenges"); + + b.Navigation("GameEvents"); + + b.Navigation("GameNotices"); + + b.Navigation("Participations"); + + b.Navigation("Submissions"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.Participation", b => + { + b.Navigation("Instances"); + + b.Navigation("Members"); + + b.Navigation("Submissions"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.Team", b => + { + b.Navigation("Participations"); + }); + + modelBuilder.Entity("GZCTF.Models.Data.UserInfo", b => + { + b.Navigation("Submissions"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/GZCTF/Migrations/20231231065800_UpdateLogModel.cs b/src/GZCTF/Migrations/20231231065800_UpdateLogModel.cs new file mode 100644 index 000000000..d9f5dc2da --- /dev/null +++ b/src/GZCTF/Migrations/20231231065800_UpdateLogModel.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace GZCTF.Migrations +{ + /// + public partial class UpdateLogModel : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "RemoteIP", + table: "Logs", + type: "character varying(40)", + maxLength: 40, + nullable: true, + oldClrType: typeof(string), + oldType: "character varying(25)", + oldMaxLength: 25, + oldNullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "RemoteIP", + table: "Logs", + type: "character varying(25)", + maxLength: 25, + nullable: true, + oldClrType: typeof(string), + oldType: "character varying(40)", + oldMaxLength: 40, + oldNullable: true); + } + } +} diff --git a/src/GZCTF/Migrations/AppDbContextModelSnapshot.cs b/src/GZCTF/Migrations/AppDbContextModelSnapshot.cs index 989184e97..546922561 100644 --- a/src/GZCTF/Migrations/AppDbContextModelSnapshot.cs +++ b/src/GZCTF/Migrations/AppDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "7.0.10") + .HasAnnotation("ProductVersion", "7.0.14") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -195,6 +195,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ExpectStopAt") .HasColumnType("timestamp with time zone"); + b.Property("IP") + .IsRequired() + .HasColumnType("text"); + b.Property("Image") .IsRequired() .HasColumnType("text"); @@ -202,10 +206,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("InstanceId") .HasColumnType("integer"); - b.Property("IP") - .IsRequired() - .HasColumnType("text"); - b.Property("IsProxy") .HasColumnType("boolean"); @@ -507,8 +507,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("text"); b.Property("RemoteIP") - .HasMaxLength(25) - .HasColumnType("character varying(25)"); + .HasMaxLength(40) + .HasColumnType("character varying(40)"); b.Property("Status") .HasMaxLength(20) diff --git a/src/GZCTF/Models/Data/LogModel.cs b/src/GZCTF/Models/Data/LogModel.cs index 8ef787131..6170eaa3a 100644 --- a/src/GZCTF/Models/Data/LogModel.cs +++ b/src/GZCTF/Models/Data/LogModel.cs @@ -18,7 +18,7 @@ public class LogModel [MaxLength(250)] public string Logger { get; set; } = string.Empty; - [MaxLength(25)] + [MaxLength(40)] public string? RemoteIP { get; set; } [MaxLength(25)] diff --git a/src/GZCTF/Services/FlagChecker.cs b/src/GZCTF/Services/FlagChecker.cs index 0d3c54cb6..a1eb77e27 100644 --- a/src/GZCTF/Services/FlagChecker.cs +++ b/src/GZCTF/Services/FlagChecker.cs @@ -6,7 +6,8 @@ namespace GZCTF.Services; -public class FlagChecker(ChannelReader channelReader, +public class FlagChecker( + ChannelReader channelReader, ChannelWriter channelWriter, ILogger logger, IServiceScopeFactory serviceScopeFactory) : IHostedService