✨ Save uncategorized actor data in metadata
This commit is contained in:
@@ -11,67 +11,33 @@ public class SnFediverseActor : ModelBase
|
|||||||
{
|
{
|
||||||
public Guid Id { get; set; } = Guid.NewGuid();
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
|
|
||||||
[MaxLength(2048)]
|
[MaxLength(2048)] public string Type { get; set; } = "Person";
|
||||||
public string Uri { get; set; } = null!;
|
[MaxLength(2048)] public string Uri { get; set; } = null!;
|
||||||
|
[MaxLength(256)] public string Username { get; set; } = null!;
|
||||||
[MaxLength(256)]
|
[MaxLength(2048)] public string? DisplayName { get; set; }
|
||||||
public string Username { get; set; } = null!;
|
[MaxLength(4096)] public string? Bio { get; set; }
|
||||||
|
[MaxLength(2048)] public string? InboxUri { get; set; }
|
||||||
[MaxLength(2048)]
|
[MaxLength(2048)] public string? OutboxUri { get; set; }
|
||||||
public string? DisplayName { get; set; }
|
[MaxLength(2048)] public string? FollowersUri { get; set; }
|
||||||
|
[MaxLength(2048)] public string? FollowingUri { get; set; }
|
||||||
[MaxLength(4096)]
|
[MaxLength(2048)] public string? FeaturedUri { get; set; }
|
||||||
public string? Bio { get; set; }
|
[MaxLength(2048)] public string? PublicKeyId { get; set; }
|
||||||
|
[MaxLength(8192)] public string? PublicKey { get; set; }
|
||||||
[MaxLength(2048)]
|
[Column(TypeName = "jsonb")] public Dictionary<string, object>? Metadata { get; set; }
|
||||||
public string? InboxUri { get; set; }
|
[MaxLength(2048)] public string? AvatarUrl { get; set; }
|
||||||
|
[MaxLength(2048)] public string? HeaderUrl { get; set; }
|
||||||
[MaxLength(2048)]
|
|
||||||
public string? OutboxUri { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(2048)]
|
|
||||||
public string? FollowersUri { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(2048)]
|
|
||||||
public string? FollowingUri { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(2048)]
|
|
||||||
public string? FeaturedUri { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(2048)]
|
|
||||||
public string? PublicKeyId { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(8192)]
|
|
||||||
public string? PublicKey { get; set; }
|
|
||||||
|
|
||||||
[Column(TypeName = "jsonb")]
|
|
||||||
public Dictionary<string, object>? Metadata { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(2048)]
|
|
||||||
public string? AvatarUrl { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(2048)]
|
|
||||||
public string? HeaderUrl { get; set; }
|
|
||||||
|
|
||||||
public bool IsBot { get; set; } = false;
|
public bool IsBot { get; set; } = false;
|
||||||
public bool IsLocked { get; set; } = false;
|
public bool IsLocked { get; set; } = false;
|
||||||
public bool IsDiscoverable { get; set; } = true;
|
public bool IsDiscoverable { get; set; } = true;
|
||||||
|
|
||||||
public Guid InstanceId { get; set; }
|
public Guid InstanceId { get; set; }
|
||||||
[JsonIgnore]
|
|
||||||
public SnFediverseInstance Instance { get; set; } = null!;
|
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore] public SnFediverseInstance Instance { get; set; } = null!;
|
||||||
public ICollection<SnFediverseContent> Contents { get; set; } = [];
|
[JsonIgnore] public ICollection<SnFediverseContent> Contents { get; set; } = [];
|
||||||
|
[JsonIgnore] public ICollection<SnFediverseActivity> Activities { get; set; } = [];
|
||||||
[JsonIgnore]
|
[JsonIgnore] public ICollection<SnFediverseRelationship> FollowingRelationships { get; set; } = [];
|
||||||
public ICollection<SnFediverseActivity> Activities { get; set; } = [];
|
[JsonIgnore] public ICollection<SnFediverseRelationship> FollowerRelationships { get; set; } = [];
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public ICollection<SnFediverseRelationship> FollowingRelationships { get; set; } = [];
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public ICollection<SnFediverseRelationship> FollowerRelationships { get; set; } = [];
|
|
||||||
|
|
||||||
public Instant? LastFetchedAt { get; set; }
|
public Instant? LastFetchedAt { get; set; }
|
||||||
public Instant? LastActivityAt { get; set; }
|
public Instant? LastActivityAt { get; set; }
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using DysonNetwork.Shared.Models;
|
using DysonNetwork.Shared.Models;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
@@ -66,7 +67,8 @@ public partial class ActivityPubDiscoveryService(
|
|||||||
return !match.Success ? (null, null) : (match.Groups[1].Value, match.Groups[2].Value);
|
return !match.Success ? (null, null) : (match.Groups[1].Value, match.Groups[2].Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<(string? actorUri, string? avatarUrl)> GetActorUriFromWebfingerAsync(string username, string domain)
|
private async Task<(string? actorUri, string? avatarUrl)> GetActorUriFromWebfingerAsync(string username,
|
||||||
|
string domain)
|
||||||
{
|
{
|
||||||
if (domain == Domain)
|
if (domain == Domain)
|
||||||
return (null, null);
|
return (null, null);
|
||||||
@@ -101,7 +103,8 @@ public partial class ActivityPubDiscoveryService(
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logger.LogWarning("Unknown Content-Type from {Url}: {ContentType}, trying JSON parsing", webfingerUrl, contentType);
|
logger.LogWarning("Unknown Content-Type from {Url}: {ContentType}, trying JSON parsing", webfingerUrl,
|
||||||
|
contentType);
|
||||||
result = ParseJsonWebfingerResponse(content, webfingerUrl);
|
result = ParseJsonWebfingerResponse(content, webfingerUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +114,8 @@ public partial class ActivityPubDiscoveryService(
|
|||||||
return (null, null);
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.LogInformation("Found actor URI via Webfinger: {ActorUri}, Avatar: {AvatarUrl}", result.actorUri, result.avatarUrl);
|
logger.LogInformation("Found actor URI via Webfinger: {ActorUri}, Avatar: {AvatarUrl}", result.actorUri,
|
||||||
|
result.avatarUrl);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -206,7 +210,8 @@ public partial class ActivityPubDiscoveryService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<SnFediverseActor?> StoreActorAsync(string actorUri, string username, string domain, string? webfingerAvatarUrl)
|
private async Task<SnFediverseActor?> StoreActorAsync(string actorUri, string username, string domain,
|
||||||
|
string? webfingerAvatarUrl)
|
||||||
{
|
{
|
||||||
var existingActor = await db.FediverseActors
|
var existingActor = await db.FediverseActors
|
||||||
.FirstOrDefaultAsync(a => a.Uri == actorUri);
|
.FirstOrDefaultAsync(a => a.Uri == actorUri);
|
||||||
@@ -246,7 +251,7 @@ public partial class ActivityPubDiscoveryService(
|
|||||||
|
|
||||||
logger.LogInformation("Successfully stored actor from Webfinger: {Username}@{Domain}", username, domain);
|
logger.LogInformation("Successfully stored actor from Webfinger: {Username}@{Domain}", username, domain);
|
||||||
|
|
||||||
await FetchAdditionalActorDataAsync(actor);
|
await FetchActorDataAsync(actor);
|
||||||
|
|
||||||
return actor;
|
return actor;
|
||||||
}
|
}
|
||||||
@@ -257,19 +262,21 @@ public partial class ActivityPubDiscoveryService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task FetchAdditionalActorDataAsync(SnFediverseActor actor)
|
private async Task FetchActorDataAsync(SnFediverseActor actor)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
logger.LogInformation("Attempting to fetch additional actor data from: {ActorUri}", actor.Uri);
|
logger.LogInformation("Attempting to fetch additional actor data from: {ActorUri}", actor.Uri);
|
||||||
|
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, actor.Uri);
|
var request = new HttpRequestMessage(HttpMethod.Get, actor.Uri);
|
||||||
request.Headers.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/activity+json"));
|
request.Headers.Accept.Add(
|
||||||
|
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/activity+json"));
|
||||||
|
|
||||||
var response = await HttpClient.SendAsync(request);
|
var response = await HttpClient.SendAsync(request);
|
||||||
if (!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
logger.LogWarning("Failed to fetch actor data: {Url} - {StatusCode}, using Webfinger data only", actor.Uri, response.StatusCode);
|
logger.LogWarning("Failed to fetch actor data: {Url} - {StatusCode}, using Webfinger data only",
|
||||||
|
actor.Uri, response.StatusCode);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,6 +289,7 @@ public partial class ActivityPubDiscoveryService(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
actor.Type = actorData.GetValueOrDefault("type")?.ToString();
|
||||||
actor.DisplayName = actorData.GetValueOrDefault("name")?.ToString();
|
actor.DisplayName = actorData.GetValueOrDefault("name")?.ToString();
|
||||||
actor.Bio = actorData.GetValueOrDefault("summary")?.ToString();
|
actor.Bio = actorData.GetValueOrDefault("summary")?.ToString();
|
||||||
actor.InboxUri = actorData.GetValueOrDefault("inbox")?.ToString();
|
actor.InboxUri = actorData.GetValueOrDefault("inbox")?.ToString();
|
||||||
@@ -297,13 +305,23 @@ public partial class ActivityPubDiscoveryService(
|
|||||||
actor.IsLocked = actorData.GetValueOrDefault("manuallyApprovesFollowers")?.ToString() == "true";
|
actor.IsLocked = actorData.GetValueOrDefault("manuallyApprovesFollowers")?.ToString() == "true";
|
||||||
actor.IsDiscoverable = actorData.GetValueOrDefault("discoverable")?.ToString() != "false";
|
actor.IsDiscoverable = actorData.GetValueOrDefault("discoverable")?.ToString() != "false";
|
||||||
|
|
||||||
|
// Store additional fields in Metadata
|
||||||
|
var excludedKeys = new HashSet<string>
|
||||||
|
{
|
||||||
|
"id", "name", "summary", "preferredUsername", "inbox", "outbox", "followers", "following", "featured",
|
||||||
|
"icon", "image", "publicKey", "type", "manuallyApprovesFollowers", "discoverable", "@context"
|
||||||
|
};
|
||||||
|
actor.Metadata = actorData.Where(kvp => !excludedKeys.Contains(kvp.Key))
|
||||||
|
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||||
|
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
logger.LogInformation("Successfully fetched additional actor data for: {Username}", actor.Username);
|
logger.LogInformation("Successfully fetched additional actor data for: {Username}", actor.Username);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
logger.LogWarning(ex, "Failed to fetch additional actor data for {Uri}, using Webfinger data only", actor.Uri);
|
logger.LogWarning(ex, "Failed to fetch additional actor data for {Uri}, using Webfinger data only",
|
||||||
|
actor.Uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2663
DysonNetwork.Sphere/Migrations/20251228165042_AddSeprateActorType.Designer.cs
generated
Normal file
2663
DysonNetwork.Sphere/Migrations/20251228165042_AddSeprateActorType.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace DysonNetwork.Sphere.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddSeprateActorType : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "type",
|
||||||
|
table: "fediverse_actors",
|
||||||
|
type: "character varying(2048)",
|
||||||
|
maxLength: 2048,
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "Person");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "type",
|
||||||
|
table: "fediverse_actors");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -495,6 +495,12 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnType("character varying(2048)")
|
.HasColumnType("character varying(2048)")
|
||||||
.HasColumnName("public_key_id");
|
.HasColumnName("public_key_id");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("character varying(2048)")
|
||||||
|
.HasColumnName("type");
|
||||||
|
|
||||||
b.Property<Instant>("UpdatedAt")
|
b.Property<Instant>("UpdatedAt")
|
||||||
.HasColumnType("timestamp with time zone")
|
.HasColumnType("timestamp with time zone")
|
||||||
.HasColumnName("updated_at");
|
.HasColumnName("updated_at");
|
||||||
|
|||||||
Reference in New Issue
Block a user