✨ Page data
This commit is contained in:
@@ -41,6 +41,7 @@
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.Design" Version="1.1.0"/>
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite" Version="9.0.4"/>
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime" Version="9.0.4"/>
|
||||
<PackageReference Include="OpenGraph-Net" Version="4.0.1" />
|
||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.12.0"/>
|
||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.12.0"/>
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.12.0"/>
|
||||
|
75
DysonNetwork.Sphere/PageData/PostPageData.cs
Normal file
75
DysonNetwork.Sphere/PageData/PostPageData.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System.Net;
|
||||
using DysonNetwork.Shared.PageData;
|
||||
using DysonNetwork.Shared.Proto;
|
||||
using DysonNetwork.Sphere.Post;
|
||||
using DysonNetwork.Sphere.Publisher;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using OpenGraphNet;
|
||||
|
||||
namespace DysonNetwork.Sphere.PageData;
|
||||
|
||||
public class PostPageData(
|
||||
AppDatabase db,
|
||||
AccountService.AccountServiceClient accounts,
|
||||
PublisherService pub,
|
||||
PostService ps,
|
||||
IConfiguration configuration
|
||||
)
|
||||
: IPageDataProvider
|
||||
{
|
||||
private readonly string _siteUrl = configuration["SiteUrl"]!;
|
||||
|
||||
public bool CanHandlePath(PathString path) =>
|
||||
path.StartsWithSegments("/posts");
|
||||
|
||||
public async Task<IDictionary<string, object?>> GetAppDataAsync(HttpContext context)
|
||||
{
|
||||
var path = context.Request.Path.Value!;
|
||||
var startIndex = "/posts/".Length;
|
||||
var endIndex = path.IndexOf('/', startIndex);
|
||||
var slug = endIndex == -1 ? path[startIndex..] : path.Substring(startIndex, endIndex - startIndex);
|
||||
slug = WebUtility.UrlDecode(slug);
|
||||
|
||||
var postId = Guid.TryParse(slug, out var postIdGuid) ? postIdGuid : Guid.Empty;
|
||||
if (postId == Guid.Empty) return new Dictionary<string, object?>();
|
||||
|
||||
context.Items.TryGetValue("CurrentUser", out var currentUserValue);
|
||||
var currentUser = currentUserValue as Account;
|
||||
List<Guid> userFriends = [];
|
||||
if (currentUser != null)
|
||||
{
|
||||
var friendsResponse = await accounts.ListFriendsAsync(new ListRelationshipSimpleRequest
|
||||
{ AccountId = currentUser.Id });
|
||||
userFriends = friendsResponse.AccountsId.Select(Guid.Parse).ToList();
|
||||
}
|
||||
|
||||
var userPublishers = currentUser is null ? [] : await pub.GetUserPublishers(Guid.Parse(currentUser.Id));
|
||||
|
||||
var post = await db.Posts
|
||||
.Where(e => e.Id == postId)
|
||||
.Include(e => e.Publisher)
|
||||
.Include(e => e.Tags)
|
||||
.Include(e => e.Categories)
|
||||
.FilterWithVisibility(currentUser, userFriends, userPublishers)
|
||||
.FirstOrDefaultAsync();
|
||||
post = await ps.LoadPostInfo(post, currentUser);
|
||||
|
||||
// Track view - use the account ID as viewer ID if user is logged in
|
||||
await ps.IncreaseViewCount(post.Id, currentUser?.Id);
|
||||
|
||||
var og = OpenGraph.MakeGraph(
|
||||
title: post.Title ?? $"Post from {post.Publisher.Name}",
|
||||
type: "article",
|
||||
image: $"{_siteUrl}/cgi/drive/files/{post.Publisher.Background?.Id}?original=true",
|
||||
url: $"{_siteUrl}/@{slug}",
|
||||
description: post.Description ?? post.Content?[..80] ?? "Posted with some media",
|
||||
siteName: "Solar Network"
|
||||
);
|
||||
|
||||
return new Dictionary<string, object?>()
|
||||
{
|
||||
["Post"] = post,
|
||||
["OpenGraph"] = og
|
||||
};
|
||||
}
|
||||
}
|
@@ -1,7 +1,9 @@
|
||||
using DysonNetwork.Shared.Auth;
|
||||
using DysonNetwork.Shared.Http;
|
||||
using DysonNetwork.Shared.PageData;
|
||||
using DysonNetwork.Shared.Registry;
|
||||
using DysonNetwork.Sphere;
|
||||
using DysonNetwork.Sphere.PageData;
|
||||
using DysonNetwork.Sphere.Startup;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
@@ -33,6 +35,8 @@ builder.Services.AddAppBusinessServices(builder.Configuration);
|
||||
// Add scheduled jobs
|
||||
builder.Services.AddAppScheduledJobs();
|
||||
|
||||
builder.Services.AddTransient<IPageDataProvider, PostPageData>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Run database migrations
|
||||
|
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"Debug": true,
|
||||
"BaseUrl": "http://localhost:5071",
|
||||
"SiteUrl": "https://solian.app",
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
|
@@ -54,6 +54,7 @@
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFieldMask_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F331aca3f6f414013b09964063341351379060_003F68_003Fc6da3cbf_003FFieldMask_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AFileResult_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F0b5acdd962e549369896cece0026e556214600_003F8c_003F9f6e3f4f_003FFileResult_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AForwardedHeaders_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fcfe5737f9bb84738979cbfedd11822a8ea00_003F50_003F9a335f87_003FForwardedHeaders_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AHttpContext_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fc181aff8c6ec418494a7efcfec578fc154e00_003Fd0_003Fcc905531_003FHttpContext_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AHttpRequestHeaders_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb904f9896c4049fabd596decf1be9c381dc400_003F32_003F906beb77_003FHttpRequestHeaders_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AHttpStatusCode_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb3f2e07d4b3f4b42a41fbcf3137e534f3be00_003Fe2_003F215f9441_003FHttpStatusCode_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AHttpTransformer_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fbf3f51607a3e4e76b5b91640cd7409195c430_003F8a_003Fd9fba048_003FHttpTransformer_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||
|
Reference in New Issue
Block a user