God bloody christ. There's like three layers of shit here. So firstly, apparently we were still using Npgsql.EnableLegacyTimestampBehavior. This means that time values (which are stored UTC in the database) were converted to local time when read out. This meant they were passed around as kind Local to clients (instead of UTC in the case of SQLite). That's easy enough to fix just turn off the flag and fix the couple spots we're passing a local DateTime ez. Oh but it turns out there's a DIFFERENT problem with SQLite: See SQLite we definitely store the DateTimes as UTC, but when Microsoft.Data.Sqlite reads them it reads them as Kind Unspecified instead of Utc. Why are these so bad? Because the admin notes system passes DateTime instances from EF Core straight to the rest of the game code. And that means it's a PAIN IN THE ASS to run the necessary conversions to fix the DateTime instances. GOD DAMNIT now I have to make a whole new set of "Record" entities so we avoid leaking the EF Core model entities. WAAAAAAA. Fixes #19897
89 lines
3.4 KiB
C#
89 lines
3.4 KiB
C#
using System;
|
|
using System.Linq;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.Diagnostics;
|
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
using Microsoft.EntityFrameworkCore.Storage;
|
|
using Npgsql;
|
|
|
|
namespace Content.Server.Database
|
|
{
|
|
public sealed class PostgresServerDbContext : ServerDbContext
|
|
{
|
|
public PostgresServerDbContext(DbContextOptions<PostgresServerDbContext> options) : base(options)
|
|
{
|
|
}
|
|
|
|
protected override void OnConfiguring(DbContextOptionsBuilder options)
|
|
{
|
|
((IDbContextOptionsBuilderInfrastructure) options).AddOrUpdateExtension(new SnakeCaseExtension());
|
|
|
|
options.ConfigureWarnings(x =>
|
|
{
|
|
x.Ignore(CoreEventId.ManyServiceProvidersCreatedWarning);
|
|
#if DEBUG
|
|
// for tests
|
|
x.Ignore(CoreEventId.SensitiveDataLoggingEnabledWarning);
|
|
#endif
|
|
});
|
|
|
|
#if DEBUG
|
|
options.EnableSensitiveDataLogging();
|
|
#endif
|
|
}
|
|
|
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
{
|
|
base.OnModelCreating(modelBuilder);
|
|
|
|
// ReSharper disable StringLiteralTypo
|
|
// Enforce that an address cannot be IPv6-mapped IPv4.
|
|
// So that IPv4 addresses are consistent between separate-socket and dual-stack socket modes.
|
|
modelBuilder.Entity<ServerBan>().ToTable(t =>
|
|
t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"));
|
|
|
|
modelBuilder.Entity<ServerRoleBan>().ToTable( t =>
|
|
t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"));
|
|
|
|
modelBuilder.Entity<Player>().ToTable(t =>
|
|
t.HasCheckConstraint("LastSeenAddressNotIPv6MappedIPv4",
|
|
"NOT inet '::ffff:0.0.0.0/96' >>= last_seen_address"));
|
|
|
|
modelBuilder.Entity<ConnectionLog>().ToTable(t =>
|
|
t.HasCheckConstraint("AddressNotIPv6MappedIPv4",
|
|
"NOT inet '::ffff:0.0.0.0/96' >>= address"));
|
|
|
|
// ReSharper restore StringLiteralTypo
|
|
|
|
modelBuilder.Entity<AdminLog>()
|
|
.HasIndex(l => l.Message)
|
|
.HasMethod("GIN")
|
|
.IsTsVectorExpressionIndex("english");
|
|
|
|
foreach(var entity in modelBuilder.Model.GetEntityTypes())
|
|
{
|
|
foreach(var property in entity.GetProperties())
|
|
{
|
|
if (property.FieldInfo?.FieldType == typeof(DateTime) || property.FieldInfo?.FieldType == typeof(DateTime?))
|
|
property.SetColumnType("timestamp with time zone");
|
|
}
|
|
}
|
|
}
|
|
|
|
public override IQueryable<AdminLog> SearchLogs(IQueryable<AdminLog> query, string searchText)
|
|
{
|
|
return query.Where(log => EF.Functions.ToTsVector("english", log.Message).Matches(searchText));
|
|
}
|
|
|
|
public override int CountAdminLogs()
|
|
{
|
|
using var command = new NpgsqlCommand("SELECT reltuples FROM pg_class WHERE relname = 'admin_log';", (NpgsqlConnection?) Database.GetDbConnection());
|
|
|
|
Database.GetDbConnection().Open();
|
|
var count = Convert.ToInt32((float) (command.ExecuteScalar() ?? 0));
|
|
Database.GetDbConnection().Close();
|
|
return count;
|
|
}
|
|
}
|
|
}
|