This commit is contained in:
2023-02-19 00:43:43 +01:00
parent 9719a0c0fd
commit 1e66851113
146 changed files with 738 additions and 382 deletions

View File

@ -0,0 +1,44 @@
using PrivaPub.Models.ActivityPub.Extra;
using System.Text.Json.Serialization;
namespace PrivaPub.Models.ActivityPub
{
[JsonSerializable(typeof(ActivityPubActivity))]
public class ActivityPubActivity : ActivityPubObject
{
[JsonPropertyName("actor")]
public ActivityPubActor Actor { get; set; }
[JsonPropertyName("object")]
public ActivityPubObject Object { get; set; }
[JsonPropertyName("target")]
public ActivityPubObject Target { get; set; }
[JsonPropertyName("result")]
public ActivityPubResult Result { get; set; }
[JsonPropertyName("origin")]
public ActivityPubOrigin Origin { get; set; }
[JsonPropertyName("instrument")]
public ActivityPubInstrument Instrument { get; set; }
[JsonIgnore]
public bool NeedsObjectForInbox => Type is
ObjectType.Create or
ObjectType.Update or
ObjectType.Delete or
ObjectType.Follow or
ObjectType.Add or
ObjectType.Remove or
ObjectType.Like or
ObjectType.Block or
ObjectType.Undo;
[JsonIgnore]
public bool NeedsTargetForInbox => Type is
ObjectType.Add or
ObjectType.Remove;
}
}

View File

@ -0,0 +1,79 @@
using PrivaPub.Models.ActivityPub.Extra;
using System.Text.Json.Serialization;
namespace PrivaPub.Models.ActivityPub
{
[JsonSerializable(typeof(ActivityPubActor))]
public class ActivityPubActor : ActivityPubObject
{
[JsonPropertyName("type")]
public new ObjectType? Type => ObjectType.Person;
[JsonPropertyName("inbox")]
public string Inbox { get; set; }
[JsonPropertyName("outbox")]
public string Outbox { get; set; }
[JsonPropertyName("url")]
public string ProfileURL { get; set; }
[JsonPropertyName("preferredUsername")]
public string PreferredUsername { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("summary")]
public string Summary { get; set; }
[JsonPropertyName("icon")]
public ActivityPubIcon Icon { get; set; }
[JsonPropertyName("image")]
public ActivityPubIcon Thumbnail { get; set; }
[JsonPropertyName("manuallyApprovesFollowers")]
public bool ManuallyApprovesFollowers { get; set; } = false;
[JsonPropertyName("discoverable")]
public bool Discoverable { get; set; } = true;
[JsonPropertyName("publicKey")]
public ActivityPubPublicKey PublicKey { get; set; } = new();
[JsonPropertyName("endpoints")]
public ActivityPubActorEndpoints Endpoints { get; set; } = new();
[JsonPropertyName("attachment")]
public IEnumerable<ActivityPubAttachment> Attachment { get; set; } = Enumerable.Empty<ActivityPubAttachment>();
[JsonPropertyName("tag")]
public IEnumerable<string> Tags => Enumerable.Empty<string>();
}
public class ActivityPubAttachment
{
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("value")]
public string Value { get; set; }
}
public class ActivityPubActorEndpoints
{
[JsonPropertyName("sharedInbox")]
public string SharedInboxURL { get; set; }
[JsonPropertyName("oauthAuthorizationEndpoint")]
public string OAuthAuthorizationEndpoint { get; set; }
[JsonExtensionData]
public Dictionary<string, object> OtherData { get; set; }
}
public class ActivityPubPublicKey
{
[JsonPropertyName("id")]
public string Id { get; set; }
[JsonPropertyName("owner")]
public string Owner { get; set; }
[JsonPropertyName("publicKeyPem")]
public string PublicKeyPem { get; set; }
}
}

View File

@ -0,0 +1,24 @@
using System.Text.Json.Serialization;
namespace PrivaPub.Models.ActivityPub
{
[JsonSerializable(typeof(ActivityPubCollection))]
public class ActivityPubCollection : ActivityPubObject
{
[JsonPropertyName("type")]
public new ObjectType Type => ObjectType.Collection;
[JsonPropertyName("current")]
public string RecentlyUpdatedItem { get; set; }
[JsonPropertyName("first")]
public string FirstItem { get; set; }
[JsonPropertyName("last")]
public string LastItem { get; set; }
[JsonPropertyName("items")]
public List<ActivityPubLink> Items { get; set; }
[JsonPropertyName("totalItems")]
public int TotalItems => Items.Count;
}
}

View File

@ -0,0 +1,18 @@
using System.Text.Json.Serialization;
namespace PrivaPub.Models.ActivityPub
{
[JsonSerializable(typeof(ActivityPubCollectionPage))]
public class ActivityPubCollectionPage : ActivityPubCollection
{
[JsonPropertyName("type")]
public new ObjectType Type => ObjectType.CollectionPage;
[JsonPropertyName("partOf")]
public string BaseCollectionLink { get; set; }
[JsonPropertyName("next")]
public string NextCollectionLink { get; set; }
[JsonPropertyName("prev")]
public string PreviousCollectionLink { get; set; }
}
}

View File

@ -0,0 +1,28 @@
using System.Text.Json.Serialization;
namespace PrivaPub.Models.ActivityPub
{
[JsonSerializable(typeof(ActivityPubLink))]
public class ActivityPubLink : ActivityPubObject
{
[JsonPropertyName("id")]
public new ObjectType Type { get; set; } = ObjectType.Mention;
[JsonPropertyName("href")]
public string Href { get; set; }
[JsonPropertyName("preview")]
public string Preview { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("rel")]
public string Relation { get; set; }
[JsonPropertyName("hreflang")]
public string HrefLang { get; set; }
[JsonPropertyName("height")]
public int Height { get; set; }
[JsonPropertyName("width")]
public int Width { get; set; }
}
}

View File

@ -0,0 +1,126 @@
using PrivaPub.Models.ActivityPub.Extra;
using System.Text.Json.Serialization;
namespace PrivaPub.Models.ActivityPub
{
[JsonSerializable(typeof(ActivityPubObject))]
public partial class ActivityPubObject
{
[JsonPropertyName("@context")]
public List<object> Context => new()
{
"https://www.w3.org/ns/activitystreams",
"https://{0}/schemas/litepub-0.1.jsonld",
new ActivityPubContextLanguage()
};
[JsonPropertyName("id")]
public string Id { get; set; }
[JsonPropertyName("type")]
public ObjectType? Type { get; set; }
[JsonPropertyName("mediaType")]
public string MediaType { get; set; }
[JsonPropertyName("published")]
public DateTime? Published { get; set; }
[JsonPropertyName("updated")]
public DateTime? Updated { get; set; }
[JsonExtensionData]
public Dictionary<string, object> OtherData { get; set; }
[JsonPropertyName("source")]
public ActivityPubSource Source { get; set; }
//and part of link
[JsonPropertyName("to")]
public string[] To { get; set; }
[JsonPropertyName("cc")]
public string[] Cc { get; set; }
[JsonPropertyName("bcc")]
public string[] Bcc { get; set; }
[JsonPropertyName("bto")]
public string[] Bto { get; set; }
[JsonPropertyName("audience")]
public ActivityPubAudience Audience { get; set; }
[JsonIgnore]
public MacroType MacroType => Type switch
{
ObjectType.Article or
ObjectType.Audio or
ObjectType.Document or
ObjectType.Event or
ObjectType.Image or
ObjectType.Note or
ObjectType.Page or
ObjectType.Place or
ObjectType.Profile or
ObjectType.Relationship or
ObjectType.Tombstone or
ObjectType.Video => MacroType.Object,
ObjectType.Mention => MacroType.Link,
ObjectType.Application or
ObjectType.Group or
ObjectType.Organization or
ObjectType.Person or
ObjectType.Service => MacroType.Actor,
ObjectType.Accept or
ObjectType.Add or
ObjectType.Announce or
ObjectType.Arrive or
ObjectType.Block or
ObjectType.Create or
ObjectType.Delete or
ObjectType.Dislike or
ObjectType.Flag or
ObjectType.Follow or
ObjectType.Ignore or
ObjectType.Invite or
ObjectType.Join or
ObjectType.Leave or
ObjectType.Like or
ObjectType.Listen or
ObjectType.Move or
ObjectType.Offer or
ObjectType.Question or
ObjectType.Reject or
ObjectType.Read or
ObjectType.Remove or
ObjectType.TentativeReject or
ObjectType.TentativeAccept or
ObjectType.Travel or
ObjectType.Undo or
ObjectType.Update or
ObjectType.View => MacroType.Activity,
ObjectType.Collection => MacroType.Collection,
ObjectType.CollectionPage => MacroType.CollectionPage,
ObjectType.OrderedCollection => MacroType.OrderedCollection,
ObjectType.OrderedCollectionPage => MacroType.OrderedCollectionPage,
_ => MacroType.Unknown
};
}
public class ActivityPubContextLanguage
{
[JsonPropertyName("@language")]
public string Language { get; set; } = "und";
}
public class ActivityPubSource
{
[JsonPropertyName("content")]
public string Content { get; set; }
[JsonPropertyName("mediaType")]
public string MediaType { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using System.Text.Json.Serialization;
namespace PrivaPub.Models.ActivityPub
{
[JsonSerializable(typeof(ActivityPubOrderedCollection))]
public class ActivityPubOrderedCollection : ActivityPubCollection
{
[JsonPropertyName("type")]
public new ObjectType Type => ObjectType.OrderedCollection;
[JsonPropertyName("orderedItems")]
public List<ActivityPubLink> OrderedItems { get; set; }
[JsonPropertyName("totalItems")]
public new int TotalItems => OrderedItems?.Count ?? default;
}
}

View File

@ -0,0 +1,27 @@
using System.Text.Json.Serialization;
namespace PrivaPub.Models.ActivityPub
{
[JsonSerializable(typeof(ActivityPubOrderedCollectionPage))]
public class ActivityPubOrderedCollectionPage : ActivityPubCollection
{
[JsonPropertyName("type")]
public new ObjectType Type => ObjectType.OrderedCollectionPage;
[JsonPropertyName("partOf")]
public string BaseCollectionLink { get; set; }
[JsonPropertyName("next")]
public string NextCollectionLink { get; set; }
[JsonPropertyName("prev")]
public string PreviousCollectionLink { get; set; }
[JsonPropertyName("startIndex")]
public uint? StartIndex { get; set; }
[JsonPropertyName("orderedItems")]
public List<ActivityPubLink> OrderedItems { get; set; }
[JsonPropertyName("totalItems")]
public new int TotalItems => OrderedItems?.Count ?? default;
}
}

View File

@ -0,0 +1,14 @@
using System.Text.Json.Serialization;
namespace PrivaPub.Models.ActivityPub
{
[JsonSerializable(typeof(ActivityPubTombstone))]
public class ActivityPubTombstone : ActivityPubObject
{
[JsonPropertyName("formerType")]
public ObjectType FormerType { get; set; } = ObjectType.Unknown;
[JsonPropertyName("deleted")]
public DateTime Deleted { get; set; } = DateTime.UtcNow;
}
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace PrivaPub.Models.ActivityPub.Extra
{
[JsonSerializable(typeof(ActivityPubPublicKey))]
public class ActivityPubAudience
{
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
}
}

View File

@ -0,0 +1,19 @@
using System.Text.Json.Serialization;
namespace PrivaPub.Models.ActivityPub.Extra
{
[JsonSerializable(typeof(ActivityPubPublicKey))]
public class ActivityPubIcon
{
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("url")]
public string URL { get; set; }
[JsonPropertyName("width")]
public int Width { get; set; }
[JsonPropertyName("height")]
public int Height { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace PrivaPub.Models.ActivityPub.Extra
{
[JsonSerializable(typeof(ActivityPubPublicKey))]
public class ActivityPubInstrument
{
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace PrivaPub.Models.ActivityPub.Extra
{
[JsonSerializable(typeof(ActivityPubPublicKey))]
public class ActivityPubOrigin
{
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System.Text.Json.Serialization;
namespace PrivaPub.Models.ActivityPub.Extra
{
[JsonSerializable(typeof(ActivityPubPublicKey))]
public class ActivityPubResult
{
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
}
}

View File

@ -0,0 +1,17 @@
namespace PrivaPub.Models.ActivityPub
{
public enum MacroType
{
Object,
Link,
Actor,
Activity,
Collection,
OrderedCollection,
CollectionPage,
OrderedCollectionPage,
Unknown
}
}

View File

@ -0,0 +1,63 @@
namespace PrivaPub.Models.ActivityPub
{
public enum ObjectType
{
//Object types
Article,
Audio,
Document,
Event,
Image,
Note,
Page,
Place,
Profile,
Relationship,
Tombstone,
Video,
//Link types
Mention,
//Actor
Application,
Group,
Organization,
Person,
Service,
//Activity
Accept,
Add,
Announce,
Arrive,
Block,
Create,
Delete,
Dislike,
Flag,
Follow,
Ignore,
Invite,
Join,
Leave,
Like,
Listen,
Move,
Offer,
Question,
Reject,
Read,
Remove,
TentativeReject,
TentativeAccept,
Travel,
Undo,
Update,
View,
//Collections
Collection,
OrderedCollection,
CollectionPage,
OrderedCollectionPage,
//NULL or default
Unknown
}
}

View File

@ -0,0 +1,21 @@
using MongoDB.Entities;
using PrivaPub.Models.Email;
using PrivaPub.StaticServices;
namespace PrivaPub.Models
{
public class AppConfiguration : Entity
{
public bool RequiresFirstTimeSetup { get; set; } = true;
public string Version { get; set; }
public int MaxAllowedUploadFiles { get; set; }
public int MaxAllowedFileSize { get; set; }
public string ValidDiscussionFilesTypes { get; set; }
public List<string> SupportedLanguages { get; set; } = new();
public string BackendBaseAddress { get; set; }
public EmailConfiguration EmailConfiguration { get; set; }
public HashingOptions HashingOptions { get; set; }
public DateTime LastUpdateDate { get; set; } = DateTime.UtcNow;
}
}

View File

@ -0,0 +1,11 @@
using MongoDB.Entities;
namespace PrivaPub.Models.Data
{
public class Language : Entity
{
public string EnglishName { get; set; }
public string NativeName { get; set; }
public string International2Code { get; set; }
}
}

View File

@ -0,0 +1,11 @@
namespace PrivaPub.Models.Email
{
public class EmailConfiguration
{
public string SmtpServer { get; set; }
public int SmtpPort { get; set; }
public bool UseSSL { get; set; }
public string SmtpUsername { get; set; }
public string SmtpPassword { get; set; }
}
}

View File

@ -0,0 +1,9 @@
namespace PrivaPub.Models
{
public class MongoSettings
{
public string Database { get; set; }
public string LogsDatabase { get; set; }
public string ConnectionString { get; set; }
}
}

View File

@ -0,0 +1,22 @@
using MongoDB.Entities;
namespace PrivaPub.Models.Post
{
public class DmPost : Entity
{
public string GroupUserId { get; set; }
public string AnsweringToPostId { get; set; }
public string GroupId { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public List<PostMedia> Media { get; set; } = new();
public List<float> Location { get; set; } = new();
public bool HasContentWarning { get; set; } = false;
public bool IsFederatedCopy { get; set; }
public DateTime CreationDate { get; set; } = DateTime.UtcNow;
public DateTime? UpdateDate { get; set; } = DateTime.UtcNow;
public string UpdateReason { get; set; }
}
}

View File

@ -0,0 +1,23 @@
using MongoDB.Entities;
namespace PrivaPub.Models.Post
{
public class Post : Entity
{
public string GroupUserId { get; set; }
public string AnsweringToPostId { get; set; }
public string GroupId { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public List<PostMedia> Media { get; set; } = new();
public List<float> Location { get; set; } = new();
public float RangeKm { get; set; } = 5.0f;
public bool HasContentWarning { get; set; } = false;
public bool IsFederatedCopy { get; set; }
public DateTime CreationDate { get; set; } = DateTime.UtcNow;
public DateTime? UpdateDate { get; set; } = DateTime.UtcNow;
public string UpdateReason { get; set; }
}
}

View File

@ -0,0 +1,12 @@
namespace PrivaPub.Models.Post
{
public class PostBoost
{
public string PostId { get; set; }
public string GroupUserId { get; set; }
public bool IsFederatedCopy { get; set; }
public DateTime CreationDate { get; set; }
}
}

View File

@ -0,0 +1,12 @@
namespace PrivaPub.Models.Post
{
public class PostMedia
{
public Guid Id { get; set; } = Guid.NewGuid();
public string ContentType { get; set; }
public string FileName { get; set; }
public string Extension { get; set; }
public string Path { get; set; }
public string URL { get; set; }
}
}

View File

@ -0,0 +1,116 @@
using MongoDB.Entities;
namespace PrivaPub.Models.User
{
public class Avatar : Entity
{
public string Url { get; set; }//url
public string Name { get; set; }//name
public string UserName { get; set; }//preferredUsername
public string Biography { get; set; }//summary
public Dictionary<string, string> SharedPersonalContacts { get; set; } = new();
public string PrivateKey { get; set; }
public string PublicKey { get; set; }
public AvatarAccountState AccountState { get; set; } = AvatarAccountState.Normal;
public AvatarSettings Settings { get; set; } = new();
public Dictionary<string, string> Fields { get; set; } = new();
public string Domain { get; set; }
public string PersonalNote { get; set; }
public string ModerationNote { get; set; }
public string InboxURL { get; set; }
public string OutboxURL { get; set; }
public string MovedToURL { get; set; }
public string PictureURL { get; set; }//icon
public string ThumbnailURL { get; set; }//image
public AvatarType AvatarType { get; set; } = AvatarType.Person;
public AvatarServer AvatarServer { get; set; } = AvatarServer.PrivaPub;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime? SilencedAt { get; set; }
public DateTime? SuspendedAt { get; set; }
public DateTime? BannedAt { get; set; }
public DateTime? DeletionAt { get; set; }
}
public class ForeignAvatar : Entity
{
public string Url { get; set; }//url
public string Name { get; set; }//name
public string UserName { get; set; }//preferredUsername
public string Biography { get; set; }//summary
public Dictionary<string, string> SharedPersonalContacts { get; set; } = new();
public string PrivateKey { get; set; }
public string PublicKey { get; set; }
public AvatarAccountState AccountState { get; set; } = AvatarAccountState.Normal;
public AvatarSettings Settings { get; set; } = new();
public Dictionary<string, string> Fields { get; set; } = new();
public string Domain { get; set; }
public string PersonalNote { get; set; }
public string ModerationNote { get; set; }
public bool IsDiscoverable { get; set; } = true;
public string InboxURL { get; set; }
public string OutboxURL { get; set; }
public string MovedToURL { get; set; }
public string PictureURL { get; set; }//icon
public string ThumbnailURL { get; set; }//image
public AvatarType AvatarType { get; set; } = AvatarType.Person;
public AvatarServer AvatarServer { get; set; } = AvatarServer.Unknown;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime? SilencedAt { get; set; }
public DateTime? SuspendedAt { get; set; }
public DateTime? BannedAt { get; set; }
public DateTime? DeletionAt { get; set; }
}
public class AvatarSettings
{
public string LanguageCode { get; set; } = "en-GB";
public bool IsDefault { get; set; } = true;
public short IconsThemeIndexColour { get; set; } = 25;
public short LightThemeIndexColour { get; set; } = 25;
public short DarkThemeIndexColour { get; set; } = 215;
public bool PreferSystemTheming { get; set; } = true;
public bool ThemeIsDarkMode { get; set; } = false;
public bool ThemeIsDarkGray { get; set; } = false;
public bool ThemeIsLightGray { get; set; } = true;
}
public enum AvatarServer
{
Unknown,
Pleroma,
Mastodon,
Akkoma,
Misskey,
PrivaPub
}
public enum AvatarType
{
Application,
Group,
Organization,
Person,
Service
}
public enum AvatarAccountState
{
Normal,
Silenced,
Suspended,
Banned,
Deleted
}
}

View File

@ -0,0 +1,12 @@
using MongoDB.Entities;
namespace PrivaPub.Models.User
{
public class EmailRecovery : Entity
{
public string RootUserId { get; set; }
public string RecoveryCode { get; set; }
public string RequestIP { get; set; }
public DateTime CreationDate { get; set; } = DateTime.UtcNow;
}
}

View File

@ -0,0 +1,10 @@
using MongoDB.Entities;
namespace PrivaPub.Models.User
{
public class RootToAvatar : Entity
{
public string RootId { get; set; }
public string AvatarId { get; set; }
}
}

View File

@ -0,0 +1,48 @@
using MongoDB.Entities;
using System.ComponentModel.DataAnnotations;
namespace PrivaPub.Models.User
{
public class RootUser : Entity
{
[StringLength(32)]
public string UserName { get; set; }
[EmailAddress]
public string Email { get; set; }
public bool IsEmailValidated { get; set; } = false;
public bool IsBanned { get; set; } = false;
public string HashedPassword { get; set; }
public List<string> Policies { get; set; } = new() { ClientModels.Policies.IsUser };
public List<ContactItem> Contacts { get; set; } = new();
public RootUserSettings Settings { get; set; } = new();
public string ResetPasswordToken { get; set; }
public DateTime? ResetPasswordTokenSentAt { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
public DateTime? DeletedAt { get; set; }
}
public class ContactItem
{
public string Type { get; set; }
public string Contact { get; set; }
public string Note { get; set; }
public DateTime CreationDate { get; set; } = DateTime.UtcNow;
}
public class RootUserSettings
{
public string LanguageCode { get; set; } = "en-GB";
public short IconsThemeIndexColour { get; set; } = 25;
public short LightThemeIndexColour { get; set; } = 25;
public short DarkThemeIndexColour { get; set; } = 215;
public bool PreferSystemTheming { get; set; } = true;
public bool ThemeIsDarkMode { get; set; } = false;
public bool ThemeIsDarkGray { get; set; } = false;
public bool ThemeIsLightGray { get; set; } = true;
}
}

View File

@ -0,0 +1,14 @@
using MongoDB.Entities;
namespace PrivaPub.Models.User
{
public class RootUserNote : Entity
{
public string RootUserId { get; set; }
public string AvatarUserId { get; set; }
public string Note { get; set; }
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
}

View File

@ -0,0 +1,9 @@
namespace PrivaPub.Models.User
{
public enum UserPolicyType
{
IsUser,
IsModerator,
IsAdmin
}
}

View File

@ -0,0 +1,24 @@
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Entities;
namespace PrivaPub.Models.Data
{
[BsonIgnoreExtraElements]
public class PrivaPub : Entity
{
public DateTime Timestamp { get; set; }
public string Level { get; set; }
public string RenderedMessage { get; set; }
[BsonIgnoreIfDefault, BsonIgnoreIfNull]
public DbLogProperties Properties { get; set; }
[BsonIgnoreIfDefault, BsonIgnoreIfNull]
public string Exception { get; set; }
}
[BsonIgnoreExtraElements]
public class DbLogProperties
{
public string SourceContext { get; set; }
}
}