#nullable enable using System; using System.ComponentModel.DataAnnotations.Schema; using System.IO; using System.Threading.Tasks; using BTCPayServer.Abstractions; using Dapper; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata.Builders; using NBitcoin; using NBitcoin.DataEncoders; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace BTCPayServer.Data; public class BaseEntityData { static BaseEntityData() { Settings = new JsonSerializerSettings() { ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver(), DefaultValueHandling = DefaultValueHandling.Ignore }; Serializer = JsonSerializer.Create(Settings); } public static readonly JsonSerializerSettings Settings; public static readonly JsonSerializer Serializer; /// /// User-defined custom data /// [Column("metadata", TypeName = "jsonb")] public string Metadata { get; set; } = "{}"; /// /// Data that is not user-defined, but can be used and extended internally by BTCPay Server or plugins. /// [Column("additional_data", TypeName = "jsonb")] public string AdditionalData { get; set; } = "{}"; [Column("created_at", TypeName = "timestamptz")] public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; public T? GetAdditionalData(string key) where T: class => JObject.Parse(AdditionalData)[key]?.ToObject(Serializer); public void SetAdditionalData(string key, T? obj) { if (obj is null) { RemoveAdditionalData(key); } else { var w = new StringWriter(); Serializer.Serialize(w, obj); w.Flush(); var jobj = JObject.Parse(AdditionalData); jobj[key] = JToken.Parse(w.ToString()); AdditionalData = jobj.ToString(); } } public void RemoveAdditionalData(string key) { var jobj = JObject.Parse(AdditionalData); jobj.Remove(key); AdditionalData = jobj.ToString(); } public static string GenerateId() => Encoders.Base58.EncodeData(RandomUtils.GetBytes(13)); protected static void OnModelCreateBase(EntityTypeBuilder b, ModelBuilder builder, DatabaseFacade databaseFacade) where TEntity : BaseEntityData { b.Property(x => x.CreatedAt).HasColumnName("created_at").HasColumnType("timestamptz") .HasDefaultValueSql("now()"); b.Property(x => x.Metadata).HasColumnName("metadata").HasColumnType("jsonb") .HasDefaultValueSql("'{}'::jsonb"); b.Property(x => x.AdditionalData).HasColumnName("additional_data").HasColumnType("jsonb") .HasDefaultValueSql("'{}'::jsonb"); } }