Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions PinkSea.Frontend/.vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,11 @@
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[typescript]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
},
"[html]": {
"editor.defaultFormatter": "vscode.html-language-features"
},
"[json]": {
"editor.defaultFormatter": "vscode.json-language-features"
}
}
Binary file added PinkSea.Frontend/public/assets/img/logo-16x16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added PinkSea.Frontend/public/assets/img/logo-32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
68 changes: 68 additions & 0 deletions PinkSea.Gateway/ActivityStreams/Actor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System.Text.Json.Serialization;

namespace PinkSea.Gateway.ActivityStreams;

/// <summary>
/// An ActivityStreams actor.
/// </summary>
public class Actor : IActivityStreamsObject
{
/// <summary>
/// The ID of the object.
/// </summary>
[JsonPropertyName("id")]
public required string Id { get; init; }

/// <summary>
/// When was this object published at?
/// </summary>
[JsonPropertyName("published")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public DateTimeOffset? PublishedAt { get; set; }

/// <inheritdoc />
[JsonPropertyName("type")]
public string Type => "Person";

/// <summary>
/// The name of the actor.
/// </summary>
[JsonPropertyName("name")]
public string? Name { get; set; }

/// <summary>
/// The display name.
/// </summary>
[JsonPropertyName("preferredUsername")]
public string? PreferredUsername { get; set; }

/// <summary>
/// The bio of this actor.
/// </summary>
[JsonPropertyName("summary")]
public string? Bio { get; set; }

/// <summary>
/// The inbox.
/// </summary>
[JsonPropertyName("inbox")]
public string? Inbox { get; set; }

/// <summary>
/// The outbox.
/// </summary>
[JsonPropertyName("outbox")]
public string? Outbox { get; set; }

/// <summary>
/// The icon.
/// </summary>
[JsonPropertyName("icon")]
public Image? Icon { get; set; }

/// <summary>
/// The url of the actor.
/// </summary>
[JsonPropertyName("url")]
public string? Url { get; set; }
}
31 changes: 31 additions & 0 deletions PinkSea.Gateway/ActivityStreams/Document.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Text.Json.Serialization;

namespace PinkSea.Gateway.ActivityStreams;

/// <summary>
/// An ActivityStreams document.
/// </summary>
public class Document : IActivityStreamsObject
{
/// <inheritdoc />
[JsonPropertyName("type")]
public string Type => "Document";

/// <summary>
/// The name of the document.
/// </summary>
[JsonPropertyName("name")]
public string? Name { get; set; }

/// <summary>
/// The hyperlink.
/// </summary>
[JsonPropertyName("url")]
public string? Href { get; set; }

/// <summary>
/// The media type of the document.
/// </summary>
[JsonPropertyName("mediaType")]
public string? MediaType { get; set; }
}
21 changes: 21 additions & 0 deletions PinkSea.Gateway/ActivityStreams/Hashtag.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Text.Json.Serialization;

namespace PinkSea.Gateway.ActivityStreams;

/// <summary>
/// An ActivityStreams hashtag.
/// </summary>
public class Hashtag
{
/// <summary>
/// The ID of the hashtag.
/// </summary>
[JsonPropertyName("id")]
public required string Id { get; set; }

/// <summary>
/// The name of the hashtag.
/// </summary>
[JsonPropertyName("name")]
public required string Name { get; set; }
}
12 changes: 12 additions & 0 deletions PinkSea.Gateway/ActivityStreams/IActivityStreamsObject.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace PinkSea.Gateway.ActivityStreams;

/// <summary>
/// An interface for an activity streams object.
/// </summary>
public interface IActivityStreamsObject
{
/// <summary>
/// The type of the object.
/// </summary>
string Type { get; }
}
19 changes: 19 additions & 0 deletions PinkSea.Gateway/ActivityStreams/Link.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Text.Json.Serialization;

namespace PinkSea.Gateway.ActivityStreams;

/// <summary>
/// An ActivityStreams image.
/// </summary>
public class Image : IActivityStreamsObject
{
/// <inheritdoc />
[JsonPropertyName("type")]
public string Type => "Image";

/// <summary>
/// The url of the image.
/// </summary>
[JsonPropertyName("url")]
public string? Url { get; set; }
}
70 changes: 70 additions & 0 deletions PinkSea.Gateway/ActivityStreams/Note.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System.Text.Json.Serialization;

namespace PinkSea.Gateway.ActivityStreams;

public class Note : IActivityStreamsObject
{
/// <summary>
/// The type of the object
/// </summary>
[JsonPropertyName("type")]
public string Type => "Note";

/// <summary>
/// The ID of the object.
/// </summary>
[JsonPropertyName("id")]
public required string Id { get; init; }

/// <summary>
/// When was this object published at?
/// </summary>
[JsonPropertyName("published")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public DateTimeOffset? PublishedAt { get; set; }

/// <summary>
/// Who is this note attributed to?
/// </summary>
[JsonPropertyName("attributedTo")]
public string? AttributedTo { get; set; }

/// <summary>
/// The url of the note.
/// </summary>
[JsonPropertyName("url")]
public string? Url { get; set; }

/// <summary>
/// The content of the note.
/// </summary>
[JsonPropertyName("content")]
public string? Content { get; set; }

/// <summary>
/// What this note is replying to.
/// </summary>
[JsonPropertyName("inReplyTo")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? InReplyTo { get; set; }

/// <summary>
/// Is this post sensitive?
/// </summary>
[JsonPropertyName("sensitive")]
public bool? Sensitive { get; set; }

/// <summary>
/// The list of tags this note has.
/// </summary>
[JsonPropertyName("tag")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public IReadOnlyList<Hashtag>? Tags { get; set; }

/// <summary>
/// The list of attachments this note has.
/// </summary>
[JsonPropertyName("attachment")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public IReadOnlyList<Document>? Attachments { get; set; }
}
40 changes: 40 additions & 0 deletions PinkSea.Gateway/Endpoints/ActivityPubEndpointsMapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Microsoft.AspNetCore.Mvc;
using PinkSea.Gateway.Services;

namespace PinkSea.Gateway.Endpoints;

/// <summary>
/// Maps the ActivityPub-specific endpoints for PinkSea.
/// </summary>
public static class ActivityPubEndpointsMapper
{
/// <summary>
/// Maps the ActivityPub-specific endpoints for PinkSea.
/// </summary>
public static void MapActivityPubEndpoints(this IEndpointRouteBuilder routeBuilder)
{
routeBuilder.MapGet("/ap/note.json",
async ([FromQuery] string did, [FromQuery] string rkey, [FromServices] ActivityPubRenderer activityPubRenderer) =>
{
var response = await activityPubRenderer.RenderNoteForOekaki(did, rkey);
if (response is null)
{
return Results.NotFound();
}

return Results.Json(response, contentType: "application/activity+json");
});

routeBuilder.MapGet("/ap/actor.json",
async ([FromQuery] string did, [FromServices] ActivityPubRenderer activityPubRenderer) =>
{
var response = await activityPubRenderer.RenderActorForProfile(did);
if (response is null)
{
return Results.NotFound();
}

return Results.Json(response, contentType: "application/activity+json");
});
}
}
44 changes: 44 additions & 0 deletions PinkSea.Gateway/Endpoints/GeneralEndpointsMapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Microsoft.AspNetCore.Mvc;
using PinkSea.Gateway.Services;

namespace PinkSea.Gateway.Endpoints;

/// <summary>
/// Mapper for the general endpoints.
/// </summary>
public static class GeneralEndpointsMapper
{
/// <summary>
/// Maps the general endpoints for PinkSea.
/// </summary>
public static void MapGeneralEndpoints(this IEndpointRouteBuilder routeBuilder)
{
routeBuilder.MapGet(
"/{did}/oekaki/{rkey}",
async ([FromRoute] string did, [FromRoute] string rkey, [FromServices] MetaGeneratorService metaGenerator) =>
{
var file = await File.ReadAllTextAsync($"./wwwroot/index.html");
file = file.Replace("<!-- META -->", await metaGenerator.GetOekakiMetaFor(did, rkey));

return Results.Text(file, contentType: "text/html");
});

// The regex ensures we don't accidentally match the favicon...
routeBuilder.MapGet(
"/{did:regex(^(?!favicon\\.ico$).*$)}",
async ([FromRoute] string did, [FromServices] MetaGeneratorService metaGenerator) =>
{
var file = await File.ReadAllTextAsync($"./wwwroot/index.html");
file = file.Replace("<!-- META -->", await metaGenerator.GetProfileMetaFor(did));

return Results.Text(file, contentType: "text/html");
});

routeBuilder.MapGet(
"/xrpc/_health",
() => new
{
version = "PinkSea.Gateway"
});
}
}
30 changes: 30 additions & 0 deletions PinkSea.Gateway/Endpoints/OEmbedEndpointsMapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Microsoft.AspNetCore.Mvc;
using PinkSea.Gateway.Services;

namespace PinkSea.Gateway.Endpoints;

/// <summary>
/// Maps the OEmbed-specific endpoints for PinkSea.
/// </summary>
public static class OEmbedEndpointsMapper
{
/// <summary>
/// Maps the OEmbed-specific endpoints for PinkSea.
/// </summary>
public static void MapOEmbedEndpoints(this IEndpointRouteBuilder routeBuilder)
{
routeBuilder.MapGet("/oembed.json",
async ([FromQuery] string url, [FromServices] OEmbedRenderer oEmbedRenderer) =>
{
var uri = new Uri(url);
var split = uri.AbsolutePath.Split("/");
var response = await oEmbedRenderer.RenderOEmbedForOekaki(split[1], split[3]);
if (response is null)
{
return Results.NotFound();
}

return Results.Json(response, contentType: "application/json+oembed");
});
}
}
6 changes: 6 additions & 0 deletions PinkSea.Gateway/Lexicons/Author.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,10 @@ public class Author
/// </summary>
[JsonPropertyName("handle")]
public required string Handle { get; set; }

/// <summary>
/// The nickname selected by the user.
/// </summary>
[JsonPropertyName("nick")]
public string? Nickname { get; set; }
}
21 changes: 21 additions & 0 deletions PinkSea.Gateway/Lexicons/GetParentForReplyResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Text.Json.Serialization;

namespace PinkSea.Gateway.Lexicons;

/// <summary>
/// The response for the "com.shinolabs.pinksea.getParentForReply" xrpc query.
/// </summary>
public class GetParentForReplyResponse
{
/// <summary>
/// The DID of the author.
/// </summary>
[JsonPropertyName("did")]
public required string AuthorDid { get; init; }

/// <summary>
/// The record key.
/// </summary>
[JsonPropertyName("rkey")]
public required string RecordKey { get; init; }
}
Loading
Loading