1
0

ModelSqlite.cs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. using System;
  2. using System.Globalization;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Net;
  6. using System.Text;
  7. using System.Text.Json;
  8. using Microsoft.EntityFrameworkCore;
  9. using Microsoft.EntityFrameworkCore.Diagnostics;
  10. using Microsoft.EntityFrameworkCore.Infrastructure;
  11. using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
  12. using NpgsqlTypes;
  13. namespace Content.Server.Database
  14. {
  15. public sealed class SqliteServerDbContext : ServerDbContext
  16. {
  17. public SqliteServerDbContext(DbContextOptions<SqliteServerDbContext> options) : base(options)
  18. {
  19. }
  20. protected override void OnConfiguring(DbContextOptionsBuilder options)
  21. {
  22. ((IDbContextOptionsBuilderInfrastructure) options).AddOrUpdateExtension(new SnakeCaseExtension());
  23. options.ConfigureWarnings(x =>
  24. {
  25. x.Ignore(CoreEventId.ManyServiceProvidersCreatedWarning);
  26. #if DEBUG
  27. // for tests
  28. x.Ignore(CoreEventId.SensitiveDataLoggingEnabledWarning);
  29. #endif
  30. });
  31. #if DEBUG
  32. options.EnableSensitiveDataLogging();
  33. #endif
  34. }
  35. protected override void OnModelCreating(ModelBuilder modelBuilder)
  36. {
  37. base.OnModelCreating(modelBuilder);
  38. var ipConverter = new ValueConverter<IPAddress, string>(
  39. v => v.ToString(),
  40. v => IPAddress.Parse(v));
  41. modelBuilder.Entity<Player>()
  42. .Property(p => p.LastSeenAddress)
  43. .HasConversion(ipConverter);
  44. var ipMaskConverter = new ValueConverter<NpgsqlInet, string>(
  45. v => InetToString(v.Address, v.Netmask),
  46. v => StringToInet(v)
  47. );
  48. modelBuilder
  49. .Entity<ServerBan>()
  50. .Property(e => e.Address)
  51. .HasColumnType("TEXT")
  52. .HasConversion(ipMaskConverter);
  53. modelBuilder
  54. .Entity<ServerRoleBan>()
  55. .Property(e => e.Address)
  56. .HasColumnType("TEXT")
  57. .HasConversion(ipMaskConverter);
  58. var jsonStringConverter = new ValueConverter<JsonDocument, string>(
  59. v => JsonDocumentToString(v),
  60. v => StringToJsonDocument(v));
  61. var jsonByteArrayConverter = new ValueConverter<JsonDocument?, byte[]>(
  62. v => JsonDocumentToByteArray(v),
  63. v => ByteArrayToJsonDocument(v));
  64. modelBuilder.Entity<AdminLog>()
  65. .Property(log => log.Json)
  66. .HasConversion(jsonStringConverter);
  67. modelBuilder.Entity<Profile>()
  68. .Property(log => log.Markings)
  69. .HasConversion(jsonByteArrayConverter);
  70. // EF core can make this automatically unique on sqlite but not psql.
  71. modelBuilder.Entity<IPIntelCache>()
  72. .HasIndex(p => p.Address)
  73. .IsUnique();
  74. }
  75. public override int CountAdminLogs()
  76. {
  77. return AdminLog.Count();
  78. }
  79. private static string InetToString(IPAddress address, int mask) {
  80. if (address.IsIPv4MappedToIPv6)
  81. {
  82. // Fix IPv6-mapped IPv4 addresses
  83. // So that IPv4 addresses are consistent between separate-socket and dual-stack socket modes.
  84. address = address.MapToIPv4();
  85. mask -= 96;
  86. }
  87. return $"{address}/{mask}";
  88. }
  89. private static NpgsqlInet StringToInet(string inet) {
  90. var idx = inet.IndexOf('/', StringComparison.Ordinal);
  91. return new NpgsqlInet(
  92. IPAddress.Parse(inet.AsSpan(0, idx)),
  93. byte.Parse(inet.AsSpan(idx + 1), provider: CultureInfo.InvariantCulture)
  94. );
  95. }
  96. private static string JsonDocumentToString(JsonDocument document)
  97. {
  98. using var stream = new MemoryStream();
  99. using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions {Indented = false});
  100. document.WriteTo(writer);
  101. writer.Flush();
  102. return Encoding.UTF8.GetString(stream.ToArray());
  103. }
  104. private static JsonDocument StringToJsonDocument(string str)
  105. {
  106. return JsonDocument.Parse(str);
  107. }
  108. private static byte[] JsonDocumentToByteArray(JsonDocument? document)
  109. {
  110. if (document == null)
  111. {
  112. return Array.Empty<byte>();
  113. }
  114. using var stream = new MemoryStream();
  115. using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions {Indented = false});
  116. document.WriteTo(writer);
  117. writer.Flush();
  118. return stream.ToArray();
  119. }
  120. private static JsonDocument ByteArrayToJsonDocument(byte[] str)
  121. {
  122. return JsonDocument.Parse(str);
  123. }
  124. }
  125. }