Server names and admin log full-text search (#6327)
Co-authored-by: Julian Giebel <j.giebel@netrocks.info> Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
This commit is contained in:
1082
Content.Server.Database/Migrations/Postgres/20220313151800_ServerNameFts.Designer.cs
generated
Normal file
1082
Content.Server.Database/Migrations/Postgres/20220313151800_ServerNameFts.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,80 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Postgres
|
||||
{
|
||||
public partial class ServerNameFts : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "server_id",
|
||||
table: "round",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "server",
|
||||
columns: table => new
|
||||
{
|
||||
server_id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
name = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_server", x => x.server_id);
|
||||
});
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
"server",
|
||||
new[] {"server_id", "name"},
|
||||
new object[] { 0, "unknown" }
|
||||
);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_round_server_id",
|
||||
table: "round",
|
||||
column: "server_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_admin_log_message",
|
||||
table: "admin_log",
|
||||
column: "message")
|
||||
.Annotation("Npgsql:TsVectorConfig", "english");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_round_server_server_id",
|
||||
table: "round",
|
||||
column: "server_id",
|
||||
principalTable: "server",
|
||||
principalColumn: "server_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_round_server_server_id",
|
||||
table: "round");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "server");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_round_server_id",
|
||||
table: "round");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_admin_log_message",
|
||||
table: "admin_log");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "server_id",
|
||||
table: "round");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,6 +120,9 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
b.HasKey("Id", "RoundId")
|
||||
.HasName("PK_admin_log");
|
||||
|
||||
b.HasIndex("Message")
|
||||
.HasAnnotation("Npgsql:TsVectorConfig", "english");
|
||||
|
||||
b.HasIndex("RoundId")
|
||||
.HasDatabaseName("IX_admin_log_round_id");
|
||||
|
||||
@@ -559,12 +562,39 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("ServerId")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("server_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_round");
|
||||
|
||||
b.HasIndex("ServerId")
|
||||
.HasDatabaseName("IX_round_server_id");
|
||||
|
||||
b.ToTable("round", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Server", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("server_id");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_server");
|
||||
|
||||
b.ToTable("server", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ServerBan", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -908,6 +938,18 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
b.Navigation("Preference");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Round", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Server", "Server")
|
||||
.WithMany("Rounds")
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("FK_round_server_server_id");
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ServerBanHit", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.ServerBan", "Ban")
|
||||
@@ -1016,6 +1058,11 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
b.Navigation("AdminLogs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Server", b =>
|
||||
{
|
||||
b.Navigation("Rounds");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ServerBan", b =>
|
||||
{
|
||||
b.Navigation("BanHits");
|
||||
|
||||
1026
Content.Server.Database/Migrations/Sqlite/20220313151753_ServerNameFts.Designer.cs
generated
Normal file
1026
Content.Server.Database/Migrations/Sqlite/20220313151753_ServerNameFts.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,69 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Sqlite
|
||||
{
|
||||
public partial class ServerNameFts : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "server_id",
|
||||
table: "round",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "server",
|
||||
columns: table => new
|
||||
{
|
||||
server_id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
name = table.Column<string>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_server", x => x.server_id);
|
||||
});
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
"server",
|
||||
new[] {"server_id", "name"},
|
||||
new object[] { 0, "unknown" }
|
||||
);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_round_server_id",
|
||||
table: "round",
|
||||
column: "server_id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_round_server_server_id",
|
||||
table: "round",
|
||||
column: "server_id",
|
||||
principalTable: "server",
|
||||
principalColumn: "server_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_round_server_server_id",
|
||||
table: "round");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "server");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_round_server_id",
|
||||
table: "round");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "server_id",
|
||||
table: "round");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -522,12 +522,37 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("round_id");
|
||||
|
||||
b.Property<int>("ServerId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("server_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_round");
|
||||
|
||||
b.HasIndex("ServerId")
|
||||
.HasDatabaseName("IX_round_server_id");
|
||||
|
||||
b.ToTable("round", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Server", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("server_id");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_server");
|
||||
|
||||
b.ToTable("server", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ServerBan", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -857,6 +882,18 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
b.Navigation("Preference");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Round", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Server", "Server")
|
||||
.WithMany("Rounds")
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("FK_round_server_server_id");
|
||||
|
||||
b.Navigation("Server");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ServerBanHit", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.ServerBan", "Ban")
|
||||
@@ -965,6 +1002,11 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
b.Navigation("AdminLogs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Server", b =>
|
||||
{
|
||||
b.Navigation("Rounds");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ServerBan", b =>
|
||||
{
|
||||
b.Navigation("BanHits");
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using Content.Shared.Database;
|
||||
@@ -22,6 +23,7 @@ namespace Content.Server.Database
|
||||
public DbSet<Admin> Admin { get; set; } = null!;
|
||||
public DbSet<AdminRank> AdminRank { get; set; } = null!;
|
||||
public DbSet<Round> Round { get; set; } = null!;
|
||||
public DbSet<Server> Server { get; set; } = null!;
|
||||
public DbSet<AdminLog> AdminLog { get; set; } = null!;
|
||||
public DbSet<AdminLogPlayer> AdminLogPlayer { get; set; } = null!;
|
||||
public DbSet<Whitelist> Whitelist { get; set; } = null!;
|
||||
@@ -138,6 +140,11 @@ namespace Content.Server.Database
|
||||
modelBuilder.Entity<ConnectionLog>()
|
||||
.HasIndex(p => p.UserId);
|
||||
}
|
||||
|
||||
public virtual IQueryable<AdminLog> SearchLogs(IQueryable<AdminLog> query, string searchText)
|
||||
{
|
||||
return query.Where(log => EF.Functions.Like(log.Message, "%" + searchText + "%"));
|
||||
}
|
||||
}
|
||||
|
||||
public class Preference
|
||||
@@ -295,6 +302,20 @@ namespace Content.Server.Database
|
||||
public List<Player> Players { get; set; } = default!;
|
||||
|
||||
public List<AdminLog> AdminLogs { get; set; } = default!;
|
||||
|
||||
[ForeignKey("Server")] public int ServerId { get; set; }
|
||||
public Server Server { get; set; } = default!;
|
||||
}
|
||||
|
||||
public class Server
|
||||
{
|
||||
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Name { get; set; } = default!;
|
||||
|
||||
[InverseProperty(nameof(Round.Server))]
|
||||
public List<Round> Rounds { get; set; } = default!;
|
||||
}
|
||||
|
||||
[Index(nameof(Type))]
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using NpgsqlTypes;
|
||||
|
||||
namespace Content.Server.Database
|
||||
{
|
||||
@@ -59,6 +61,10 @@ namespace Content.Server.Database
|
||||
"NOT inet '::ffff:0.0.0.0/96' >>= address");
|
||||
// ReSharper restore StringLiteralTypo
|
||||
|
||||
modelBuilder.Entity<AdminLog>()
|
||||
.HasIndex(l => l.Message)
|
||||
.IsTsVectorExpressionIndex("english");
|
||||
|
||||
foreach(var entity in modelBuilder.Model.GetEntityTypes())
|
||||
{
|
||||
foreach(var property in entity.GetProperties())
|
||||
@@ -68,5 +74,10 @@ namespace Content.Server.Database
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override IQueryable<AdminLog> SearchLogs(IQueryable<AdminLog> query, string searchText)
|
||||
{
|
||||
return query.Where(log => EF.Functions.ToTsVector("english", log.Message).Matches(searchText));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,7 +500,7 @@ namespace Content.Server.Database
|
||||
await db.DbContext.SaveChangesAsync(cancel);
|
||||
}
|
||||
|
||||
public virtual async Task<int> AddNewRound(params Guid[] playerIds)
|
||||
public virtual async Task<int> AddNewRound(Server server, params Guid[] playerIds)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
@@ -510,7 +510,8 @@ namespace Content.Server.Database
|
||||
|
||||
var round = new Round
|
||||
{
|
||||
Players = players
|
||||
Players = players,
|
||||
ServerId = server.Id
|
||||
};
|
||||
|
||||
db.DbContext.Round.Add(round);
|
||||
@@ -574,6 +575,27 @@ namespace Content.Server.Database
|
||||
|
||||
#region Admin Logs
|
||||
|
||||
public async Task<Server> AddOrGetServer(string serverName)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
var server = await db.DbContext.Server.Where(server => server.Name.Equals(serverName)).SingleOrDefaultAsync();
|
||||
if (server != default)
|
||||
{
|
||||
return server;
|
||||
}
|
||||
|
||||
server = new Server
|
||||
{
|
||||
Name = serverName
|
||||
};
|
||||
|
||||
db.DbContext.Server.Add(server);
|
||||
|
||||
await db.DbContext.SaveChangesAsync();
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
public virtual async Task AddAdminLogs(List<QueuedLog> logs)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
@@ -160,7 +160,7 @@ namespace Content.Server.Database
|
||||
|
||||
#region Rounds
|
||||
|
||||
Task<int> AddNewRound(params Guid[] playerIds);
|
||||
Task<int> AddNewRound(Server server, params Guid[] playerIds);
|
||||
Task<Round> GetRound(int id);
|
||||
Task AddRoundPlayers(int id, params Guid[] playerIds);
|
||||
|
||||
@@ -168,6 +168,7 @@ namespace Content.Server.Database
|
||||
|
||||
#region Admin Logs
|
||||
|
||||
Task<Server> AddOrGetServer(string serverName);
|
||||
Task AddAdminLogs(List<QueuedLog> logs);
|
||||
IAsyncEnumerable<string> GetAdminLogMessages(LogFilter? filter = null);
|
||||
IAsyncEnumerable<SharedAdminLog> GetAdminLogs(LogFilter? filter = null);
|
||||
@@ -394,9 +395,9 @@ namespace Content.Server.Database
|
||||
return _db.AddAdminRankAsync(rank, cancel);
|
||||
}
|
||||
|
||||
public Task<int> AddNewRound(params Guid[] playerIds)
|
||||
public Task<int> AddNewRound(Server server, params Guid[] playerIds)
|
||||
{
|
||||
return _db.AddNewRound(playerIds);
|
||||
return _db.AddNewRound(server, playerIds);
|
||||
}
|
||||
|
||||
public Task<Round> GetRound(int id)
|
||||
@@ -414,6 +415,11 @@ namespace Content.Server.Database
|
||||
return _db.UpdateAdminRankAsync(rank, cancel);
|
||||
}
|
||||
|
||||
public Task<Server> AddOrGetServer(string serverName)
|
||||
{
|
||||
return _db.AddOrGetServer(serverName);
|
||||
}
|
||||
|
||||
public Task AddAdminLogs(List<QueuedLog> logs)
|
||||
{
|
||||
return _db.AddAdminLogs(logs);
|
||||
|
||||
@@ -427,7 +427,7 @@ namespace Content.Server.Database
|
||||
return (admins.Select(p => (p.a, p.LastSeenUserName)).ToArray(), adminRanks)!;
|
||||
}
|
||||
|
||||
public override async Task<int> AddNewRound(params Guid[] playerIds)
|
||||
public override async Task<int> AddNewRound(Server server, params Guid[] playerIds)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
|
||||
@@ -444,7 +444,8 @@ namespace Content.Server.Database
|
||||
var round = new Round
|
||||
{
|
||||
Id = nextId,
|
||||
Players = players
|
||||
Players = players,
|
||||
ServerId = server.Id
|
||||
};
|
||||
|
||||
db.DbContext.Round.Add(round);
|
||||
|
||||
@@ -6,6 +6,7 @@ using Content.Server.Maps;
|
||||
using Content.Server.Mind;
|
||||
using Content.Server.Players;
|
||||
using Content.Server.Station;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Coordinates;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Preferences;
|
||||
@@ -190,10 +191,15 @@ namespace Content.Server.GameTicking
|
||||
RoundLengthMetric.Set(0);
|
||||
|
||||
var playerIds = _playersInLobby.Keys.Select(player => player.UserId.UserId).ToArray();
|
||||
var serverName = _configurationManager.GetCVar(CCVars.AdminLogsServerName);
|
||||
// TODO FIXME AAAAAAAAAAAAAAAAAAAH THIS IS BROKEN
|
||||
// Task.Run as a terrible dirty workaround to avoid synchronization context deadlock from .Result here.
|
||||
// This whole setup logic should be made asynchronous so we can properly wait on the DB AAAAAAAAAAAAAH
|
||||
RoundId = Task.Run(async () => await _db.AddNewRound(playerIds)).Result;
|
||||
RoundId = Task.Run(async () =>
|
||||
{
|
||||
var server = await _db.AddOrGetServer(serverName);
|
||||
return await _db.AddNewRound(server, playerIds);
|
||||
}).Result;
|
||||
|
||||
var startingEvent = new RoundStartingEvent();
|
||||
RaiseLocalEvent(startingEvent);
|
||||
|
||||
@@ -401,6 +401,9 @@ namespace Content.Shared.CCVar
|
||||
public static readonly CVarDef<int> AdminLogsClientBatchSize =
|
||||
CVarDef.Create("adminlogs.client_batch_size", 1000, CVar.SERVERONLY);
|
||||
|
||||
public static readonly CVarDef<string> AdminLogsServerName =
|
||||
CVarDef.Create("adminlogs.server_name", "unknown", CVar.SERVERONLY);
|
||||
|
||||
/*
|
||||
* Atmos
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user