[backend] Code cleanup
This commit is contained in:
parent
b7584674c7
commit
a408fa247a
62 changed files with 274 additions and 209 deletions
|
@ -55,7 +55,9 @@ public class NodeInfoController(IOptions<Config.InstanceSection> config, Databas
|
||||||
//FIXME Implement members
|
//FIXME Implement members
|
||||||
Users = new NodeInfoResponse.NodeInfoUsers
|
Users = new NodeInfoResponse.NodeInfoUsers
|
||||||
{
|
{
|
||||||
Total = totalUsers, ActiveMonth = activeMonth, ActiveHalfYear = activeHalfYear
|
Total = totalUsers,
|
||||||
|
ActiveMonth = activeMonth,
|
||||||
|
ActiveHalfYear = activeHalfYear
|
||||||
},
|
},
|
||||||
LocalComments = 0,
|
LocalComments = 0,
|
||||||
LocalPosts = localPosts
|
LocalPosts = localPosts
|
||||||
|
|
|
@ -6,19 +6,19 @@ namespace Iceshrimp.Backend.Controllers.Federation.Schemas;
|
||||||
[XmlRoot("XRD", Namespace = "http://docs.oasis-open.org/ns/xri/xrd-1.0", IsNullable = false)]
|
[XmlRoot("XRD", Namespace = "http://docs.oasis-open.org/ns/xri/xrd-1.0", IsNullable = false)]
|
||||||
public class HostMetaXmlResponse()
|
public class HostMetaXmlResponse()
|
||||||
{
|
{
|
||||||
|
[XmlElement("Link")] public required HostMetaXmlResponseLink Link;
|
||||||
|
|
||||||
[SetsRequiredMembers]
|
[SetsRequiredMembers]
|
||||||
public HostMetaXmlResponse(string webDomain) : this() => Link = new HostMetaXmlResponseLink(webDomain);
|
public HostMetaXmlResponse(string webDomain) : this() => Link = new HostMetaXmlResponseLink(webDomain);
|
||||||
|
|
||||||
[XmlElement("Link")] public required HostMetaXmlResponseLink Link;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class HostMetaXmlResponseLink()
|
public class HostMetaXmlResponseLink()
|
||||||
{
|
{
|
||||||
|
[XmlAttribute("rel")] public string Rel = "lrdd";
|
||||||
|
[XmlAttribute("template")] public required string Template;
|
||||||
|
[XmlAttribute("type")] public string Type = "application/xrd+xml";
|
||||||
|
|
||||||
[SetsRequiredMembers]
|
[SetsRequiredMembers]
|
||||||
public HostMetaXmlResponseLink(string webDomain) : this() =>
|
public HostMetaXmlResponseLink(string webDomain) : this() =>
|
||||||
Template = $"https://{webDomain}/.well-known/webfinger?resource={{uri}}";
|
Template = $"https://{webDomain}/.well-known/webfinger?resource={{uri}}";
|
||||||
|
|
||||||
[XmlAttribute("rel")] public string Rel = "lrdd";
|
|
||||||
[XmlAttribute("type")] public string Type = "application/xrd+xml";
|
|
||||||
[XmlAttribute("template")] public required string Template;
|
|
||||||
}
|
}
|
|
@ -59,7 +59,9 @@ public class WellKnownController(IOptions<Config.InstanceSection> config, Databa
|
||||||
[
|
[
|
||||||
new WebFingerLink
|
new WebFingerLink
|
||||||
{
|
{
|
||||||
Rel = "self", Type = "application/activity+json", Href = user.GetPublicUri(config.Value)
|
Rel = "self",
|
||||||
|
Type = "application/activity+json",
|
||||||
|
Href = user.GetPublicUri(config.Value)
|
||||||
},
|
},
|
||||||
new WebFingerLink
|
new WebFingerLink
|
||||||
{
|
{
|
||||||
|
|
|
@ -76,7 +76,12 @@ public class AccountController(
|
||||||
if (request.Fields?.Where(p => p is { Name: not null, Value: not null }).ToList() is { Count: > 0 } fields)
|
if (request.Fields?.Where(p => p is { Name: not null, Value: not null }).ToList() is { Count: > 0 } fields)
|
||||||
{
|
{
|
||||||
user.UserProfile.Fields =
|
user.UserProfile.Fields =
|
||||||
fields.Select(p => new UserProfile.Field { Name = p.Name!, Value = p.Value!, IsVerified = false })
|
fields.Select(p => new UserProfile.Field
|
||||||
|
{
|
||||||
|
Name = p.Name!,
|
||||||
|
Value = p.Value!,
|
||||||
|
IsVerified = false
|
||||||
|
})
|
||||||
.ToArray();
|
.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +92,9 @@ public class AccountController(
|
||||||
{
|
{
|
||||||
var rq = new DriveFileCreationRequest
|
var rq = new DriveFileCreationRequest
|
||||||
{
|
{
|
||||||
Filename = request.Avatar.FileName, IsSensitive = false, MimeType = request.Avatar.ContentType
|
Filename = request.Avatar.FileName,
|
||||||
|
IsSensitive = false,
|
||||||
|
MimeType = request.Avatar.ContentType
|
||||||
};
|
};
|
||||||
var avatar = await driveSvc.StoreFile(request.Avatar.OpenReadStream(), user, rq);
|
var avatar = await driveSvc.StoreFile(request.Avatar.OpenReadStream(), user, rq);
|
||||||
user.Avatar = avatar;
|
user.Avatar = avatar;
|
||||||
|
@ -99,7 +106,9 @@ public class AccountController(
|
||||||
{
|
{
|
||||||
var rq = new DriveFileCreationRequest
|
var rq = new DriveFileCreationRequest
|
||||||
{
|
{
|
||||||
Filename = request.Banner.FileName, IsSensitive = false, MimeType = request.Banner.ContentType
|
Filename = request.Banner.FileName,
|
||||||
|
IsSensitive = false,
|
||||||
|
MimeType = request.Banner.ContentType
|
||||||
};
|
};
|
||||||
var banner = await driveSvc.StoreFile(request.Banner.OpenReadStream(), user, rq);
|
var banner = await driveSvc.StoreFile(request.Banner.OpenReadStream(), user, rq);
|
||||||
user.Banner = banner;
|
user.Banner = banner;
|
||||||
|
@ -282,11 +291,11 @@ public class AccountController(
|
||||||
throw GracefulException.BadRequest("You cannot block yourself");
|
throw GracefulException.BadRequest("You cannot block yourself");
|
||||||
|
|
||||||
var blockee = await db.Users
|
var blockee = await db.Users
|
||||||
.Where(p => p.Id == id)
|
.Where(p => p.Id == id)
|
||||||
.IncludeCommonProperties()
|
.IncludeCommonProperties()
|
||||||
.PrecomputeRelationshipData(user)
|
.PrecomputeRelationshipData(user)
|
||||||
.FirstOrDefaultAsync() ??
|
.FirstOrDefaultAsync() ??
|
||||||
throw GracefulException.RecordNotFound();
|
throw GracefulException.RecordNotFound();
|
||||||
|
|
||||||
await userSvc.BlockUserAsync(user, blockee);
|
await userSvc.BlockUserAsync(user, blockee);
|
||||||
|
|
||||||
|
@ -305,11 +314,11 @@ public class AccountController(
|
||||||
throw GracefulException.BadRequest("You cannot unblock yourself");
|
throw GracefulException.BadRequest("You cannot unblock yourself");
|
||||||
|
|
||||||
var blockee = await db.Users
|
var blockee = await db.Users
|
||||||
.Where(p => p.Id == id)
|
.Where(p => p.Id == id)
|
||||||
.IncludeCommonProperties()
|
.IncludeCommonProperties()
|
||||||
.PrecomputeRelationshipData(user)
|
.PrecomputeRelationshipData(user)
|
||||||
.FirstOrDefaultAsync() ??
|
.FirstOrDefaultAsync() ??
|
||||||
throw GracefulException.RecordNotFound();
|
throw GracefulException.RecordNotFound();
|
||||||
|
|
||||||
await userSvc.UnblockUserAsync(user, blockee);
|
await userSvc.UnblockUserAsync(user, blockee);
|
||||||
|
|
||||||
|
|
|
@ -145,9 +145,9 @@ public class ConversationsController(
|
||||||
|
|
||||||
private class Conversation
|
private class Conversation
|
||||||
{
|
{
|
||||||
public required string Id { get; init; }
|
|
||||||
public required Note LastNote;
|
public required Note LastNote;
|
||||||
public required List<User> Users;
|
|
||||||
public required bool Unread;
|
public required bool Unread;
|
||||||
|
public required List<User> Users;
|
||||||
|
public required string Id { get; init; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -34,7 +34,12 @@ public class ListController(DatabaseContext db, UserRenderer userRenderer) : Con
|
||||||
|
|
||||||
var res = await db.UserLists
|
var res = await db.UserLists
|
||||||
.Where(p => p.User == user)
|
.Where(p => p.User == user)
|
||||||
.Select(p => new ListEntity { Id = p.Id, Title = p.Name, Exclusive = p.HideFromHomeTl })
|
.Select(p => new ListEntity
|
||||||
|
{
|
||||||
|
Id = p.Id,
|
||||||
|
Title = p.Name,
|
||||||
|
Exclusive = p.HideFromHomeTl
|
||||||
|
})
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
return Ok(res);
|
return Ok(res);
|
||||||
|
@ -50,7 +55,12 @@ public class ListController(DatabaseContext db, UserRenderer userRenderer) : Con
|
||||||
|
|
||||||
var res = await db.UserLists
|
var res = await db.UserLists
|
||||||
.Where(p => p.User == user && p.Id == id)
|
.Where(p => p.User == user && p.Id == id)
|
||||||
.Select(p => new ListEntity { Id = p.Id, Title = p.Name, Exclusive = p.HideFromHomeTl })
|
.Select(p => new ListEntity
|
||||||
|
{
|
||||||
|
Id = p.Id,
|
||||||
|
Title = p.Name,
|
||||||
|
Exclusive = p.HideFromHomeTl
|
||||||
|
})
|
||||||
.FirstOrDefaultAsync() ??
|
.FirstOrDefaultAsync() ??
|
||||||
throw GracefulException.RecordNotFound();
|
throw GracefulException.RecordNotFound();
|
||||||
|
|
||||||
|
@ -79,7 +89,12 @@ public class ListController(DatabaseContext db, UserRenderer userRenderer) : Con
|
||||||
await db.AddAsync(list);
|
await db.AddAsync(list);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
var res = new ListEntity { Id = list.Id, Title = list.Name, Exclusive = list.HideFromHomeTl };
|
var res = new ListEntity
|
||||||
|
{
|
||||||
|
Id = list.Id,
|
||||||
|
Title = list.Name,
|
||||||
|
Exclusive = list.HideFromHomeTl
|
||||||
|
};
|
||||||
return Ok(res);
|
return Ok(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +120,12 @@ public class ListController(DatabaseContext db, UserRenderer userRenderer) : Con
|
||||||
db.Update(list);
|
db.Update(list);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
var res = new ListEntity { Id = list.Id, Title = list.Name, Exclusive = list.HideFromHomeTl };
|
var res = new ListEntity
|
||||||
|
{
|
||||||
|
Id = list.Id,
|
||||||
|
Title = list.Name,
|
||||||
|
Exclusive = list.HideFromHomeTl
|
||||||
|
};
|
||||||
return Ok(res);
|
return Ok(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -165,7 +165,7 @@ public class PushController(DatabaseContext db, MetaService meta) : ControllerBa
|
||||||
Status = sub.Types.Contains("status"),
|
Status = sub.Types.Contains("status"),
|
||||||
Update = sub.Types.Contains("update"),
|
Update = sub.Types.Contains("update"),
|
||||||
FollowRequest = sub.Types.Contains("follow_request")
|
FollowRequest = sub.Types.Contains("follow_request")
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -273,15 +273,15 @@ public class NoteRenderer(
|
||||||
public class NoteRendererDto
|
public class NoteRendererDto
|
||||||
{
|
{
|
||||||
public List<AccountEntity>? Accounts;
|
public List<AccountEntity>? Accounts;
|
||||||
public List<MentionEntity>? Mentions;
|
|
||||||
public List<AttachmentEntity>? Attachments;
|
public List<AttachmentEntity>? Attachments;
|
||||||
public List<PollEntity>? Polls;
|
|
||||||
public List<string>? LikedNotes;
|
|
||||||
public List<string>? BookmarkedNotes;
|
public List<string>? BookmarkedNotes;
|
||||||
public List<string>? PinnedNotes;
|
|
||||||
public List<string>? Renotes;
|
|
||||||
public List<EmojiEntity>? Emoji;
|
public List<EmojiEntity>? Emoji;
|
||||||
|
public List<string>? LikedNotes;
|
||||||
|
public List<MentionEntity>? Mentions;
|
||||||
|
public List<string>? PinnedNotes;
|
||||||
|
public List<PollEntity>? Polls;
|
||||||
public List<ReactionEntity>? Reactions;
|
public List<ReactionEntity>? Reactions;
|
||||||
|
public List<string>? Renotes;
|
||||||
|
|
||||||
public bool Source;
|
public bool Source;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,10 @@ namespace Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
|
||||||
|
|
||||||
public class AnnouncementEntity
|
public class AnnouncementEntity
|
||||||
{
|
{
|
||||||
|
[J("reactions")] public List<object> Reactions = []; //FIXME
|
||||||
|
|
||||||
|
[J("statuses")] public List<object> Statuses = []; //FIXME
|
||||||
|
[J("tags")] public List<object> Tags = []; //FIXME
|
||||||
[J("id")] public required string Id { get; set; }
|
[J("id")] public required string Id { get; set; }
|
||||||
[J("content")] public required string Content { get; set; }
|
[J("content")] public required string Content { get; set; }
|
||||||
[J("published_at")] public required string PublishedAt { get; set; }
|
[J("published_at")] public required string PublishedAt { get; set; }
|
||||||
|
@ -12,10 +16,6 @@ public class AnnouncementEntity
|
||||||
[J("mentions")] public required List<MentionEntity> Mentions { get; set; }
|
[J("mentions")] public required List<MentionEntity> Mentions { get; set; }
|
||||||
[J("emojis")] public required List<EmojiEntity> Emoji { get; set; }
|
[J("emojis")] public required List<EmojiEntity> Emoji { get; set; }
|
||||||
|
|
||||||
[J("statuses")] public List<object> Statuses = []; //FIXME
|
|
||||||
[J("reactions")] public List<object> Reactions = []; //FIXME
|
|
||||||
[J("tags")] public List<object> Tags = []; //FIXME
|
|
||||||
|
|
||||||
[J("published")] public bool Published => true;
|
[J("published")] public bool Published => true;
|
||||||
[J("all_day")] public bool AllDay => false;
|
[J("all_day")] public bool AllDay => false;
|
||||||
}
|
}
|
|
@ -5,8 +5,8 @@ namespace Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
|
||||||
|
|
||||||
public class ConversationEntity : IEntity
|
public class ConversationEntity : IEntity
|
||||||
{
|
{
|
||||||
[J("id")] public required string Id { get; set; }
|
|
||||||
[J("unread")] public required bool Unread { get; set; }
|
[J("unread")] public required bool Unread { get; set; }
|
||||||
[J("accounts")] public required List<AccountEntity> Accounts { get; set; }
|
[J("accounts")] public required List<AccountEntity> Accounts { get; set; }
|
||||||
[J("last_status")] public required StatusEntity LastStatus { get; set; }
|
[J("last_status")] public required StatusEntity LastStatus { get; set; }
|
||||||
|
[J("id")] public required string Id { get; set; }
|
||||||
}
|
}
|
|
@ -5,7 +5,6 @@ namespace Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
|
||||||
|
|
||||||
public class PollEntity : IEntity
|
public class PollEntity : IEntity
|
||||||
{
|
{
|
||||||
[J("id")] public required string Id { get; set; }
|
|
||||||
[J("expires_at")] public required string? ExpiresAt { get; set; }
|
[J("expires_at")] public required string? ExpiresAt { get; set; }
|
||||||
[J("expired")] public required bool Expired { get; set; }
|
[J("expired")] public required bool Expired { get; set; }
|
||||||
[J("multiple")] public required bool Multiple { get; set; }
|
[J("multiple")] public required bool Multiple { get; set; }
|
||||||
|
@ -16,6 +15,7 @@ public class PollEntity : IEntity
|
||||||
|
|
||||||
[J("options")] public required List<PollOptionEntity> Options { get; set; }
|
[J("options")] public required List<PollOptionEntity> Options { get; set; }
|
||||||
[J("emojis")] public List<EmojiEntity> Emoji => []; //TODO
|
[J("emojis")] public List<EmojiEntity> Emoji => []; //TODO
|
||||||
|
[J("id")] public required string Id { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PollOptionEntity
|
public class PollOptionEntity
|
||||||
|
|
|
@ -9,7 +9,6 @@ namespace Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
|
||||||
|
|
||||||
public class StatusEntity : IEntity
|
public class StatusEntity : IEntity
|
||||||
{
|
{
|
||||||
[J("id")] public required string Id { get; set; }
|
|
||||||
[J("content")] public required string? Content { get; set; }
|
[J("content")] public required string? Content { get; set; }
|
||||||
[J("uri")] public required string Uri { get; set; }
|
[J("uri")] public required string Uri { get; set; }
|
||||||
[J("url")] public required string Url { get; set; }
|
[J("url")] public required string Url { get; set; }
|
||||||
|
@ -52,7 +51,8 @@ public class StatusEntity : IEntity
|
||||||
[J("card")] public object? Card => null; //FIXME
|
[J("card")] public object? Card => null; //FIXME
|
||||||
[J("application")] public object? Application => null; //FIXME
|
[J("application")] public object? Application => null; //FIXME
|
||||||
|
|
||||||
[J("language")] public string? Language => null; //FIXME
|
[J("language")] public string? Language => null; //FIXME
|
||||||
|
[J("id")] public required string Id { get; set; }
|
||||||
|
|
||||||
public static string EncodeVisibility(Note.NoteVisibility visibility)
|
public static string EncodeVisibility(Note.NoteVisibility visibility)
|
||||||
{
|
{
|
||||||
|
|
|
@ -209,7 +209,7 @@ public class SearchController(
|
||||||
{
|
{
|
||||||
Name = p.Name,
|
Name = p.Name,
|
||||||
Url = $"https://{config.Value.WebDomain}/tags/{p.Name}",
|
Url = $"https://{config.Value.WebDomain}/tags/{p.Name}",
|
||||||
Following = false, //TODO
|
Following = false //TODO
|
||||||
})
|
})
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
|
@ -324,7 +324,7 @@ public class StatusController(
|
||||||
{
|
{
|
||||||
Choices = request.Poll.Options,
|
Choices = request.Poll.Options,
|
||||||
Multiple = request.Poll.Multiple,
|
Multiple = request.Poll.Multiple,
|
||||||
ExpiresAt = DateTime.UtcNow + TimeSpan.FromSeconds(request.Poll.ExpiresIn),
|
ExpiresAt = DateTime.UtcNow + TimeSpan.FromSeconds(request.Poll.ExpiresIn)
|
||||||
}
|
}
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
@ -465,7 +465,12 @@ public class StatusController(
|
||||||
{
|
{
|
||||||
var user = HttpContext.GetUserOrFail();
|
var user = HttpContext.GetUserOrFail();
|
||||||
var res = await db.Notes.Where(p => p.Id == id && p.User == user)
|
var res = await db.Notes.Where(p => p.Id == id && p.User == user)
|
||||||
.Select(p => new StatusSource { Id = p.Id, ContentWarning = p.Cw ?? "", Text = p.Text ?? "" })
|
.Select(p => new StatusSource
|
||||||
|
{
|
||||||
|
Id = p.Id,
|
||||||
|
ContentWarning = p.Cw ?? "",
|
||||||
|
Text = p.Text ?? ""
|
||||||
|
})
|
||||||
.FirstOrDefaultAsync() ??
|
.FirstOrDefaultAsync() ??
|
||||||
throw GracefulException.RecordNotFound();
|
throw GracefulException.RecordNotFound();
|
||||||
|
|
||||||
|
|
|
@ -12,13 +12,13 @@ public class PublicChannel(
|
||||||
bool onlyMedia
|
bool onlyMedia
|
||||||
) : IChannel
|
) : IChannel
|
||||||
{
|
{
|
||||||
|
public readonly ILogger<PublicChannel> Logger =
|
||||||
|
connection.ScopeFactory.CreateScope().ServiceProvider.GetRequiredService<ILogger<PublicChannel>>();
|
||||||
|
|
||||||
public string Name => name;
|
public string Name => name;
|
||||||
public List<string> Scopes => ["read:statuses"];
|
public List<string> Scopes => ["read:statuses"];
|
||||||
public bool IsSubscribed { get; private set; }
|
public bool IsSubscribed { get; private set; }
|
||||||
|
|
||||||
public readonly ILogger<PublicChannel> Logger =
|
|
||||||
connection.ScopeFactory.CreateScope().ServiceProvider.GetRequiredService<ILogger<PublicChannel>>();
|
|
||||||
|
|
||||||
public Task Subscribe(StreamingRequestMessage _)
|
public Task Subscribe(StreamingRequestMessage _)
|
||||||
{
|
{
|
||||||
if (IsSubscribed) return Task.CompletedTask;
|
if (IsSubscribed) return Task.CompletedTask;
|
||||||
|
@ -65,7 +65,9 @@ public class PublicChannel(
|
||||||
var rendered = await renderer.RenderAsync(note, connection.Token.User);
|
var rendered = await renderer.RenderAsync(note, connection.Token.User);
|
||||||
var message = new StreamingUpdateMessage
|
var message = new StreamingUpdateMessage
|
||||||
{
|
{
|
||||||
Stream = [Name], Event = "update", Payload = JsonSerializer.Serialize(rendered)
|
Stream = [Name],
|
||||||
|
Event = "update",
|
||||||
|
Payload = JsonSerializer.Serialize(rendered)
|
||||||
};
|
};
|
||||||
await connection.SendMessageAsync(JsonSerializer.Serialize(message));
|
await connection.SendMessageAsync(JsonSerializer.Serialize(message));
|
||||||
}
|
}
|
||||||
|
@ -85,7 +87,9 @@ public class PublicChannel(
|
||||||
var rendered = await renderer.RenderAsync(note, connection.Token.User);
|
var rendered = await renderer.RenderAsync(note, connection.Token.User);
|
||||||
var message = new StreamingUpdateMessage
|
var message = new StreamingUpdateMessage
|
||||||
{
|
{
|
||||||
Stream = [Name], Event = "status.update", Payload = JsonSerializer.Serialize(rendered)
|
Stream = [Name],
|
||||||
|
Event = "status.update",
|
||||||
|
Payload = JsonSerializer.Serialize(rendered)
|
||||||
};
|
};
|
||||||
await connection.SendMessageAsync(JsonSerializer.Serialize(message));
|
await connection.SendMessageAsync(JsonSerializer.Serialize(message));
|
||||||
}
|
}
|
||||||
|
@ -100,7 +104,12 @@ public class PublicChannel(
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!IsApplicable(note)) return;
|
if (!IsApplicable(note)) return;
|
||||||
var message = new StreamingUpdateMessage { Stream = [Name], Event = "delete", Payload = note.Id };
|
var message = new StreamingUpdateMessage
|
||||||
|
{
|
||||||
|
Stream = [Name],
|
||||||
|
Event = "delete",
|
||||||
|
Payload = note.Id
|
||||||
|
};
|
||||||
await connection.SendMessageAsync(JsonSerializer.Serialize(message));
|
await connection.SendMessageAsync(JsonSerializer.Serialize(message));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
|
|
@ -10,14 +10,14 @@ namespace Iceshrimp.Backend.Controllers.Mastodon.Streaming.Channels;
|
||||||
|
|
||||||
public class UserChannel(WebSocketConnection connection, bool notificationsOnly) : IChannel
|
public class UserChannel(WebSocketConnection connection, bool notificationsOnly) : IChannel
|
||||||
{
|
{
|
||||||
|
public readonly ILogger<UserChannel> Logger =
|
||||||
|
connection.ScopeFactory.CreateScope().ServiceProvider.GetRequiredService<ILogger<UserChannel>>();
|
||||||
|
|
||||||
private List<string> _followedUsers = [];
|
private List<string> _followedUsers = [];
|
||||||
public string Name => notificationsOnly ? "user:notification" : "user";
|
public string Name => notificationsOnly ? "user:notification" : "user";
|
||||||
public List<string> Scopes => ["read:statuses", "read:notifications"];
|
public List<string> Scopes => ["read:statuses", "read:notifications"];
|
||||||
public bool IsSubscribed { get; private set; }
|
public bool IsSubscribed { get; private set; }
|
||||||
|
|
||||||
public readonly ILogger<UserChannel> Logger =
|
|
||||||
connection.ScopeFactory.CreateScope().ServiceProvider.GetRequiredService<ILogger<UserChannel>>();
|
|
||||||
|
|
||||||
public async Task Subscribe(StreamingRequestMessage _)
|
public async Task Subscribe(StreamingRequestMessage _)
|
||||||
{
|
{
|
||||||
if (IsSubscribed) return;
|
if (IsSubscribed) return;
|
||||||
|
@ -74,7 +74,9 @@ public class UserChannel(WebSocketConnection connection, bool notificationsOnly)
|
||||||
var rendered = await renderer.RenderAsync(note, connection.Token.User);
|
var rendered = await renderer.RenderAsync(note, connection.Token.User);
|
||||||
var message = new StreamingUpdateMessage
|
var message = new StreamingUpdateMessage
|
||||||
{
|
{
|
||||||
Stream = [Name], Event = "update", Payload = JsonSerializer.Serialize(rendered)
|
Stream = [Name],
|
||||||
|
Event = "update",
|
||||||
|
Payload = JsonSerializer.Serialize(rendered)
|
||||||
};
|
};
|
||||||
await connection.SendMessageAsync(JsonSerializer.Serialize(message));
|
await connection.SendMessageAsync(JsonSerializer.Serialize(message));
|
||||||
}
|
}
|
||||||
|
@ -94,7 +96,9 @@ public class UserChannel(WebSocketConnection connection, bool notificationsOnly)
|
||||||
var rendered = await renderer.RenderAsync(note, connection.Token.User);
|
var rendered = await renderer.RenderAsync(note, connection.Token.User);
|
||||||
var message = new StreamingUpdateMessage
|
var message = new StreamingUpdateMessage
|
||||||
{
|
{
|
||||||
Stream = [Name], Event = "status.update", Payload = JsonSerializer.Serialize(rendered)
|
Stream = [Name],
|
||||||
|
Event = "status.update",
|
||||||
|
Payload = JsonSerializer.Serialize(rendered)
|
||||||
};
|
};
|
||||||
await connection.SendMessageAsync(JsonSerializer.Serialize(message));
|
await connection.SendMessageAsync(JsonSerializer.Serialize(message));
|
||||||
}
|
}
|
||||||
|
@ -109,7 +113,12 @@ public class UserChannel(WebSocketConnection connection, bool notificationsOnly)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!IsApplicable(note)) return;
|
if (!IsApplicable(note)) return;
|
||||||
var message = new StreamingUpdateMessage { Stream = [Name], Event = "delete", Payload = note.Id };
|
var message = new StreamingUpdateMessage
|
||||||
|
{
|
||||||
|
Stream = [Name],
|
||||||
|
Event = "delete",
|
||||||
|
Payload = note.Id
|
||||||
|
};
|
||||||
await connection.SendMessageAsync(JsonSerializer.Serialize(message));
|
await connection.SendMessageAsync(JsonSerializer.Serialize(message));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -139,7 +148,9 @@ public class UserChannel(WebSocketConnection connection, bool notificationsOnly)
|
||||||
|
|
||||||
var message = new StreamingUpdateMessage
|
var message = new StreamingUpdateMessage
|
||||||
{
|
{
|
||||||
Stream = [Name], Event = "notification", Payload = JsonSerializer.Serialize(rendered)
|
Stream = [Name],
|
||||||
|
Event = "notification",
|
||||||
|
Payload = JsonSerializer.Serialize(rendered)
|
||||||
};
|
};
|
||||||
await connection.SendMessageAsync(JsonSerializer.Serialize(message));
|
await connection.SendMessageAsync(JsonSerializer.Serialize(message));
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,8 @@ using Iceshrimp.Backend.Controllers.Attributes;
|
||||||
using Iceshrimp.Backend.Controllers.Renderers;
|
using Iceshrimp.Backend.Controllers.Renderers;
|
||||||
using Iceshrimp.Backend.Controllers.Schemas;
|
using Iceshrimp.Backend.Controllers.Schemas;
|
||||||
using Iceshrimp.Backend.Core.Database;
|
using Iceshrimp.Backend.Core.Database;
|
||||||
using Iceshrimp.Backend.Core.Database.Tables;
|
|
||||||
using Iceshrimp.Backend.Core.Extensions;
|
using Iceshrimp.Backend.Core.Extensions;
|
||||||
using Iceshrimp.Backend.Core.Middleware;
|
using Iceshrimp.Backend.Core.Middleware;
|
||||||
using Iceshrimp.Backend.Core.Services;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.RateLimiting;
|
using Microsoft.AspNetCore.RateLimiting;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
@ -14,7 +12,8 @@ using Microsoft.EntityFrameworkCore;
|
||||||
namespace Iceshrimp.Backend.Controllers;
|
namespace Iceshrimp.Backend.Controllers;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Authenticate, Authorize]
|
[Authenticate]
|
||||||
|
[Authorize]
|
||||||
[EnableRateLimiting("sliding")]
|
[EnableRateLimiting("sliding")]
|
||||||
[Route("/api/iceshrimp/v1/notification")]
|
[Route("/api/iceshrimp/v1/notification")]
|
||||||
[Produces(MediaTypeNames.Application.Json)]
|
[Produces(MediaTypeNames.Application.Json)]
|
||||||
|
|
|
@ -73,7 +73,7 @@ public class NoteRenderer(UserRenderer userRenderer, DatabaseContext db, EmojiSe
|
||||||
i.Reaction == p.First().Reaction &&
|
i.Reaction == p.First().Reaction &&
|
||||||
i.User == user),
|
i.User == user),
|
||||||
Name = p.First().Reaction,
|
Name = p.First().Reaction,
|
||||||
Url = null,
|
Url = null
|
||||||
})
|
})
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
|
@ -102,8 +102,8 @@ public class NoteRenderer(UserRenderer userRenderer, DatabaseContext db, EmojiSe
|
||||||
|
|
||||||
public class NoteRendererDto
|
public class NoteRendererDto
|
||||||
{
|
{
|
||||||
public List<UserResponse>? Users;
|
|
||||||
public List<NoteAttachment>? Attachments;
|
public List<NoteAttachment>? Attachments;
|
||||||
public List<NoteReactionSchema>? Reactions;
|
public List<NoteReactionSchema>? Reactions;
|
||||||
|
public List<UserResponse>? Users;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,6 @@
|
||||||
using Iceshrimp.Backend.Controllers.Schemas;
|
using Iceshrimp.Backend.Controllers.Schemas;
|
||||||
using Iceshrimp.Backend.Core.Database;
|
|
||||||
using Iceshrimp.Backend.Core.Database.Tables;
|
using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
using Iceshrimp.Backend.Core.Extensions;
|
using Iceshrimp.Backend.Core.Extensions;
|
||||||
using Iceshrimp.Backend.Core.Services;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace Iceshrimp.Backend.Controllers.Renderers;
|
namespace Iceshrimp.Backend.Controllers.Renderers;
|
||||||
|
|
||||||
|
@ -81,7 +78,7 @@ public class NotificationRenderer(
|
||||||
|
|
||||||
public class NotificationRendererDto
|
public class NotificationRendererDto
|
||||||
{
|
{
|
||||||
public List<UserResponse>? Users;
|
|
||||||
public List<NoteResponse>? Notes;
|
public List<NoteResponse>? Notes;
|
||||||
|
public List<UserResponse>? Users;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -36,7 +36,7 @@ public class UserProfileRenderer(DatabaseContext db)
|
||||||
Fields = user.UserProfile?.Fields,
|
Fields = user.UserProfile?.Fields,
|
||||||
Location = user.UserProfile?.Location,
|
Location = user.UserProfile?.Location,
|
||||||
Followers = followers,
|
Followers = followers,
|
||||||
Following = following,
|
Following = following
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using J = System.Text.Json.Serialization.JsonPropertyNameAttribute;
|
using J = System.Text.Json.Serialization.JsonPropertyNameAttribute;
|
||||||
using JI = System.Text.Json.Serialization.JsonIgnoreAttribute;
|
|
||||||
|
|
||||||
namespace Iceshrimp.Backend.Controllers.Schemas;
|
namespace Iceshrimp.Backend.Controllers.Schemas;
|
||||||
|
|
||||||
|
|
|
@ -84,8 +84,8 @@ public class DatabaseContext(DbContextOptions<DatabaseContext> options)
|
||||||
public virtual DbSet<Webhook> Webhooks { get; init; } = null!;
|
public virtual DbSet<Webhook> Webhooks { get; init; } = null!;
|
||||||
public virtual DbSet<AllowedInstance> AllowedInstances { get; init; } = null!;
|
public virtual DbSet<AllowedInstance> AllowedInstances { get; init; } = null!;
|
||||||
public virtual DbSet<BlockedInstance> BlockedInstances { get; init; } = null!;
|
public virtual DbSet<BlockedInstance> BlockedInstances { get; init; } = null!;
|
||||||
public virtual DbSet<DataProtectionKey> DataProtectionKeys { get; init; } = null!;
|
|
||||||
public virtual DbSet<MetaStoreEntry> MetaStore { get; init; } = null!;
|
public virtual DbSet<MetaStoreEntry> MetaStore { get; init; } = null!;
|
||||||
|
public virtual DbSet<DataProtectionKey> DataProtectionKeys { get; init; } = null!;
|
||||||
|
|
||||||
public static NpgsqlDataSource GetDataSource(Config.DatabaseSection? config)
|
public static NpgsqlDataSource GetDataSource(Config.DatabaseSection? config)
|
||||||
{
|
{
|
||||||
|
|
|
@ -184,14 +184,14 @@ public class DriveFile : IEntity
|
||||||
[InverseProperty(nameof(Tables.User.Banner))]
|
[InverseProperty(nameof(Tables.User.Banner))]
|
||||||
public virtual User? UserBanner { get; set; }
|
public virtual User? UserBanner { get; set; }
|
||||||
|
|
||||||
|
[NotMapped] public string PublicUrl => WebpublicUrl ?? Url;
|
||||||
|
[NotMapped] public string PublicThumbnailUrl => ThumbnailUrl ?? WebpublicUrl ?? Url;
|
||||||
|
|
||||||
[Key]
|
[Key]
|
||||||
[Column("id")]
|
[Column("id")]
|
||||||
[StringLength(32)]
|
[StringLength(32)]
|
||||||
public string Id { get; set; } = null!;
|
public string Id { get; set; } = null!;
|
||||||
|
|
||||||
[NotMapped] public string PublicUrl => WebpublicUrl ?? Url;
|
|
||||||
[NotMapped] public string PublicThumbnailUrl => ThumbnailUrl ?? WebpublicUrl ?? Url;
|
|
||||||
|
|
||||||
public class FileProperties
|
public class FileProperties
|
||||||
{
|
{
|
||||||
[J("width")] public int? Width { get; set; }
|
[J("width")] public int? Width { get; set; }
|
||||||
|
|
|
@ -8,10 +8,10 @@ namespace Iceshrimp.Backend.Core.Database.Tables;
|
||||||
[Index("Name", IsUnique = true)]
|
[Index("Name", IsUnique = true)]
|
||||||
public class Hashtag : IEntity
|
public class Hashtag : IEntity
|
||||||
{
|
{
|
||||||
|
[Column("name")] [StringLength(128)] public string Name { get; set; } = null!;
|
||||||
|
|
||||||
[Key]
|
[Key]
|
||||||
[Column("id")]
|
[Column("id")]
|
||||||
[StringLength(32)]
|
[StringLength(32)]
|
||||||
public string Id { get; set; } = null!;
|
public string Id { get; set; } = null!;
|
||||||
|
|
||||||
[Column("name")] [StringLength(128)] public string Name { get; set; } = null!;
|
|
||||||
}
|
}
|
|
@ -11,13 +11,16 @@ namespace Iceshrimp.Backend.Core.Database.Tables;
|
||||||
[PrimaryKey("UserId", "Type")]
|
[PrimaryKey("UserId", "Type")]
|
||||||
public class Marker
|
public class Marker
|
||||||
{
|
{
|
||||||
[Column("userId")]
|
[PgName("marker_type_enum")]
|
||||||
[StringLength(32)]
|
public enum MarkerType
|
||||||
public string UserId { get; set; } = null!;
|
{
|
||||||
|
[PgName("home")] Home,
|
||||||
|
[PgName("notifications")] Notifications
|
||||||
|
}
|
||||||
|
|
||||||
[Column("type")]
|
[Column("userId")] [StringLength(32)] public string UserId { get; set; } = null!;
|
||||||
[StringLength(32)]
|
|
||||||
public MarkerType Type { get; set; }
|
[Column("type")] [StringLength(32)] public MarkerType Type { get; set; }
|
||||||
|
|
||||||
[Column("position")]
|
[Column("position")]
|
||||||
[StringLength(32)]
|
[StringLength(32)]
|
||||||
|
@ -30,11 +33,4 @@ public class Marker
|
||||||
[ForeignKey("UserId")]
|
[ForeignKey("UserId")]
|
||||||
[InverseProperty(nameof(Tables.User.Markers))]
|
[InverseProperty(nameof(Tables.User.Markers))]
|
||||||
public virtual User User { get; set; } = null!;
|
public virtual User User { get; set; } = null!;
|
||||||
|
|
||||||
[PgName("marker_type_enum")]
|
|
||||||
public enum MarkerType
|
|
||||||
{
|
|
||||||
[PgName("home")] Home,
|
|
||||||
[PgName("notifications")] Notifications
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -241,9 +241,6 @@ public class Note : IEntity
|
||||||
public string RawAttachments
|
public string RawAttachments
|
||||||
=> InternalRawAttachments(Id);
|
=> InternalRawAttachments(Id);
|
||||||
|
|
||||||
public static string InternalRawAttachments(string id)
|
|
||||||
=> throw new NotSupportedException();
|
|
||||||
|
|
||||||
[NotMapped] [Projectable] public bool IsPureRenote => (RenoteId != null || Renote != null) && !IsQuote;
|
[NotMapped] [Projectable] public bool IsPureRenote => (RenoteId != null || Renote != null) && !IsQuote;
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
|
@ -265,6 +262,9 @@ public class Note : IEntity
|
||||||
[StringLength(32)]
|
[StringLength(32)]
|
||||||
public string Id { get; set; } = null!;
|
public string Id { get; set; } = null!;
|
||||||
|
|
||||||
|
public static string InternalRawAttachments(string id)
|
||||||
|
=> throw new NotSupportedException();
|
||||||
|
|
||||||
[Projectable]
|
[Projectable]
|
||||||
public bool TextContainsCaseInsensitive(string str) =>
|
public bool TextContainsCaseInsensitive(string str) =>
|
||||||
Text != null && EF.Functions.ILike(Text, "%" + EfHelpers.EscapeLikeQuery(str) + "%", @"\");
|
Text != null && EF.Functions.ILike(Text, "%" + EfHelpers.EscapeLikeQuery(str) + "%", @"\");
|
||||||
|
|
|
@ -117,15 +117,15 @@ public class Notification : IEntity
|
||||||
[InverseProperty(nameof(Tables.UserGroupInvitation.Notifications))]
|
[InverseProperty(nameof(Tables.UserGroupInvitation.Notifications))]
|
||||||
public virtual UserGroupInvitation? UserGroupInvitation { get; set; }
|
public virtual UserGroupInvitation? UserGroupInvitation { get; set; }
|
||||||
|
|
||||||
|
[Column("masto_id")]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public long MastoId { get; set; }
|
||||||
|
|
||||||
[Key]
|
[Key]
|
||||||
[Column("id")]
|
[Column("id")]
|
||||||
[StringLength(32)]
|
[StringLength(32)]
|
||||||
public string Id { get; set; } = null!;
|
public string Id { get; set; } = null!;
|
||||||
|
|
||||||
[Column("masto_id")]
|
|
||||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
|
||||||
public long MastoId { get; set; }
|
|
||||||
|
|
||||||
public Notification WithPrecomputedNoteVisibilities(bool reply, bool renote)
|
public Notification WithPrecomputedNoteVisibilities(bool reply, bool renote)
|
||||||
{
|
{
|
||||||
Note = Note?.WithPrecomputedVisibilities(reply, renote);
|
Note = Note?.WithPrecomputedVisibilities(reply, renote);
|
||||||
|
|
|
@ -16,9 +16,12 @@ public class PushSubscription
|
||||||
[PgName("all")] All,
|
[PgName("all")] All,
|
||||||
[PgName("followed")] Followed,
|
[PgName("followed")] Followed,
|
||||||
[PgName("follower")] Follower,
|
[PgName("follower")] Follower,
|
||||||
[PgName("none")] None,
|
[PgName("none")] None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Column("types", TypeName = "character varying(32)[]")]
|
||||||
|
public List<string> Types = null!;
|
||||||
|
|
||||||
[Key]
|
[Key]
|
||||||
[Column("id")]
|
[Column("id")]
|
||||||
[StringLength(32)]
|
[StringLength(32)]
|
||||||
|
@ -42,9 +45,6 @@ public class PushSubscription
|
||||||
[StringLength(128)]
|
[StringLength(128)]
|
||||||
public string PublicKey { get; set; } = null!;
|
public string PublicKey { get; set; } = null!;
|
||||||
|
|
||||||
[Column("types", TypeName = "character varying(32)[]")]
|
|
||||||
public List<string> Types = null!;
|
|
||||||
|
|
||||||
[Column("policy")] public PushPolicy Policy { get; set; }
|
[Column("policy")] public PushPolicy Policy { get; set; }
|
||||||
|
|
||||||
[ForeignKey("UserId")]
|
[ForeignKey("UserId")]
|
||||||
|
|
|
@ -494,6 +494,9 @@ public class User : IEntity
|
||||||
[NotMapped] public bool? PrecomputedIsRequested { get; set; }
|
[NotMapped] public bool? PrecomputedIsRequested { get; set; }
|
||||||
[NotMapped] public bool? PrecomputedIsRequestedBy { get; set; }
|
[NotMapped] public bool? PrecomputedIsRequestedBy { get; set; }
|
||||||
|
|
||||||
|
public bool IsLocalUser => Host == null;
|
||||||
|
public bool IsRemoteUser => Host != null;
|
||||||
|
|
||||||
[Key]
|
[Key]
|
||||||
[Column("id")]
|
[Column("id")]
|
||||||
[StringLength(32)]
|
[StringLength(32)]
|
||||||
|
@ -616,7 +619,4 @@ public class User : IEntity
|
||||||
: throw new Exception("Cannot access PublicUrl for remote user");
|
: throw new Exception("Cannot access PublicUrl for remote user");
|
||||||
|
|
||||||
public string GetIdenticonUrl(string webDomain) => $"https://{webDomain}/identicon/{Id}";
|
public string GetIdenticonUrl(string webDomain) => $"https://{webDomain}/identicon/{Id}";
|
||||||
|
|
||||||
public bool IsLocalUser => Host == null;
|
|
||||||
public bool IsRemoteUser => Host != null;
|
|
||||||
}
|
}
|
|
@ -20,6 +20,8 @@ public class UserProfile
|
||||||
[PgName("private")] Private
|
[PgName("private")] Private
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Column("mentionsResolved")] public bool MentionsResolved;
|
||||||
|
|
||||||
[Key]
|
[Key]
|
||||||
[Column("userId")]
|
[Column("userId")]
|
||||||
[StringLength(32)]
|
[StringLength(32)]
|
||||||
|
@ -176,9 +178,6 @@ public class UserProfile
|
||||||
[InverseProperty(nameof(Tables.User.UserProfile))]
|
[InverseProperty(nameof(Tables.User.UserProfile))]
|
||||||
public virtual User User { get; set; } = null!;
|
public virtual User User { get; set; } = null!;
|
||||||
|
|
||||||
[Column("mentionsResolved")]
|
|
||||||
public bool MentionsResolved;
|
|
||||||
|
|
||||||
public class Field
|
public class Field
|
||||||
{
|
{
|
||||||
[J("name")] public required string Name { get; set; }
|
[J("name")] public required string Name { get; set; }
|
||||||
|
|
|
@ -28,7 +28,7 @@ public static class MvcBuilderExtensions
|
||||||
|
|
||||||
if (!opts.OutputFormatters.OfType<SystemTextJsonOutputFormatter>().Any())
|
if (!opts.OutputFormatters.OfType<SystemTextJsonOutputFormatter>().Any())
|
||||||
opts.OutputFormatters.Add(new SystemTextJsonOutputFormatter(jsonOpts.Value
|
opts.OutputFormatters.Add(new SystemTextJsonOutputFormatter(jsonOpts.Value
|
||||||
.JsonSerializerOptions));
|
.JsonSerializerOptions));
|
||||||
|
|
||||||
opts.InputFormatters.Insert(0, new JsonInputMultiFormatter());
|
opts.InputFormatters.Insert(0, new JsonInputMultiFormatter());
|
||||||
opts.OutputFormatters.Insert(0, new JsonOutputMultiFormatter());
|
opts.OutputFormatters.Insert(0, new JsonOutputMultiFormatter());
|
||||||
|
@ -39,20 +39,19 @@ public static class MvcBuilderExtensions
|
||||||
|
|
||||||
public static IMvcBuilder AddModelBindingProviders(this IMvcBuilder builder)
|
public static IMvcBuilder AddModelBindingProviders(this IMvcBuilder builder)
|
||||||
{
|
{
|
||||||
builder.Services.AddOptions<MvcOptions>().PostConfigure(options =>
|
builder.Services.AddOptions<MvcOptions>()
|
||||||
{
|
.PostConfigure(options => { options.ModelBinderProviders.AddHybridBindingProvider(); });
|
||||||
options.ModelBinderProviders.AddHybridBindingProvider();
|
|
||||||
});
|
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IMvcBuilder AddValueProviderFactories(this IMvcBuilder builder)
|
public static IMvcBuilder AddValueProviderFactories(this IMvcBuilder builder)
|
||||||
{
|
{
|
||||||
builder.Services.AddOptions<MvcOptions>().PostConfigure(options =>
|
builder.Services.AddOptions<MvcOptions>()
|
||||||
{
|
.PostConfigure(options =>
|
||||||
options.ValueProviderFactories.Add(new JQueryQueryStringValueProviderFactory());
|
{
|
||||||
});
|
options.ValueProviderFactories.Add(new JQueryQueryStringValueProviderFactory());
|
||||||
|
});
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
|
@ -289,7 +289,9 @@ public static class QueryableExtensions
|
||||||
.Where(note => note.Reply == null || !note.Reply.User.IsMuting(user));
|
.Where(note => note.Reply == null || !note.Reply.User.IsMuting(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IQueryable<Note> FilterBlockedConversations(this IQueryable<Note> query, User user, DatabaseContext db)
|
public static IQueryable<Note> FilterBlockedConversations(
|
||||||
|
this IQueryable<Note> query, User user, DatabaseContext db
|
||||||
|
)
|
||||||
{
|
{
|
||||||
return query.Where(p => !db.Blockings.Any(i => i.Blocker == user && p.VisibleUserIds.Contains(i.BlockeeId)));
|
return query.Where(p => !db.Blockings.Any(i => i.Blocker == user && p.VisibleUserIds.Contains(i.BlockeeId)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,9 @@ public class UserRenderer(IOptions<Config.InstanceSection> config, DatabaseConte
|
||||||
Endpoints = new ASEndpoints { SharedInbox = new ASObjectBase($"https://{config.Value.WebDomain}/inbox") },
|
Endpoints = new ASEndpoints { SharedInbox = new ASObjectBase($"https://{config.Value.WebDomain}/inbox") },
|
||||||
PublicKey = new ASPublicKey
|
PublicKey = new ASPublicKey
|
||||||
{
|
{
|
||||||
Id = $"{id}#main-key", Owner = new ASObjectBase(id), PublicKey = keypair.PublicKey
|
Id = $"{id}#main-key",
|
||||||
|
Owner = new ASObjectBase(id),
|
||||||
|
PublicKey = keypair.PublicKey
|
||||||
},
|
},
|
||||||
Tags = tags
|
Tags = tags
|
||||||
};
|
};
|
||||||
|
|
|
@ -64,8 +64,6 @@ public static class LdHelpers
|
||||||
JToken.Parse(File.ReadAllText(Path.Combine("Core", "Federation", "ActivityStreams", "Contexts",
|
JToken.Parse(File.ReadAllText(Path.Combine("Core", "Federation", "ActivityStreams", "Contexts",
|
||||||
"as-extensions.json")));
|
"as-extensions.json")));
|
||||||
|
|
||||||
private static IEnumerable<string> ASForceArray => ["tag", "to", "cc", "bcc", "bto"];
|
|
||||||
|
|
||||||
private static readonly JsonLdProcessorOptions Options = new()
|
private static readonly JsonLdProcessorOptions Options = new()
|
||||||
{
|
{
|
||||||
DocumentLoader = CustomLoader,
|
DocumentLoader = CustomLoader,
|
||||||
|
@ -75,7 +73,7 @@ public static class LdHelpers
|
||||||
ForceArray = ASForceArray.Select(p => $"{Constants.ActivityStreamsNs}#{p}").ToList(),
|
ForceArray = ASForceArray.Select(p => $"{Constants.ActivityStreamsNs}#{p}").ToList(),
|
||||||
|
|
||||||
// separated for readability
|
// separated for readability
|
||||||
RemoveUnusedInlineContextProperties = true,
|
RemoveUnusedInlineContextProperties = true
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly JsonSerializerSettings JsonSerializerSettings = new()
|
public static readonly JsonSerializerSettings JsonSerializerSettings = new()
|
||||||
|
@ -88,6 +86,8 @@ public static class LdHelpers
|
||||||
NullValueHandling = NullValueHandling.Ignore, DateTimeZoneHandling = DateTimeZoneHandling.Local
|
NullValueHandling = NullValueHandling.Ignore, DateTimeZoneHandling = DateTimeZoneHandling.Local
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static IEnumerable<string> ASForceArray => ["tag", "to", "cc", "bcc", "bto"];
|
||||||
|
|
||||||
private static RemoteDocument CustomLoader(Uri uri, JsonLdLoaderOptions jsonLdLoaderOptions)
|
private static RemoteDocument CustomLoader(Uri uri, JsonLdLoaderOptions jsonLdLoaderOptions)
|
||||||
{
|
{
|
||||||
var key = uri.AbsolutePath == "/schemas/litepub-0.1.jsonld" ? "litepub-0.1" : uri.ToString();
|
var key = uri.AbsolutePath == "/schemas/litepub-0.1.jsonld" ? "litepub-0.1" : uri.ToString();
|
||||||
|
|
|
@ -37,7 +37,7 @@ public class ASActivity : ASObject
|
||||||
|
|
||||||
// Extensions
|
// Extensions
|
||||||
public const string Bite = "https://ns.mia.jetzt/as#Bite";
|
public const string Bite = "https://ns.mia.jetzt/as#Bite";
|
||||||
public const string EmojiReact = $"http://litepub.social/ns#EmojiReact";
|
public const string EmojiReact = "http://litepub.social/ns#EmojiReact";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,13 +36,13 @@ public class ASDocument : ASAttachment
|
||||||
|
|
||||||
public class ASField : ASAttachment
|
public class ASField : ASAttachment
|
||||||
{
|
{
|
||||||
public ASField() => Type = $"{Constants.SchemaNs}#PropertyValue";
|
|
||||||
|
|
||||||
[J($"{Constants.ActivityStreamsNs}#name")] [JC(typeof(ValueObjectConverter))]
|
[J($"{Constants.ActivityStreamsNs}#name")] [JC(typeof(ValueObjectConverter))]
|
||||||
public string? Name;
|
public string? Name;
|
||||||
|
|
||||||
[J($"{Constants.SchemaNs}#value")] [JC(typeof(ValueObjectConverter))]
|
[J($"{Constants.SchemaNs}#value")] [JC(typeof(ValueObjectConverter))]
|
||||||
public string? Value;
|
public string? Value;
|
||||||
|
|
||||||
|
public ASField() => Type = $"{Constants.SchemaNs}#PropertyValue";
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ASAttachmentConverter : JsonConverter
|
public sealed class ASAttachmentConverter : JsonConverter
|
||||||
|
|
|
@ -10,6 +10,8 @@ namespace Iceshrimp.Backend.Core.Federation.ActivityStreams.Types;
|
||||||
|
|
||||||
public class ASCollection : ASObject
|
public class ASCollection : ASObject
|
||||||
{
|
{
|
||||||
|
public const string ObjectType = $"{Constants.ActivityStreamsNs}#Collection";
|
||||||
|
|
||||||
[JsonConstructor]
|
[JsonConstructor]
|
||||||
public ASCollection(bool withType = true) => Type = withType ? ObjectType : null;
|
public ASCollection(bool withType = true) => Type = withType ? ObjectType : null;
|
||||||
|
|
||||||
|
@ -37,8 +39,6 @@ public class ASCollection : ASObject
|
||||||
public ASLink? Last { get; set; }
|
public ASLink? Last { get; set; }
|
||||||
|
|
||||||
public new bool IsUnresolved => !TotalItems.HasValue;
|
public new bool IsUnresolved => !TotalItems.HasValue;
|
||||||
|
|
||||||
public const string ObjectType = $"{Constants.ActivityStreamsNs}#Collection";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ASCollectionConverter : JsonConverter
|
public sealed class ASCollectionConverter : JsonConverter
|
||||||
|
|
|
@ -7,6 +7,8 @@ namespace Iceshrimp.Backend.Core.Federation.ActivityStreams.Types;
|
||||||
|
|
||||||
public class ASCollectionBase : ASObjectBase
|
public class ASCollectionBase : ASObjectBase
|
||||||
{
|
{
|
||||||
|
public const string ObjectType = $"{Constants.ActivityStreamsNs}#Collection";
|
||||||
|
|
||||||
[J("@type")]
|
[J("@type")]
|
||||||
[JC(typeof(StringListSingleConverter))]
|
[JC(typeof(StringListSingleConverter))]
|
||||||
public string Type => ObjectType;
|
public string Type => ObjectType;
|
||||||
|
@ -14,8 +16,6 @@ public class ASCollectionBase : ASObjectBase
|
||||||
[J($"{Constants.ActivityStreamsNs}#totalItems")]
|
[J($"{Constants.ActivityStreamsNs}#totalItems")]
|
||||||
[JC(typeof(VC))]
|
[JC(typeof(VC))]
|
||||||
public ulong? TotalItems { get; set; }
|
public ulong? TotalItems { get; set; }
|
||||||
|
|
||||||
public const string ObjectType = $"{Constants.ActivityStreamsNs}#Collection";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ASCollectionBaseConverter : ASSerializer.ListSingleObjectConverter<ASCollectionBase>;
|
public sealed class ASCollectionBaseConverter : ASSerializer.ListSingleObjectConverter<ASCollectionBase>;
|
|
@ -10,6 +10,8 @@ namespace Iceshrimp.Backend.Core.Federation.ActivityStreams.Types;
|
||||||
|
|
||||||
public class ASCollectionPage : ASObject
|
public class ASCollectionPage : ASObject
|
||||||
{
|
{
|
||||||
|
public const string ObjectType = $"{Constants.ActivityStreamsNs}#CollectionPage";
|
||||||
|
|
||||||
[JsonConstructor]
|
[JsonConstructor]
|
||||||
public ASCollectionPage(bool withType = true) => Type = withType ? ObjectType : null;
|
public ASCollectionPage(bool withType = true) => Type = withType ? ObjectType : null;
|
||||||
|
|
||||||
|
@ -35,8 +37,6 @@ public class ASCollectionPage : ASObject
|
||||||
[J($"{Constants.ActivityStreamsNs}#next")]
|
[J($"{Constants.ActivityStreamsNs}#next")]
|
||||||
[JC(typeof(ASLinkConverter))]
|
[JC(typeof(ASLinkConverter))]
|
||||||
public ASLink? Next { get; set; }
|
public ASLink? Next { get; set; }
|
||||||
|
|
||||||
public const string ObjectType = $"{Constants.ActivityStreamsNs}#CollectionPage";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ASCollectionPageConverter : JsonConverter
|
public sealed class ASCollectionPageConverter : JsonConverter
|
||||||
|
|
|
@ -94,13 +94,13 @@ public class ASNote : ASObject
|
||||||
{
|
{
|
||||||
if (actor.Host == null) throw new Exception("Can't get recipients for local actor");
|
if (actor.Host == null) throw new Exception("Can't get recipients for local actor");
|
||||||
return (To ?? []).Concat(Cc ?? [])
|
return (To ?? []).Concat(Cc ?? [])
|
||||||
.Select(p => p.Id)
|
.Select(p => p.Id)
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.Where(p => p != $"{Constants.ActivityStreamsNs}#Public" &&
|
.Where(p => p != $"{Constants.ActivityStreamsNs}#Public" &&
|
||||||
p != (actor.FollowersUri ?? actor.Uri + "/followers"))
|
p != (actor.FollowersUri ?? actor.Uri + "/followers"))
|
||||||
.Where(p => p != null)
|
.Where(p => p != null)
|
||||||
.Select(p => p!)
|
.Select(p => p!)
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public new static class Types
|
public new static class Types
|
||||||
|
|
|
@ -9,6 +9,8 @@ namespace Iceshrimp.Backend.Core.Federation.ActivityStreams.Types;
|
||||||
|
|
||||||
public class ASOrderedCollection : ASCollection
|
public class ASOrderedCollection : ASCollection
|
||||||
{
|
{
|
||||||
|
public new const string ObjectType = $"{Constants.ActivityStreamsNs}#OrderedCollection";
|
||||||
|
|
||||||
[JsonConstructor]
|
[JsonConstructor]
|
||||||
public ASOrderedCollection(bool withType = true) => Type = withType ? ObjectType : null;
|
public ASOrderedCollection(bool withType = true) => Type = withType ? ObjectType : null;
|
||||||
|
|
||||||
|
@ -22,8 +24,6 @@ public class ASOrderedCollection : ASCollection
|
||||||
get => base.Items;
|
get => base.Items;
|
||||||
set => base.Items = value;
|
set => base.Items = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public new const string ObjectType = $"{Constants.ActivityStreamsNs}#OrderedCollection";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class ASOrderedCollectionItemsConverter : ASCollectionItemsConverter
|
internal sealed class ASOrderedCollectionItemsConverter : ASCollectionItemsConverter
|
||||||
|
|
|
@ -10,6 +10,8 @@ namespace Iceshrimp.Backend.Core.Federation.ActivityStreams.Types;
|
||||||
|
|
||||||
public class ASOrderedCollectionPage : ASObject
|
public class ASOrderedCollectionPage : ASObject
|
||||||
{
|
{
|
||||||
|
public const string ObjectType = $"{Constants.ActivityStreamsNs}#OrderedCollectionPage";
|
||||||
|
|
||||||
[JsonConstructor]
|
[JsonConstructor]
|
||||||
public ASOrderedCollectionPage(bool withType = true) => Type = withType ? ObjectType : null;
|
public ASOrderedCollectionPage(bool withType = true) => Type = withType ? ObjectType : null;
|
||||||
|
|
||||||
|
@ -35,8 +37,6 @@ public class ASOrderedCollectionPage : ASObject
|
||||||
[J($"{Constants.ActivityStreamsNs}#next")]
|
[J($"{Constants.ActivityStreamsNs}#next")]
|
||||||
[JC(typeof(ASLinkConverter))]
|
[JC(typeof(ASLinkConverter))]
|
||||||
public ASLink? Next { get; set; }
|
public ASLink? Next { get; set; }
|
||||||
|
|
||||||
public const string ObjectType = $"{Constants.ActivityStreamsNs}#OrderedCollectionPage";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ASOrderedCollectionPageConverter : JsonConverter
|
public sealed class ASOrderedCollectionPageConverter : JsonConverter
|
||||||
|
|
|
@ -7,10 +7,9 @@ namespace Iceshrimp.Backend.Core.Federation.ActivityStreams.Types;
|
||||||
|
|
||||||
public class ASQuestion : ASNote
|
public class ASQuestion : ASNote
|
||||||
{
|
{
|
||||||
public ASQuestion() => Type = Types.Question;
|
|
||||||
|
|
||||||
private List<ASQuestionOption>? _anyOf;
|
private List<ASQuestionOption>? _anyOf;
|
||||||
private List<ASQuestionOption>? _oneOf;
|
private List<ASQuestionOption>? _oneOf;
|
||||||
|
public ASQuestion() => Type = Types.Question;
|
||||||
|
|
||||||
[J($"{Constants.ActivityStreamsNs}#oneOf")]
|
[J($"{Constants.ActivityStreamsNs}#oneOf")]
|
||||||
public List<ASQuestionOption>? OneOf
|
public List<ASQuestionOption>? OneOf
|
||||||
|
|
|
@ -306,7 +306,9 @@ internal class MentionNodeParser : INodeParser
|
||||||
|
|
||||||
var node = new MfmMentionNode
|
var node = new MfmMentionNode
|
||||||
{
|
{
|
||||||
Username = split[0], Host = split.Length == 2 ? split[1] : null, Acct = $"@{buffer[start..end]}"
|
Username = split[0],
|
||||||
|
Host = split.Length == 2 ? split[1] : null,
|
||||||
|
Acct = $"@{buffer[start..end]}"
|
||||||
};
|
};
|
||||||
|
|
||||||
return (node, chars);
|
return (node, chars);
|
||||||
|
|
|
@ -146,11 +146,11 @@ public abstract class BackgroundTaskQueue
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var db = scope.GetRequiredService<DatabaseContext>();
|
var db = scope.GetRequiredService<DatabaseContext>();
|
||||||
var poll = await db.Polls.FirstOrDefaultAsync(p => p.NoteId == job.NoteId, cancellationToken: token);
|
var poll = await db.Polls.FirstOrDefaultAsync(p => p.NoteId == job.NoteId, token);
|
||||||
if (poll == null) return;
|
if (poll == null) return;
|
||||||
if (poll.ExpiresAt > DateTime.UtcNow + TimeSpan.FromSeconds(30)) return;
|
if (poll.ExpiresAt > DateTime.UtcNow + TimeSpan.FromSeconds(30)) return;
|
||||||
var note = await db.Notes.IncludeCommonProperties()
|
var note = await db.Notes.IncludeCommonProperties()
|
||||||
.FirstOrDefaultAsync(p => p.Id == poll.NoteId, cancellationToken: token);
|
.FirstOrDefaultAsync(p => p.Id == poll.NoteId, token);
|
||||||
if (note == null) return;
|
if (note == null) return;
|
||||||
|
|
||||||
var notificationSvc = scope.GetRequiredService<NotificationService>();
|
var notificationSvc = scope.GetRequiredService<NotificationService>();
|
||||||
|
@ -159,7 +159,7 @@ public abstract class BackgroundTaskQueue
|
||||||
{
|
{
|
||||||
var voters = await db.PollVotes.Where(p => p.Note == note && p.User.Host != null)
|
var voters = await db.PollVotes.Where(p => p.Note == note && p.User.Host != null)
|
||||||
.Select(p => p.User)
|
.Select(p => p.User)
|
||||||
.ToListAsync(cancellationToken: token);
|
.ToListAsync(token);
|
||||||
|
|
||||||
if (voters.Count == 0) return;
|
if (voters.Count == 0) return;
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ public class InstanceService(DatabaseContext db, HttpClient httpClient)
|
||||||
Id = IdHelpers.GenerateSlowflakeId(),
|
Id = IdHelpers.GenerateSlowflakeId(),
|
||||||
Host = host,
|
Host = host,
|
||||||
CaughtAt = DateTime.UtcNow,
|
CaughtAt = DateTime.UtcNow,
|
||||||
LastCommunicatedAt = DateTime.UtcNow,
|
LastCommunicatedAt = DateTime.UtcNow
|
||||||
};
|
};
|
||||||
await db.AddAsync(instance);
|
await db.AddAsync(instance);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
|
@ -191,7 +191,7 @@ public class NoteService(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This needs to be called before SaveChangesAsync on create & after on delete
|
/// This needs to be called before SaveChangesAsync on create & after on delete
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
private async Task UpdateNoteCountersAsync(Note note, bool create)
|
private async Task UpdateNoteCountersAsync(Note note, bool create)
|
||||||
{
|
{
|
||||||
|
|
|
@ -76,7 +76,12 @@ public class SystemUserService(ILogger<SystemUserService> logger, DatabaseContex
|
||||||
PublicKey = keypair.ExportSubjectPublicKeyInfoPem()
|
PublicKey = keypair.ExportSubjectPublicKeyInfoPem()
|
||||||
};
|
};
|
||||||
|
|
||||||
var userProfile = new UserProfile { UserId = user.Id, AutoAcceptFollowed = false, Password = null };
|
var userProfile = new UserProfile
|
||||||
|
{
|
||||||
|
UserId = user.Id,
|
||||||
|
AutoAcceptFollowed = false,
|
||||||
|
Password = null
|
||||||
|
};
|
||||||
|
|
||||||
var usedUsername = new UsedUsername { CreatedAt = DateTime.UtcNow, Username = username.ToLowerInvariant() };
|
var usedUsername = new UsedUsername { CreatedAt = DateTime.UtcNow, Username = username.ToLowerInvariant() };
|
||||||
|
|
||||||
|
|
|
@ -298,8 +298,8 @@ public class UserService(
|
||||||
db.Update(user);
|
db.Update(user);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
await processPendingDeletes();
|
await processPendingDeletes();
|
||||||
user = await UpdateProfileMentions(user, actor, force: true);
|
user = await UpdateProfileMentions(user, actor, true);
|
||||||
UpdateUserPinnedNotesInBackground(actor, user, force: true);
|
UpdateUserPinnedNotesInBackground(actor, user, true);
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,53 +16,53 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Amazon.S3" Version="0.31.2" />
|
<PackageReference Include="Amazon.S3" Version="0.31.2"/>
|
||||||
<PackageReference Include="AngleSharp" Version="1.1.0" />
|
<PackageReference Include="AngleSharp" Version="1.1.0"/>
|
||||||
<PackageReference Include="Asp.Versioning.Http" Version="8.0.0" />
|
<PackageReference Include="Asp.Versioning.Http" Version="8.0.0"/>
|
||||||
<PackageReference Include="AsyncKeyedLock" Version="6.3.4" />
|
<PackageReference Include="AsyncKeyedLock" Version="6.3.4"/>
|
||||||
<PackageReference Include="Blurhash.ImageSharp" Version="3.0.0" />
|
<PackageReference Include="Blurhash.ImageSharp" Version="3.0.0"/>
|
||||||
<PackageReference Include="cuid.net" Version="5.0.2" />
|
<PackageReference Include="cuid.net" Version="5.0.2"/>
|
||||||
<PackageReference Include="dotNetRdf.Core" Version="3.2.6-iceshrimp" />
|
<PackageReference Include="dotNetRdf.Core" Version="3.2.6-iceshrimp"/>
|
||||||
<PackageReference Include="EntityFrameworkCore.Exceptions.PostgreSQL" Version="8.0.0" />
|
<PackageReference Include="EntityFrameworkCore.Exceptions.PostgreSQL" Version="8.0.0"/>
|
||||||
<PackageReference Include="EntityFrameworkCore.Projectables" Version="3.0.4" />
|
<PackageReference Include="EntityFrameworkCore.Projectables" Version="3.0.4"/>
|
||||||
<PackageReference Include="Isopoh.Cryptography.Argon2" Version="2.0.0" />
|
<PackageReference Include="Isopoh.Cryptography.Argon2" Version="2.0.0"/>
|
||||||
<PackageReference Include="libsodium" Version="1.0.18.4" />
|
<PackageReference Include="libsodium" Version="1.0.18.4"/>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="8.0.1" />
|
<PackageReference Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="8.0.1"/>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.0"/>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.1"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Ini" Version="8.0.0"/>
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0"/>
|
||||||
<PackageReference Include="protobuf-net" Version="3.2.30" />
|
<PackageReference Include="protobuf-net" Version="3.2.30"/>
|
||||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.1" />
|
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.1"/>
|
||||||
<PackageReference Include="StackExchange.Redis" Version="2.7.17" />
|
<PackageReference Include="StackExchange.Redis" Version="2.7.17"/>
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0"/>
|
||||||
<PackageReference Include="System.IO.Hashing" Version="8.0.0" />
|
<PackageReference Include="System.IO.Hashing" Version="8.0.0"/>
|
||||||
<PackageReference Include="Vite.AspNetCore" Version="1.11.0" />
|
<PackageReference Include="Vite.AspNetCore" Version="1.11.0"/>
|
||||||
<PackageReference Include="WebPush" Version="1.0.24-iceshrimp" />
|
<PackageReference Include="WebPush" Version="1.0.24-iceshrimp"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AdditionalFiles Include="Pages\Error.cshtml" />
|
<AdditionalFiles Include="Pages\Error.cshtml"/>
|
||||||
<AdditionalFiles Include="Pages\Shared\_Layout.cshtml" />
|
<AdditionalFiles Include="Pages\Shared\_Layout.cshtml"/>
|
||||||
<AdditionalFiles Include="Pages\_ViewImports.cshtml" />
|
<AdditionalFiles Include="Pages\_ViewImports.cshtml"/>
|
||||||
<AdditionalFiles Include="Pages\_ViewStart.cshtml" />
|
<AdditionalFiles Include="Pages\_ViewStart.cshtml"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="wwwroot\.vite\manifest.json" CopyToPublishDirectory="PreserveNewest" />
|
<Content Include="wwwroot\.vite\manifest.json" CopyToPublishDirectory="PreserveNewest"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="migrate.sql" />
|
<None Remove="migrate.sql"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Iceshrimp.Parsing\Iceshrimp.Parsing.fsproj" />
|
<ProjectReference Include="..\Iceshrimp.Parsing\Iceshrimp.Parsing.fsproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -6,11 +6,11 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="SearchQuery.fs" />
|
<Compile Include="SearchQuery.fs"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FParsec" Version="1.1.1" />
|
<PackageReference Include="FParsec" Version="1.1.1"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -16,7 +16,8 @@ public class SearchQueryTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
[DataRow(false), DataRow(true)]
|
[DataRow(false)]
|
||||||
|
[DataRow(true)]
|
||||||
public void TestParseFrom(bool negated)
|
public void TestParseFrom(bool negated)
|
||||||
{
|
{
|
||||||
List<string> candidates = ["from", "author", "by", "user"];
|
List<string> candidates = ["from", "author", "by", "user"];
|
||||||
|
@ -27,7 +28,8 @@ public class SearchQueryTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
[DataRow(false), DataRow(true)]
|
[DataRow(false)]
|
||||||
|
[DataRow(true)]
|
||||||
public void TestParseMention(bool negated)
|
public void TestParseMention(bool negated)
|
||||||
{
|
{
|
||||||
List<string> candidates = ["mention", "mentions", "mentioning"];
|
List<string> candidates = ["mention", "mentions", "mentioning"];
|
||||||
|
@ -38,7 +40,8 @@ public class SearchQueryTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
[DataRow(false), DataRow(true)]
|
[DataRow(false)]
|
||||||
|
[DataRow(true)]
|
||||||
public void TestParseReply(bool negated)
|
public void TestParseReply(bool negated)
|
||||||
{
|
{
|
||||||
List<string> candidates = ["reply", "replying", "to"];
|
List<string> candidates = ["reply", "replying", "to"];
|
||||||
|
@ -49,7 +52,8 @@ public class SearchQueryTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
[DataRow(false), DataRow(true)]
|
[DataRow(false)]
|
||||||
|
[DataRow(true)]
|
||||||
public void TestParseInstance(bool negated)
|
public void TestParseInstance(bool negated)
|
||||||
{
|
{
|
||||||
List<string> candidates = ["instance", "domain", "host"];
|
List<string> candidates = ["instance", "domain", "host"];
|
||||||
|
@ -78,7 +82,8 @@ public class SearchQueryTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
[DataRow(false), DataRow(true)]
|
[DataRow(false)]
|
||||||
|
[DataRow(true)]
|
||||||
public void TestParseAttachment(bool negated)
|
public void TestParseAttachment(bool negated)
|
||||||
{
|
{
|
||||||
List<string> keyCandidates = ["has", "attachment", "attached"];
|
List<string> keyCandidates = ["has", "attachment", "attached"];
|
||||||
|
@ -127,7 +132,8 @@ public class SearchQueryTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
[DataRow(false), DataRow(true)]
|
[DataRow(false)]
|
||||||
|
[DataRow(true)]
|
||||||
public void TestParseIn(bool negated)
|
public void TestParseIn(bool negated)
|
||||||
{
|
{
|
||||||
var key = negated ? "-in" : "in";
|
var key = negated ? "-in" : "in";
|
||||||
|
@ -147,7 +153,8 @@ public class SearchQueryTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
[DataRow(false), DataRow(true)]
|
[DataRow(false)]
|
||||||
|
[DataRow(true)]
|
||||||
public void TestParseMisc(bool negated)
|
public void TestParseMisc(bool negated)
|
||||||
{
|
{
|
||||||
var key = negated ? "-filter" : "filter";
|
var key = negated ? "-filter" : "filter";
|
||||||
|
@ -165,7 +172,7 @@ public class SearchQueryTests
|
||||||
new MiscFilter(negated, "renotes"),
|
new MiscFilter(negated, "renotes"),
|
||||||
new MiscFilter(negated, "renotes"),
|
new MiscFilter(negated, "renotes"),
|
||||||
new MiscFilter(negated, "renotes"),
|
new MiscFilter(negated, "renotes"),
|
||||||
new MiscFilter(negated, "renotes"),
|
new MiscFilter(negated, "renotes")
|
||||||
];
|
];
|
||||||
results.Should()
|
results.Should()
|
||||||
.HaveCount(expectedResults.Count)
|
.HaveCount(expectedResults.Count)
|
||||||
|
@ -173,7 +180,8 @@ public class SearchQueryTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
[DataRow(false), DataRow(true)]
|
[DataRow(false)]
|
||||||
|
[DataRow(true)]
|
||||||
public void TestParseWord(bool negated)
|
public void TestParseWord(bool negated)
|
||||||
{
|
{
|
||||||
List<string> candidates = ["test", "word", "since:2023-10-10invalid", "in:bookmarkstypo"];
|
List<string> candidates = ["test", "word", "since:2023-10-10invalid", "in:bookmarkstypo"];
|
||||||
|
|
Loading…
Add table
Reference in a new issue