From fc6edd7378af3d2814e42b43a8fcf7f1b2cb6fa8 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Thu, 10 Jul 2025 14:18:02 +0800 Subject: [PATCH] :boom: Add /api prefix for json endpoints with redirect --- .../Account/AccountController.cs | 2 +- .../Account/AccountCurrentController.cs | 2 +- .../Account/MagicSpellController.cs | 2 +- .../Account/NotificationController.cs | 2 +- .../Account/RelationshipController.cs | 2 +- .../Activity/ActivityController.cs | 2 +- DysonNetwork.Sphere/Auth/AuthController.cs | 2 +- .../Controllers/OidcProviderController.cs | 2 +- .../Auth/OpenId/ConnectionController.cs | 4 +- .../Auth/OpenId/OidcController.cs | 2 +- DysonNetwork.Sphere/Chat/ChatController.cs | 2 +- .../Chat/ChatRoomController.cs | 2 +- .../Chat/RealtimeCallController.cs | 2 +- .../Connection/ClientTypeMiddleware.cs | 44 ++++++++++++++ .../WebReader/WebArticleController.cs | 2 +- .../Connection/WebReader/WebFeedController.cs | 2 +- .../WebReader/WebReaderController.cs | 2 +- .../Connection/WebSocketController.cs | 4 +- .../Developer/CustomAppController.cs | 2 +- .../Developer/DeveloperController.cs | 2 +- .../Discovery/DiscoveryController.cs | 2 +- .../Pages/Account/Profile.cshtml | 2 +- .../Pages/Auth/Challenge.cshtml | 4 +- DysonNetwork.Sphere/Pages/Auth/Login.cshtml | 2 +- .../Pages/Auth/SelectFactor.cshtml | 2 +- .../Pages/Auth/VerifyFactor.cshtml | 2 +- .../Pages/Posts/PostDetail.cshtml | 22 +++++++ .../Pages/Posts/PostDetail.cshtml.cs | 44 ++++++++++++++ .../Pages/Shared/_Layout.cshtml | 4 +- DysonNetwork.Sphere/Post/PostController.cs | 6 +- .../Publisher/PublisherController.cs | 2 +- .../PublisherSubscriptionController.cs | 2 +- .../Realm/RealmChatController.cs | 2 +- DysonNetwork.Sphere/Realm/RealmController.cs | 2 +- .../Safety/AbuseReportController.cs | 2 +- .../Startup/ApplicationConfiguration.cs | 2 + .../Sticker/StickerController.cs | 2 +- DysonNetwork.Sphere/Storage/FileController.cs | 2 +- DysonNetwork.Sphere/Wallet/OrderController.cs | 2 +- .../Wallet/SubscriptionController.cs | 2 +- .../Wallet/WalletController.cs | 2 +- DysonNetwork.Sphere/wwwroot/css/styles.css | 59 +++++++++++++++++++ DysonNetwork.sln.DotSettings.user | 1 + 43 files changed, 217 insertions(+), 41 deletions(-) create mode 100644 DysonNetwork.Sphere/Connection/ClientTypeMiddleware.cs create mode 100644 DysonNetwork.Sphere/Pages/Posts/PostDetail.cshtml create mode 100644 DysonNetwork.Sphere/Pages/Posts/PostDetail.cshtml.cs diff --git a/DysonNetwork.Sphere/Account/AccountController.cs b/DysonNetwork.Sphere/Account/AccountController.cs index 29c4871..1e68f22 100644 --- a/DysonNetwork.Sphere/Account/AccountController.cs +++ b/DysonNetwork.Sphere/Account/AccountController.cs @@ -11,7 +11,7 @@ using System.Collections.Generic; namespace DysonNetwork.Sphere.Account; [ApiController] -[Route("/accounts")] +[Route("/api/accounts")] public class AccountController( AppDatabase db, AuthService auth, diff --git a/DysonNetwork.Sphere/Account/AccountCurrentController.cs b/DysonNetwork.Sphere/Account/AccountCurrentController.cs index 453b285..2230006 100644 --- a/DysonNetwork.Sphere/Account/AccountCurrentController.cs +++ b/DysonNetwork.Sphere/Account/AccountCurrentController.cs @@ -12,7 +12,7 @@ namespace DysonNetwork.Sphere.Account; [Authorize] [ApiController] -[Route("/accounts/me")] +[Route("/api/accounts/me")] public class AccountCurrentController( AppDatabase db, AccountService accounts, diff --git a/DysonNetwork.Sphere/Account/MagicSpellController.cs b/DysonNetwork.Sphere/Account/MagicSpellController.cs index d29bb7d..a98f882 100644 --- a/DysonNetwork.Sphere/Account/MagicSpellController.cs +++ b/DysonNetwork.Sphere/Account/MagicSpellController.cs @@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Mvc; namespace DysonNetwork.Sphere.Account; [ApiController] -[Route("/spells")] +[Route("/api/spells")] public class MagicSpellController(AppDatabase db, MagicSpellService sp) : ControllerBase { [HttpPost("{spellId:guid}/resend")] diff --git a/DysonNetwork.Sphere/Account/NotificationController.cs b/DysonNetwork.Sphere/Account/NotificationController.cs index 0e18633..01d9a58 100644 --- a/DysonNetwork.Sphere/Account/NotificationController.cs +++ b/DysonNetwork.Sphere/Account/NotificationController.cs @@ -9,7 +9,7 @@ using NodaTime; namespace DysonNetwork.Sphere.Account; [ApiController] -[Route("/notifications")] +[Route("/api/notifications")] public class NotificationController(AppDatabase db, NotificationService nty) : ControllerBase { [HttpGet("count")] diff --git a/DysonNetwork.Sphere/Account/RelationshipController.cs b/DysonNetwork.Sphere/Account/RelationshipController.cs index 5b0a2f4..d91c43a 100644 --- a/DysonNetwork.Sphere/Account/RelationshipController.cs +++ b/DysonNetwork.Sphere/Account/RelationshipController.cs @@ -7,7 +7,7 @@ using NodaTime; namespace DysonNetwork.Sphere.Account; [ApiController] -[Route("/relationships")] +[Route("/api/relationships")] public class RelationshipController(AppDatabase db, RelationshipService rels) : ControllerBase { [HttpGet] diff --git a/DysonNetwork.Sphere/Activity/ActivityController.cs b/DysonNetwork.Sphere/Activity/ActivityController.cs index 3cd8d67..10db5a2 100644 --- a/DysonNetwork.Sphere/Activity/ActivityController.cs +++ b/DysonNetwork.Sphere/Activity/ActivityController.cs @@ -8,7 +8,7 @@ namespace DysonNetwork.Sphere.Activity; /// Activity is a universal feed that contains multiple kinds of data. Personalized and generated dynamically. /// [ApiController] -[Route("/activities")] +[Route("/api/activities")] public class ActivityController( ActivityService acts ) : ControllerBase diff --git a/DysonNetwork.Sphere/Auth/AuthController.cs b/DysonNetwork.Sphere/Auth/AuthController.cs index 15b99bc..e34769c 100644 --- a/DysonNetwork.Sphere/Auth/AuthController.cs +++ b/DysonNetwork.Sphere/Auth/AuthController.cs @@ -11,7 +11,7 @@ using DysonNetwork.Sphere.Connection; namespace DysonNetwork.Sphere.Auth; [ApiController] -[Route("/auth")] +[Route("/api/auth")] public class AuthController( AppDatabase db, AccountService accounts, diff --git a/DysonNetwork.Sphere/Auth/OidcProvider/Controllers/OidcProviderController.cs b/DysonNetwork.Sphere/Auth/OidcProvider/Controllers/OidcProviderController.cs index ce635c8..847925c 100644 --- a/DysonNetwork.Sphere/Auth/OidcProvider/Controllers/OidcProviderController.cs +++ b/DysonNetwork.Sphere/Auth/OidcProvider/Controllers/OidcProviderController.cs @@ -14,7 +14,7 @@ using NodaTime; namespace DysonNetwork.Sphere.Auth.OidcProvider.Controllers; -[Route("/auth/open")] +[Route("/api/auth/open")] [ApiController] public class OidcProviderController( AppDatabase db, diff --git a/DysonNetwork.Sphere/Auth/OpenId/ConnectionController.cs b/DysonNetwork.Sphere/Auth/OpenId/ConnectionController.cs index fbf9f5a..49f11ee 100644 --- a/DysonNetwork.Sphere/Auth/OpenId/ConnectionController.cs +++ b/DysonNetwork.Sphere/Auth/OpenId/ConnectionController.cs @@ -8,7 +8,7 @@ using NodaTime; namespace DysonNetwork.Sphere.Auth.OpenId; [ApiController] -[Route("/accounts/me/connections")] +[Route("/api/accounts/me/connections")] [Authorize] public class ConnectionController( AppDatabase db, @@ -164,7 +164,7 @@ public class ConnectionController( } [AllowAnonymous] - [Route("/auth/callback/{provider}")] + [Route("/api/auth/callback/{provider}")] [HttpGet, HttpPost] public async Task HandleCallback([FromRoute] string provider) { diff --git a/DysonNetwork.Sphere/Auth/OpenId/OidcController.cs b/DysonNetwork.Sphere/Auth/OpenId/OidcController.cs index a868ef5..4324011 100644 --- a/DysonNetwork.Sphere/Auth/OpenId/OidcController.cs +++ b/DysonNetwork.Sphere/Auth/OpenId/OidcController.cs @@ -8,7 +8,7 @@ using NodaTime; namespace DysonNetwork.Sphere.Auth.OpenId; [ApiController] -[Route("/auth/login")] +[Route("/api/auth/login")] public class OidcController( IServiceProvider serviceProvider, AppDatabase db, diff --git a/DysonNetwork.Sphere/Chat/ChatController.cs b/DysonNetwork.Sphere/Chat/ChatController.cs index 822f812..212592e 100644 --- a/DysonNetwork.Sphere/Chat/ChatController.cs +++ b/DysonNetwork.Sphere/Chat/ChatController.cs @@ -9,7 +9,7 @@ using Microsoft.EntityFrameworkCore; namespace DysonNetwork.Sphere.Chat; [ApiController] -[Route("/chat")] +[Route("/api/chat")] public partial class ChatController(AppDatabase db, ChatService cs, ChatRoomService crs) : ControllerBase { public class MarkMessageReadRequest diff --git a/DysonNetwork.Sphere/Chat/ChatRoomController.cs b/DysonNetwork.Sphere/Chat/ChatRoomController.cs index 6961590..8d8b0c4 100644 --- a/DysonNetwork.Sphere/Chat/ChatRoomController.cs +++ b/DysonNetwork.Sphere/Chat/ChatRoomController.cs @@ -13,7 +13,7 @@ using NodaTime; namespace DysonNetwork.Sphere.Chat; [ApiController] -[Route("/chat")] +[Route("/api/chat")] public class ChatRoomController( AppDatabase db, FileReferenceService fileRefService, diff --git a/DysonNetwork.Sphere/Chat/RealtimeCallController.cs b/DysonNetwork.Sphere/Chat/RealtimeCallController.cs index 5694dc5..e26bcd9 100644 --- a/DysonNetwork.Sphere/Chat/RealtimeCallController.cs +++ b/DysonNetwork.Sphere/Chat/RealtimeCallController.cs @@ -13,7 +13,7 @@ public class RealtimeChatConfiguration } [ApiController] -[Route("/chat/realtime")] +[Route("/api/chat/realtime")] public class RealtimeCallController( IConfiguration configuration, AppDatabase db, diff --git a/DysonNetwork.Sphere/Connection/ClientTypeMiddleware.cs b/DysonNetwork.Sphere/Connection/ClientTypeMiddleware.cs new file mode 100644 index 0000000..79a75ad --- /dev/null +++ b/DysonNetwork.Sphere/Connection/ClientTypeMiddleware.cs @@ -0,0 +1,44 @@ +using Azure.Core; + +namespace DysonNetwork.Sphere.Connection; + +public class ClientTypeMiddleware(RequestDelegate next) +{ + public async Task Invoke(HttpContext context) + { + var headers = context.Request.Headers; + bool isWebPage; + + // Priority 1: Check for custom header + if (headers.TryGetValue("X-Client", out var clientType)) + { + isWebPage = clientType.ToString().Length == 0; + } + else + { + var userAgent = headers["User-Agent"].ToString(); + var accept = headers["Accept"].ToString(); + + // Priority 2: Check known app User-Agent (backward compatibility) + if (!string.IsNullOrEmpty(userAgent) && userAgent.Contains("Solian")) + isWebPage = false; + // Priority 3: Accept header can help infer intent + else if (!string.IsNullOrEmpty(accept) && accept.Contains("text/html")) + isWebPage = true; + else if (!string.IsNullOrEmpty(accept) && accept.Contains("application/json")) + isWebPage = false; + else + isWebPage = true; + } + + context.Items["IsWebPage"] = isWebPage; + + if (!isWebPage && !context.Request.Path.StartsWithSegments("/api")) + context.Response.Redirect( + "/api" + context.Request.Path.Value, + permanent: false + ); + else + await next(context); + } +} \ No newline at end of file diff --git a/DysonNetwork.Sphere/Connection/WebReader/WebArticleController.cs b/DysonNetwork.Sphere/Connection/WebReader/WebArticleController.cs index ea7f8be..96b73b6 100644 --- a/DysonNetwork.Sphere/Connection/WebReader/WebArticleController.cs +++ b/DysonNetwork.Sphere/Connection/WebReader/WebArticleController.cs @@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore; namespace DysonNetwork.Sphere.Connection.WebReader; [ApiController] -[Route("/feeds/articles")] +[Route("/api/feeds/articles")] public class WebArticleController(AppDatabase db) : ControllerBase { /// diff --git a/DysonNetwork.Sphere/Connection/WebReader/WebFeedController.cs b/DysonNetwork.Sphere/Connection/WebReader/WebFeedController.cs index a93d725..4001447 100644 --- a/DysonNetwork.Sphere/Connection/WebReader/WebFeedController.cs +++ b/DysonNetwork.Sphere/Connection/WebReader/WebFeedController.cs @@ -7,7 +7,7 @@ namespace DysonNetwork.Sphere.Connection.WebReader; [Authorize] [ApiController] -[Route("/publishers/{pubName}/feeds")] +[Route("/api/publishers/{pubName}/feeds")] public class WebFeedController(WebFeedService webFeed, PublisherService ps) : ControllerBase { public record WebFeedRequest( diff --git a/DysonNetwork.Sphere/Connection/WebReader/WebReaderController.cs b/DysonNetwork.Sphere/Connection/WebReader/WebReaderController.cs index c32bbb4..013e790 100644 --- a/DysonNetwork.Sphere/Connection/WebReader/WebReaderController.cs +++ b/DysonNetwork.Sphere/Connection/WebReader/WebReaderController.cs @@ -9,7 +9,7 @@ namespace DysonNetwork.Sphere.Connection.WebReader; /// Controller for web scraping and link preview services /// [ApiController] -[Route("/scrap")] +[Route("/api/scrap")] [EnableRateLimiting("fixed")] public class WebReaderController(WebReaderService reader, ILogger logger) : ControllerBase diff --git a/DysonNetwork.Sphere/Connection/WebSocketController.cs b/DysonNetwork.Sphere/Connection/WebSocketController.cs index 3bf61cf..484f12e 100644 --- a/DysonNetwork.Sphere/Connection/WebSocketController.cs +++ b/DysonNetwork.Sphere/Connection/WebSocketController.cs @@ -8,10 +8,10 @@ using Swashbuckle.AspNetCore.Annotations; namespace DysonNetwork.Sphere.Connection; [ApiController] -[Route("/ws")] +[Route("/api/ws")] public class WebSocketController(WebSocketService ws, ILogger logger) : ControllerBase { - [Route("/ws")] + [Route("/api/ws")] [Authorize] [SwaggerIgnore] public async Task TheGateway() diff --git a/DysonNetwork.Sphere/Developer/CustomAppController.cs b/DysonNetwork.Sphere/Developer/CustomAppController.cs index 4dfc152..4b354d4 100644 --- a/DysonNetwork.Sphere/Developer/CustomAppController.cs +++ b/DysonNetwork.Sphere/Developer/CustomAppController.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Mvc; namespace DysonNetwork.Sphere.Developer; [ApiController] -[Route("/developers/{pubName}/apps")] +[Route("/api/developers/{pubName}/apps")] public class CustomAppController(CustomAppService customApps, PublisherService ps) : ControllerBase { public record CustomAppRequest( diff --git a/DysonNetwork.Sphere/Developer/DeveloperController.cs b/DysonNetwork.Sphere/Developer/DeveloperController.cs index 14947c7..8408141 100644 --- a/DysonNetwork.Sphere/Developer/DeveloperController.cs +++ b/DysonNetwork.Sphere/Developer/DeveloperController.cs @@ -9,7 +9,7 @@ using NodaTime; namespace DysonNetwork.Sphere.Developer; [ApiController] -[Route("/developers")] +[Route("/api/developers")] public class DeveloperController( AppDatabase db, PublisherService ps, diff --git a/DysonNetwork.Sphere/Discovery/DiscoveryController.cs b/DysonNetwork.Sphere/Discovery/DiscoveryController.cs index baede36..db450d4 100644 --- a/DysonNetwork.Sphere/Discovery/DiscoveryController.cs +++ b/DysonNetwork.Sphere/Discovery/DiscoveryController.cs @@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Mvc; namespace DysonNetwork.Sphere.Discovery; [ApiController] -[Route("/discovery")] +[Route("/api/discovery")] public class DiscoveryController(DiscoveryService discoveryService) : ControllerBase { [HttpGet("realms")] diff --git a/DysonNetwork.Sphere/Pages/Account/Profile.cshtml b/DysonNetwork.Sphere/Pages/Account/Profile.cshtml index 86e6d6a..abdbcef 100644 --- a/DysonNetwork.Sphere/Pages/Account/Profile.cshtml +++ b/DysonNetwork.Sphere/Pages/Account/Profile.cshtml @@ -1,4 +1,4 @@ -@page "/web/account/profile" +@page "//account/profile" @model DysonNetwork.Sphere.Pages.Account.ProfileModel @{ ViewData["Title"] = "Profile"; diff --git a/DysonNetwork.Sphere/Pages/Auth/Challenge.cshtml b/DysonNetwork.Sphere/Pages/Auth/Challenge.cshtml index 657bd21..d315620 100644 --- a/DysonNetwork.Sphere/Pages/Auth/Challenge.cshtml +++ b/DysonNetwork.Sphere/Pages/Auth/Challenge.cshtml @@ -1,9 +1,9 @@ -@page "/web/auth/challenge/{id:guid}" +@page "//auth/challenge/{id:guid}" @model DysonNetwork.Sphere.Pages.Auth.ChallengeModel @{ // This page is kept for backward compatibility // It will automatically redirect to the new SelectFactor page - Response.Redirect($"/web/auth/challenge/{Model.Id}/select-factor"); + Response.Redirect($"//auth/challenge/{Model.Id}/select-factor"); }
diff --git a/DysonNetwork.Sphere/Pages/Auth/Login.cshtml b/DysonNetwork.Sphere/Pages/Auth/Login.cshtml index f3d64f1..96e3618 100644 --- a/DysonNetwork.Sphere/Pages/Auth/Login.cshtml +++ b/DysonNetwork.Sphere/Pages/Auth/Login.cshtml @@ -1,4 +1,4 @@ -@page "/web/auth/login" +@page "//auth/login" @model DysonNetwork.Sphere.Pages.Auth.LoginModel @{ ViewData["Title"] = "Login | Solar Network"; diff --git a/DysonNetwork.Sphere/Pages/Auth/SelectFactor.cshtml b/DysonNetwork.Sphere/Pages/Auth/SelectFactor.cshtml index 7f07131..61af7d1 100644 --- a/DysonNetwork.Sphere/Pages/Auth/SelectFactor.cshtml +++ b/DysonNetwork.Sphere/Pages/Auth/SelectFactor.cshtml @@ -1,4 +1,4 @@ -@page "/web/auth/challenge/{id:guid}/select-factor" +@page "//auth/challenge/{id:guid}/select-factor" @using DysonNetwork.Sphere.Account @model DysonNetwork.Sphere.Pages.Auth.SelectFactorModel @{ diff --git a/DysonNetwork.Sphere/Pages/Auth/VerifyFactor.cshtml b/DysonNetwork.Sphere/Pages/Auth/VerifyFactor.cshtml index cee24f7..c02dff9 100644 --- a/DysonNetwork.Sphere/Pages/Auth/VerifyFactor.cshtml +++ b/DysonNetwork.Sphere/Pages/Auth/VerifyFactor.cshtml @@ -1,4 +1,4 @@ -@page "/web/auth/challenge/{id:guid}/verify/{factorId:guid}" +@page "//auth/challenge/{id:guid}/verify/{factorId:guid}" @using DysonNetwork.Sphere.Account @model DysonNetwork.Sphere.Pages.Auth.VerifyFactorModel @{ diff --git a/DysonNetwork.Sphere/Pages/Posts/PostDetail.cshtml b/DysonNetwork.Sphere/Pages/Posts/PostDetail.cshtml new file mode 100644 index 0000000..07442c1 --- /dev/null +++ b/DysonNetwork.Sphere/Pages/Posts/PostDetail.cshtml @@ -0,0 +1,22 @@ +@page "/posts/{PostId:guid}" +@model DysonNetwork.Sphere.Pages.Posts.PostDetailModel +@{ + ViewData["Title"] = Model.Post?.Title + " | Solar Network"; +} + +
+ @if (Model.Post != null) + { +

@Model.Post.Title

+

Created at: @Model.Post.CreatedAt

+
+

@Model.Post.Content

+
+ } + else + { +
+ Post not found. +
+ } +
\ No newline at end of file diff --git a/DysonNetwork.Sphere/Pages/Posts/PostDetail.cshtml.cs b/DysonNetwork.Sphere/Pages/Posts/PostDetail.cshtml.cs new file mode 100644 index 0000000..825a2e6 --- /dev/null +++ b/DysonNetwork.Sphere/Pages/Posts/PostDetail.cshtml.cs @@ -0,0 +1,44 @@ +using DysonNetwork.Sphere.Account; +using DysonNetwork.Sphere.Post; +using DysonNetwork.Sphere.Publisher; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.EntityFrameworkCore; + +namespace DysonNetwork.Sphere.Pages.Posts; + +public class PostDetailModel( + AppDatabase db, + PublisherService pub, + RelationshipService rels +) : PageModel +{ + [BindProperty(SupportsGet = true)] + public Guid PostId { get; set; } + + public Post.Post? Post { get; set; } + + public async Task OnGetAsync() + { + if (PostId == Guid.Empty) + return NotFound(); + + HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue); + var currentUser = currentUserValue as Sphere.Account.Account; + var userFriends = currentUser is null ? [] : await rels.ListAccountFriends(currentUser); + var userPublishers = currentUser is null ? [] : await pub.GetUserPublishers(currentUser.Id); + + 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(); + + if (Post == null) + return NotFound(); + + return Page(); + } +} \ No newline at end of file diff --git a/DysonNetwork.Sphere/Pages/Shared/_Layout.cshtml b/DysonNetwork.Sphere/Pages/Shared/_Layout.cshtml index 160b225..cb7893f 100644 --- a/DysonNetwork.Sphere/Pages/Shared/_Layout.cshtml +++ b/DysonNetwork.Sphere/Pages/Shared/_Layout.cshtml @@ -26,7 +26,7 @@ @if (Context.Request.Cookies.TryGetValue(AuthConstants.CookieTokenName, out _)) {
  • - + account_circle
  • @@ -43,7 +43,7 @@ else {
  • - login + login
  • } diff --git a/DysonNetwork.Sphere/Post/PostController.cs b/DysonNetwork.Sphere/Post/PostController.cs index 87b731e..34da748 100644 --- a/DysonNetwork.Sphere/Post/PostController.cs +++ b/DysonNetwork.Sphere/Post/PostController.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.Text.Json; using DysonNetwork.Sphere.Account; +using DysonNetwork.Sphere.Pages.Posts; using DysonNetwork.Sphere.Permission; using DysonNetwork.Sphere.Publisher; using DysonNetwork.Sphere.Storage; @@ -13,7 +14,7 @@ using NpgsqlTypes; namespace DysonNetwork.Sphere.Post; [ApiController] -[Route("/posts")] +[Route("/api/posts")] public class PostController( AppDatabase db, PostService ps, @@ -65,6 +66,9 @@ public class PostController( [HttpGet("{id:guid}")] public async Task> GetPost(Guid id) { + if (HttpContext.Items["IsWebPage"] as bool? ?? true) + return RedirectToPage("/Posts/PostDetail", new { PostId = id }); + HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue); var currentUser = currentUserValue as Account.Account; var userFriends = currentUser is null ? [] : await rels.ListAccountFriends(currentUser); diff --git a/DysonNetwork.Sphere/Publisher/PublisherController.cs b/DysonNetwork.Sphere/Publisher/PublisherController.cs index 5479e02..36dece4 100644 --- a/DysonNetwork.Sphere/Publisher/PublisherController.cs +++ b/DysonNetwork.Sphere/Publisher/PublisherController.cs @@ -11,7 +11,7 @@ using NodaTime; namespace DysonNetwork.Sphere.Publisher; [ApiController] -[Route("/publishers")] +[Route("/api/publishers")] public class PublisherController( AppDatabase db, PublisherService ps, diff --git a/DysonNetwork.Sphere/Publisher/PublisherSubscriptionController.cs b/DysonNetwork.Sphere/Publisher/PublisherSubscriptionController.cs index 97c225e..727386f 100644 --- a/DysonNetwork.Sphere/Publisher/PublisherSubscriptionController.cs +++ b/DysonNetwork.Sphere/Publisher/PublisherSubscriptionController.cs @@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore; namespace DysonNetwork.Sphere.Publisher; [ApiController] -[Route("/publishers")] +[Route("/api/publishers")] public class PublisherSubscriptionController( PublisherSubscriptionService subs, AppDatabase db, diff --git a/DysonNetwork.Sphere/Realm/RealmChatController.cs b/DysonNetwork.Sphere/Realm/RealmChatController.cs index b884c17..90d8fcf 100644 --- a/DysonNetwork.Sphere/Realm/RealmChatController.cs +++ b/DysonNetwork.Sphere/Realm/RealmChatController.cs @@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore; namespace DysonNetwork.Sphere.Realm; [ApiController] -[Route("/realms/{slug}")] +[Route("/api/realms/{slug}")] public class RealmChatController(AppDatabase db, RealmService rs) : ControllerBase { [HttpGet("chat")] diff --git a/DysonNetwork.Sphere/Realm/RealmController.cs b/DysonNetwork.Sphere/Realm/RealmController.cs index 2b12311..6e741c2 100644 --- a/DysonNetwork.Sphere/Realm/RealmController.cs +++ b/DysonNetwork.Sphere/Realm/RealmController.cs @@ -9,7 +9,7 @@ using NodaTime; namespace DysonNetwork.Sphere.Realm; [ApiController] -[Route("/realms")] +[Route("/api/realms")] public class RealmController( AppDatabase db, RealmService rs, diff --git a/DysonNetwork.Sphere/Safety/AbuseReportController.cs b/DysonNetwork.Sphere/Safety/AbuseReportController.cs index 5f8cf83..e1c9ed6 100644 --- a/DysonNetwork.Sphere/Safety/AbuseReportController.cs +++ b/DysonNetwork.Sphere/Safety/AbuseReportController.cs @@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Mvc; namespace DysonNetwork.Sphere.Safety; [ApiController] -[Route("/safety/reports")] +[Route("/api/safety/reports")] public class AbuseReportController( SafetyService safety ) : ControllerBase diff --git a/DysonNetwork.Sphere/Startup/ApplicationConfiguration.cs b/DysonNetwork.Sphere/Startup/ApplicationConfiguration.cs index f2e6c2c..d71d898 100644 --- a/DysonNetwork.Sphere/Startup/ApplicationConfiguration.cs +++ b/DysonNetwork.Sphere/Startup/ApplicationConfiguration.cs @@ -1,4 +1,5 @@ using System.Net; +using DysonNetwork.Sphere.Connection; using DysonNetwork.Sphere.Permission; using DysonNetwork.Sphere.Storage; using Microsoft.AspNetCore.HttpOverrides; @@ -14,6 +15,7 @@ public static class ApplicationConfiguration { app.MapMetrics(); app.MapOpenApi(); + app.UseMiddleware(); app.UseSwagger(); app.UseSwaggerUI(); diff --git a/DysonNetwork.Sphere/Sticker/StickerController.cs b/DysonNetwork.Sphere/Sticker/StickerController.cs index d635655..724afb4 100644 --- a/DysonNetwork.Sphere/Sticker/StickerController.cs +++ b/DysonNetwork.Sphere/Sticker/StickerController.cs @@ -9,7 +9,7 @@ using Microsoft.EntityFrameworkCore; namespace DysonNetwork.Sphere.Sticker; [ApiController] -[Route("/stickers")] +[Route("/api/stickers")] public class StickerController(AppDatabase db, StickerService st) : ControllerBase { private async Task _CheckStickerPackPermissions(Guid packId, Account.Account currentUser, diff --git a/DysonNetwork.Sphere/Storage/FileController.cs b/DysonNetwork.Sphere/Storage/FileController.cs index 58920a0..d61bdd0 100644 --- a/DysonNetwork.Sphere/Storage/FileController.cs +++ b/DysonNetwork.Sphere/Storage/FileController.cs @@ -7,7 +7,7 @@ using Minio.DataModel.Args; namespace DysonNetwork.Sphere.Storage; [ApiController] -[Route("/files")] +[Route("/api/files")] public class FileController( AppDatabase db, FileService fs, diff --git a/DysonNetwork.Sphere/Wallet/OrderController.cs b/DysonNetwork.Sphere/Wallet/OrderController.cs index a2cf8a9..7200fac 100644 --- a/DysonNetwork.Sphere/Wallet/OrderController.cs +++ b/DysonNetwork.Sphere/Wallet/OrderController.cs @@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore; namespace DysonNetwork.Sphere.Wallet; [ApiController] -[Route("/orders")] +[Route("/api/orders")] public class OrderController(PaymentService payment, AuthService auth, AppDatabase db) : ControllerBase { [HttpGet("{id:guid}")] diff --git a/DysonNetwork.Sphere/Wallet/SubscriptionController.cs b/DysonNetwork.Sphere/Wallet/SubscriptionController.cs index 12fb747..9b8ca65 100644 --- a/DysonNetwork.Sphere/Wallet/SubscriptionController.cs +++ b/DysonNetwork.Sphere/Wallet/SubscriptionController.cs @@ -8,7 +8,7 @@ using DysonNetwork.Sphere.Wallet.PaymentHandlers; namespace DysonNetwork.Sphere.Wallet; [ApiController] -[Route("/subscriptions")] +[Route("/api/subscriptions")] public class SubscriptionController(SubscriptionService subscriptions, AfdianPaymentHandler afdian, AppDatabase db) : ControllerBase { [HttpGet] diff --git a/DysonNetwork.Sphere/Wallet/WalletController.cs b/DysonNetwork.Sphere/Wallet/WalletController.cs index 92d9208..6b256b9 100644 --- a/DysonNetwork.Sphere/Wallet/WalletController.cs +++ b/DysonNetwork.Sphere/Wallet/WalletController.cs @@ -7,7 +7,7 @@ using Microsoft.EntityFrameworkCore; namespace DysonNetwork.Sphere.Wallet; [ApiController] -[Route("/wallets")] +[Route("/api/wallets")] public class WalletController(AppDatabase db, WalletService ws, PaymentService payment) : ControllerBase { [HttpPost] diff --git a/DysonNetwork.Sphere/wwwroot/css/styles.css b/DysonNetwork.Sphere/wwwroot/css/styles.css index 9e53475..bf7dca3 100644 --- a/DysonNetwork.Sphere/wwwroot/css/styles.css +++ b/DysonNetwork.Sphere/wwwroot/css/styles.css @@ -7,6 +7,7 @@ --font-mono: "Noto Sans Mono", monospace; --color-blue-400: oklch(70.7% 0.165 254.624); --color-blue-600: oklch(54.6% 0.245 262.881); + --color-gray-600: oklch(44.6% 0.03 256.802); --color-black: #000; --spacing: 0.25rem; --container-xs: 20rem; @@ -1861,6 +1862,61 @@ padding-block: calc(0.25rem * 1); --mask-chat: url("data:image/svg+xml,%3csvg width='13' height='13' xmlns='http://www.w3.org/2000/svg'%3e%3cpath fill='black' d='M0 11.5004C0 13.0004 2 13.0004 2 13.0004H12H13V0.00036329L12.5 0C12.5 0 11.977 2.09572 11.8581 2.50033C11.6075 3.35237 10.9149 4.22374 9 5.50036C6 7.50036 0 10.0004 0 11.5004Z'/%3e%3c/svg%3e"); } + .prose { + :root & { + --tw-prose-body: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-body: color-mix(in oklab, var(--color-base-content) 80%, #0000); + } + --tw-prose-headings: var(--color-base-content); + --tw-prose-lead: var(--color-base-content); + --tw-prose-links: var(--color-base-content); + --tw-prose-bold: var(--color-base-content); + --tw-prose-counters: var(--color-base-content); + --tw-prose-bullets: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-bullets: color-mix(in oklab, var(--color-base-content) 50%, #0000); + } + --tw-prose-hr: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-hr: color-mix(in oklab, var(--color-base-content) 20%, #0000); + } + --tw-prose-quotes: var(--color-base-content); + --tw-prose-quote-borders: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-quote-borders: color-mix(in oklab, var(--color-base-content) 20%, #0000); + } + --tw-prose-captions: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-captions: color-mix(in oklab, var(--color-base-content) 50%, #0000); + } + --tw-prose-code: var(--color-base-content); + --tw-prose-pre-code: var(--color-neutral-content); + --tw-prose-pre-bg: var(--color-neutral); + --tw-prose-th-borders: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-th-borders: color-mix(in oklab, var(--color-base-content) 50%, #0000); + } + --tw-prose-td-borders: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-td-borders: color-mix(in oklab, var(--color-base-content) 20%, #0000); + } + --tw-prose-kbd: var(--color-base-content); + @supports (color: color-mix(in lab, red, red)) { + --tw-prose-kbd: color-mix(in oklab, var(--color-base-content) 80%, #0000); + } + :where(code):not(pre > code) { + background-color: var(--color-base-200); + border-radius: var(--radius-selector); + border: var(--border) solid var(--color-base-300); + padding-inline: 0.5em; + font-weight: inherit; + &:before, &:after { + display: none; + } + } + } + } .mask { display: inline-block; vertical-align: middle; @@ -2243,6 +2299,9 @@ .text-error { color: var(--color-error); } + .text-gray-600 { + color: var(--color-gray-600); + } .text-info { color: var(--color-info); } diff --git a/DysonNetwork.sln.DotSettings.user b/DysonNetwork.sln.DotSettings.user index 609d66f..7803d12 100644 --- a/DysonNetwork.sln.DotSettings.user +++ b/DysonNetwork.sln.DotSettings.user @@ -12,6 +12,7 @@ ForceIncluded ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded