Compare commits
	
		
			3 Commits
		
	
	
		
			3b679d6134
			...
			3ee5e5367d
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 3ee5e5367d | |||
| 85fef30c7f | |||
| e8d8dcbb2d | 
| @@ -55,6 +55,7 @@ public class AppDatabase( | |||||||
|  |  | ||||||
|     public DbSet<WebReader.WebArticle> WebArticles { get; set; } = null!; |     public DbSet<WebReader.WebArticle> WebArticles { get; set; } = null!; | ||||||
|     public DbSet<WebReader.WebFeed> WebFeeds { get; set; } = null!; |     public DbSet<WebReader.WebFeed> WebFeeds { get; set; } = null!; | ||||||
|  |     public DbSet<WebReader.WebFeedSubscription> WebFeedSubscriptions { get; set; } = null!; | ||||||
|  |  | ||||||
|     protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) |     protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) | ||||||
|     { |     { | ||||||
|   | |||||||
							
								
								
									
										2002
									
								
								DysonNetwork.Sphere/Migrations/20250820060654_AddWebFeedSubscription.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										2002
									
								
								DysonNetwork.Sphere/Migrations/20250820060654_AddWebFeedSubscription.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1,50 @@ | |||||||
|  | using System; | ||||||
|  | using Microsoft.EntityFrameworkCore.Migrations; | ||||||
|  | using NodaTime; | ||||||
|  |  | ||||||
|  | #nullable disable | ||||||
|  |  | ||||||
|  | namespace DysonNetwork.Sphere.Migrations | ||||||
|  | { | ||||||
|  |     /// <inheritdoc /> | ||||||
|  |     public partial class AddWebFeedSubscription : Migration | ||||||
|  |     { | ||||||
|  |         /// <inheritdoc /> | ||||||
|  |         protected override void Up(MigrationBuilder migrationBuilder) | ||||||
|  |         { | ||||||
|  |             migrationBuilder.CreateTable( | ||||||
|  |                 name: "web_feed_subscriptions", | ||||||
|  |                 columns: table => new | ||||||
|  |                 { | ||||||
|  |                     id = table.Column<Guid>(type: "uuid", nullable: false), | ||||||
|  |                     feed_id = table.Column<Guid>(type: "uuid", nullable: false), | ||||||
|  |                     account_id = table.Column<Guid>(type: "uuid", nullable: false), | ||||||
|  |                     created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false), | ||||||
|  |                     updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false), | ||||||
|  |                     deleted_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true) | ||||||
|  |                 }, | ||||||
|  |                 constraints: table => | ||||||
|  |                 { | ||||||
|  |                     table.PrimaryKey("pk_web_feed_subscriptions", x => x.id); | ||||||
|  |                     table.ForeignKey( | ||||||
|  |                         name: "fk_web_feed_subscriptions_web_feeds_feed_id", | ||||||
|  |                         column: x => x.feed_id, | ||||||
|  |                         principalTable: "web_feeds", | ||||||
|  |                         principalColumn: "id", | ||||||
|  |                         onDelete: ReferentialAction.Cascade); | ||||||
|  |                 }); | ||||||
|  |  | ||||||
|  |             migrationBuilder.CreateIndex( | ||||||
|  |                 name: "ix_web_feed_subscriptions_feed_id", | ||||||
|  |                 table: "web_feed_subscriptions", | ||||||
|  |                 column: "feed_id"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /// <inheritdoc /> | ||||||
|  |         protected override void Down(MigrationBuilder migrationBuilder) | ||||||
|  |         { | ||||||
|  |             migrationBuilder.DropTable( | ||||||
|  |                 name: "web_feed_subscriptions"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1458,6 +1458,42 @@ namespace DysonNetwork.Sphere.Migrations | |||||||
|                     b.ToTable("web_feeds", (string)null); |                     b.ToTable("web_feeds", (string)null); | ||||||
|                 }); |                 }); | ||||||
|  |  | ||||||
|  |             modelBuilder.Entity("DysonNetwork.Sphere.WebReader.WebFeedSubscription", b => | ||||||
|  |                 { | ||||||
|  |                     b.Property<Guid>("Id") | ||||||
|  |                         .ValueGeneratedOnAdd() | ||||||
|  |                         .HasColumnType("uuid") | ||||||
|  |                         .HasColumnName("id"); | ||||||
|  |  | ||||||
|  |                     b.Property<Guid>("AccountId") | ||||||
|  |                         .HasColumnType("uuid") | ||||||
|  |                         .HasColumnName("account_id"); | ||||||
|  |  | ||||||
|  |                     b.Property<Instant>("CreatedAt") | ||||||
|  |                         .HasColumnType("timestamp with time zone") | ||||||
|  |                         .HasColumnName("created_at"); | ||||||
|  |  | ||||||
|  |                     b.Property<Instant?>("DeletedAt") | ||||||
|  |                         .HasColumnType("timestamp with time zone") | ||||||
|  |                         .HasColumnName("deleted_at"); | ||||||
|  |  | ||||||
|  |                     b.Property<Guid>("FeedId") | ||||||
|  |                         .HasColumnType("uuid") | ||||||
|  |                         .HasColumnName("feed_id"); | ||||||
|  |  | ||||||
|  |                     b.Property<Instant>("UpdatedAt") | ||||||
|  |                         .HasColumnType("timestamp with time zone") | ||||||
|  |                         .HasColumnName("updated_at"); | ||||||
|  |  | ||||||
|  |                     b.HasKey("Id") | ||||||
|  |                         .HasName("pk_web_feed_subscriptions"); | ||||||
|  |  | ||||||
|  |                     b.HasIndex("FeedId") | ||||||
|  |                         .HasDatabaseName("ix_web_feed_subscriptions_feed_id"); | ||||||
|  |  | ||||||
|  |                     b.ToTable("web_feed_subscriptions", (string)null); | ||||||
|  |                 }); | ||||||
|  |  | ||||||
|             modelBuilder.Entity("PostPostCategory", b => |             modelBuilder.Entity("PostPostCategory", b => | ||||||
|                 { |                 { | ||||||
|                     b.Property<Guid>("CategoriesId") |                     b.Property<Guid>("CategoriesId") | ||||||
| @@ -1808,7 +1844,7 @@ namespace DysonNetwork.Sphere.Migrations | |||||||
|             modelBuilder.Entity("DysonNetwork.Sphere.Sticker.StickerPackOwnership", b => |             modelBuilder.Entity("DysonNetwork.Sphere.Sticker.StickerPackOwnership", b => | ||||||
|                 { |                 { | ||||||
|                     b.HasOne("DysonNetwork.Sphere.Sticker.StickerPack", "Pack") |                     b.HasOne("DysonNetwork.Sphere.Sticker.StickerPack", "Pack") | ||||||
|                         .WithMany() |                         .WithMany("Ownerships") | ||||||
|                         .HasForeignKey("PackId") |                         .HasForeignKey("PackId") | ||||||
|                         .OnDelete(DeleteBehavior.Cascade) |                         .OnDelete(DeleteBehavior.Cascade) | ||||||
|                         .IsRequired() |                         .IsRequired() | ||||||
| @@ -1841,6 +1877,18 @@ namespace DysonNetwork.Sphere.Migrations | |||||||
|                     b.Navigation("Publisher"); |                     b.Navigation("Publisher"); | ||||||
|                 }); |                 }); | ||||||
|  |  | ||||||
|  |             modelBuilder.Entity("DysonNetwork.Sphere.WebReader.WebFeedSubscription", b => | ||||||
|  |                 { | ||||||
|  |                     b.HasOne("DysonNetwork.Sphere.WebReader.WebFeed", "Feed") | ||||||
|  |                         .WithMany() | ||||||
|  |                         .HasForeignKey("FeedId") | ||||||
|  |                         .OnDelete(DeleteBehavior.Cascade) | ||||||
|  |                         .IsRequired() | ||||||
|  |                         .HasConstraintName("fk_web_feed_subscriptions_web_feeds_feed_id"); | ||||||
|  |  | ||||||
|  |                     b.Navigation("Feed"); | ||||||
|  |                 }); | ||||||
|  |  | ||||||
|             modelBuilder.Entity("PostPostCategory", b => |             modelBuilder.Entity("PostPostCategory", b => | ||||||
|                 { |                 { | ||||||
|                     b.HasOne("DysonNetwork.Sphere.Post.PostCategory", null) |                     b.HasOne("DysonNetwork.Sphere.Post.PostCategory", null) | ||||||
| @@ -1936,6 +1984,8 @@ namespace DysonNetwork.Sphere.Migrations | |||||||
|  |  | ||||||
|             modelBuilder.Entity("DysonNetwork.Sphere.Sticker.StickerPack", b => |             modelBuilder.Entity("DysonNetwork.Sphere.Sticker.StickerPack", b => | ||||||
|                 { |                 { | ||||||
|  |                     b.Navigation("Ownerships"); | ||||||
|  |  | ||||||
|                     b.Navigation("Stickers"); |                     b.Navigation("Stickers"); | ||||||
|                 }); |                 }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -32,7 +32,8 @@ public class StickerPack : ModelBase | |||||||
|     [MaxLength(4096)] public string Description { get; set; } = string.Empty; |     [MaxLength(4096)] public string Description { get; set; } = string.Empty; | ||||||
|     [MaxLength(128)] public string Prefix { get; set; } = null!; |     [MaxLength(128)] public string Prefix { get; set; } = null!; | ||||||
|  |  | ||||||
|     public List<Sticker> Stickers { get; set; } = new(); |     public List<Sticker> Stickers { get; set; } = []; | ||||||
|  |     [JsonIgnore] public List<StickerPackOwnership> Ownerships { get; set; } = []; | ||||||
|  |  | ||||||
|     public Guid PublisherId { get; set; } |     public Guid PublisherId { get; set; } | ||||||
|     public Publisher.Publisher Publisher { get; set; } = null!; |     public Publisher.Publisher Publisher { get; set; } = null!; | ||||||
|   | |||||||
| @@ -10,7 +10,12 @@ namespace DysonNetwork.Sphere.Sticker; | |||||||
|  |  | ||||||
| [ApiController] | [ApiController] | ||||||
| [Route("/api/stickers")] | [Route("/api/stickers")] | ||||||
| public class StickerController(AppDatabase db, StickerService st, FileService.FileServiceClient files) : ControllerBase | public class StickerController( | ||||||
|  |     AppDatabase db, | ||||||
|  |     StickerService st, | ||||||
|  |     Publisher.PublisherService ps, | ||||||
|  |     FileService.FileServiceClient files | ||||||
|  | ) : ControllerBase | ||||||
| { | { | ||||||
|     private async Task<IActionResult> _CheckStickerPackPermissions( |     private async Task<IActionResult> _CheckStickerPackPermissions( | ||||||
|         Guid packId, |         Guid packId, | ||||||
| @@ -26,12 +31,8 @@ public class StickerController(AppDatabase db, StickerService st, FileService.Fi | |||||||
|             return NotFound("Sticker pack not found"); |             return NotFound("Sticker pack not found"); | ||||||
|  |  | ||||||
|         var accountId = Guid.Parse(currentUser.Id); |         var accountId = Guid.Parse(currentUser.Id); | ||||||
|         var member = await db.PublisherMembers |         if (!await ps.IsMemberWithRole(accountId, pack.PublisherId, requiredRole)) | ||||||
|             .FirstOrDefaultAsync(m => m.AccountId == accountId && m.PublisherId == pack.PublisherId); |  | ||||||
|         if (member is null) |  | ||||||
|             return StatusCode(403, "You are not a member of this publisher"); |             return StatusCode(403, "You are not a member of this publisher"); | ||||||
|         if (member.Role < requiredRole) |  | ||||||
|             return StatusCode(403, $"You need to be at least a {requiredRole} to perform this action"); |  | ||||||
|  |  | ||||||
|         return Ok(); |         return Ok(); | ||||||
|     } |     } | ||||||
| @@ -40,19 +41,38 @@ public class StickerController(AppDatabase db, StickerService st, FileService.Fi | |||||||
|     public async Task<ActionResult<List<StickerPack>>> ListStickerPacks( |     public async Task<ActionResult<List<StickerPack>>> ListStickerPacks( | ||||||
|         [FromQuery] int offset = 0, |         [FromQuery] int offset = 0, | ||||||
|         [FromQuery] int take = 20, |         [FromQuery] int take = 20, | ||||||
|         [FromQuery] string? pubName = null |         [FromQuery(Name = "pub")] string? pubName = null, | ||||||
|  |         [FromQuery(Name = "order")] string? order = null, | ||||||
|  |         [FromQuery(Name = "query")] string? query = null | ||||||
|     ) |     ) | ||||||
|     { |     { | ||||||
|         Publisher.Publisher? publisher = null; |         Publisher.Publisher? publisher = null; | ||||||
|         if (pubName is not null) |         if (pubName is not null) | ||||||
|             publisher = await db.Publishers.FirstOrDefaultAsync(p => p.Name == pubName); |             publisher = await db.Publishers.FirstOrDefaultAsync(p => p.Name == pubName); | ||||||
|  |  | ||||||
|         var totalCount = await db.StickerPacks |         var queryable = db.StickerPacks | ||||||
|             .If(publisher is not null, q => q.Where(f => f.PublisherId == publisher!.Id)) |             .If(publisher is not null, q => q.Where(f => f.PublisherId == publisher!.Id)); | ||||||
|  |  | ||||||
|  |         if (order is not null) | ||||||
|  |         { | ||||||
|  |             queryable = order switch | ||||||
|  |             { | ||||||
|  |                 "usage" => queryable.OrderByDescending(p => p.Ownerships.Count), | ||||||
|  |                 _ => queryable.OrderByDescending(p => p.CreatedAt) | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (!string.IsNullOrWhiteSpace(query)) | ||||||
|  |         { | ||||||
|  |             queryable = queryable.Where(p => | ||||||
|  |                 EF.Functions.ILike(p.Name, $"%{query}%") || | ||||||
|  |                 EF.Functions.ILike(p.Description, $"%{query}%") | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         var totalCount = await queryable | ||||||
|             .CountAsync(); |             .CountAsync(); | ||||||
|         var packs = await db.StickerPacks |         var packs = await queryable | ||||||
|             .If(publisher is not null, q => q.Where(f => f.PublisherId == publisher!.Id)) |  | ||||||
|             .OrderByDescending(e => e.CreatedAt) |  | ||||||
|             .Skip(offset) |             .Skip(offset) | ||||||
|             .Take(take) |             .Take(take) | ||||||
|             .ToListAsync(); |             .ToListAsync(); | ||||||
| @@ -240,7 +260,8 @@ public class StickerController(AppDatabase db, StickerService st, FileService.Fi | |||||||
|         if (HttpContext.Items["CurrentUser"] is not Account currentUser) |         if (HttpContext.Items["CurrentUser"] is not Account currentUser) | ||||||
|             return Unauthorized(); |             return Unauthorized(); | ||||||
|  |  | ||||||
|         var permissionCheck = await _CheckStickerPackPermissions(packId, currentUser, Publisher.PublisherMemberRole.Editor); |         var permissionCheck = | ||||||
|  |             await _CheckStickerPackPermissions(packId, currentUser, Publisher.PublisherMemberRole.Editor); | ||||||
|         if (permissionCheck is not OkResult) |         if (permissionCheck is not OkResult) | ||||||
|             return permissionCheck; |             return permissionCheck; | ||||||
|  |  | ||||||
| @@ -275,7 +296,8 @@ public class StickerController(AppDatabase db, StickerService st, FileService.Fi | |||||||
|         if (HttpContext.Items["CurrentUser"] is not Account currentUser) |         if (HttpContext.Items["CurrentUser"] is not Account currentUser) | ||||||
|             return Unauthorized(); |             return Unauthorized(); | ||||||
|  |  | ||||||
|         var permissionCheck = await _CheckStickerPackPermissions(packId, currentUser, Publisher.PublisherMemberRole.Editor); |         var permissionCheck = | ||||||
|  |             await _CheckStickerPackPermissions(packId, currentUser, Publisher.PublisherMemberRole.Editor); | ||||||
|         if (permissionCheck is not OkResult) |         if (permissionCheck is not OkResult) | ||||||
|             return permissionCheck; |             return permissionCheck; | ||||||
|  |  | ||||||
| @@ -305,7 +327,8 @@ public class StickerController(AppDatabase db, StickerService st, FileService.Fi | |||||||
|         if (request.ImageId is null) |         if (request.ImageId is null) | ||||||
|             return BadRequest("Image is required."); |             return BadRequest("Image is required."); | ||||||
|  |  | ||||||
|         var permissionCheck = await _CheckStickerPackPermissions(packId, currentUser, Publisher.PublisherMemberRole.Editor); |         var permissionCheck = | ||||||
|  |             await _CheckStickerPackPermissions(packId, currentUser, Publisher.PublisherMemberRole.Editor); | ||||||
|         if (permissionCheck is not OkResult) |         if (permissionCheck is not OkResult) | ||||||
|             return permissionCheck; |             return permissionCheck; | ||||||
|  |  | ||||||
| @@ -396,4 +419,4 @@ public class StickerController(AppDatabase db, StickerService st, FileService.Fi | |||||||
|  |  | ||||||
|         return NoContent(); |         return NoContent(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -1,6 +1,7 @@ | |||||||
| using System.ComponentModel.DataAnnotations; | using System.ComponentModel.DataAnnotations; | ||||||
| using System.ComponentModel.DataAnnotations.Schema; | using System.ComponentModel.DataAnnotations.Schema; | ||||||
| using System.Text.Json.Serialization; | using System.Text.Json.Serialization; | ||||||
|  | using DysonNetwork.Pass.Account; | ||||||
| using DysonNetwork.Shared.Data; | using DysonNetwork.Shared.Data; | ||||||
|  |  | ||||||
| namespace DysonNetwork.Sphere.WebReader; | namespace DysonNetwork.Sphere.WebReader; | ||||||
| @@ -44,4 +45,14 @@ public class WebFeed : ModelBase | |||||||
|     public Publisher.Publisher Publisher { get; set; } = null!; |     public Publisher.Publisher Publisher { get; set; } = null!; | ||||||
|  |  | ||||||
|     [JsonIgnore] public ICollection<WebArticle> Articles { get; set; } = new List<WebArticle>(); |     [JsonIgnore] public ICollection<WebArticle> Articles { get; set; } = new List<WebArticle>(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | public class WebFeedSubscription : ModelBase | ||||||
|  | { | ||||||
|  |     public Guid Id { get; set; } = Guid.NewGuid(); | ||||||
|  |      | ||||||
|  |     public Guid FeedId { get; set; } | ||||||
|  |     public WebFeed Feed { get; set; } = null!; | ||||||
|  |     public Guid AccountId { get; set; } | ||||||
|  |     [NotMapped] public Account Account { get; set; } = null!; | ||||||
| } | } | ||||||
							
								
								
									
										132
									
								
								DysonNetwork.Sphere/WebReader/WebFeedPublicController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								DysonNetwork.Sphere/WebReader/WebFeedPublicController.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,132 @@ | |||||||
|  | using DysonNetwork.Shared.Proto; | ||||||
|  | using Microsoft.AspNetCore.Authorization; | ||||||
|  | using Microsoft.AspNetCore.Mvc; | ||||||
|  | using Microsoft.EntityFrameworkCore; | ||||||
|  |  | ||||||
|  | namespace DysonNetwork.Sphere.WebReader; | ||||||
|  |  | ||||||
|  | [ApiController] | ||||||
|  | [Route("/api/feeds")] | ||||||
|  | public class WebFeedPublicController( | ||||||
|  |     AppDatabase db | ||||||
|  | ) : ControllerBase | ||||||
|  | { | ||||||
|  |     /// <summary> | ||||||
|  |     /// Subscribe to a web feed | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="feedId">The ID of the feed to subscribe to</param> | ||||||
|  |     /// <returns>Subscription details</returns> | ||||||
|  |     [HttpPost("{feedId:guid}/subscribe")] | ||||||
|  |     [Authorize] | ||||||
|  |     public async Task<IActionResult> Subscribe(Guid feedId) | ||||||
|  |     { | ||||||
|  |         if (HttpContext.Items["CurrentUser"] is not Account currentUser) | ||||||
|  |             return Unauthorized(); | ||||||
|  |  | ||||||
|  |         var accountId = Guid.Parse(currentUser.Id); | ||||||
|  |  | ||||||
|  |         // Check if feed exists | ||||||
|  |         var feed = await db.WebFeeds.FindAsync(feedId); | ||||||
|  |         if (feed == null) | ||||||
|  |             return NotFound("Feed not found"); | ||||||
|  |  | ||||||
|  |         // Check if already subscribed | ||||||
|  |         var existingSubscription = await db.WebFeedSubscriptions | ||||||
|  |             .FirstOrDefaultAsync(s => s.FeedId == feedId && s.AccountId == accountId); | ||||||
|  |  | ||||||
|  |         if (existingSubscription != null) | ||||||
|  |             return Ok(existingSubscription); | ||||||
|  |  | ||||||
|  |         // Create new subscription | ||||||
|  |         var subscription = new WebFeedSubscription | ||||||
|  |         { | ||||||
|  |             FeedId = feedId, | ||||||
|  |             AccountId = accountId | ||||||
|  |         }; | ||||||
|  |  | ||||||
|  |         db.WebFeedSubscriptions.Add(subscription); | ||||||
|  |         await db.SaveChangesAsync(); | ||||||
|  |  | ||||||
|  |         return CreatedAtAction(nameof(GetSubscriptionStatus), new { feedId }, subscription); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Unsubscribe from a web feed | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="feedId">The ID of the feed to unsubscribe from</param> | ||||||
|  |     [HttpDelete("{feedId:guid}/subscribe")] | ||||||
|  |     [Authorize] | ||||||
|  |     public async Task<IActionResult> Unsubscribe(Guid feedId) | ||||||
|  |     { | ||||||
|  |         if (HttpContext.Items["CurrentUser"] is not Account currentUser) | ||||||
|  |             return Unauthorized(); | ||||||
|  |  | ||||||
|  |         var accountId = Guid.Parse(currentUser.Id); | ||||||
|  |  | ||||||
|  |         var subscription = await db.WebFeedSubscriptions | ||||||
|  |             .FirstOrDefaultAsync(s => s.FeedId == feedId && s.AccountId == accountId); | ||||||
|  |  | ||||||
|  |         if (subscription == null) | ||||||
|  |             return NoContent(); | ||||||
|  |  | ||||||
|  |         db.WebFeedSubscriptions.Remove(subscription); | ||||||
|  |         await db.SaveChangesAsync(); | ||||||
|  |  | ||||||
|  |         return NoContent(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// Get subscription status for the current user and a specific feed | ||||||
|  |     /// </summary> | ||||||
|  |     /// <param name="feedId">The ID of the feed to check</param> | ||||||
|  |     /// <returns>Subscription status</returns> | ||||||
|  |     [HttpGet("{feedId:guid}/subscription")] | ||||||
|  |     [Authorize] | ||||||
|  |     public async Task<ActionResult<WebFeedSubscription>> GetSubscriptionStatus(Guid feedId) | ||||||
|  |     { | ||||||
|  |         if (HttpContext.Items["CurrentUser"] is not Account currentUser) | ||||||
|  |             return Unauthorized(); | ||||||
|  |  | ||||||
|  |         var accountId = Guid.Parse(currentUser.Id); | ||||||
|  |  | ||||||
|  |         var subscription = await db.WebFeedSubscriptions | ||||||
|  |             .Where(s => s.FeedId == feedId && s.AccountId == accountId) | ||||||
|  |             .FirstOrDefaultAsync(); | ||||||
|  |         if (subscription is null) | ||||||
|  |             return NotFound(); | ||||||
|  |  | ||||||
|  |         return Ok(subscription); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// <summary> | ||||||
|  |     /// List all feeds the current user is subscribed to | ||||||
|  |     /// </summary> | ||||||
|  |     /// <returns>List of subscribed feeds</returns> | ||||||
|  |     [HttpGet("me")] | ||||||
|  |     [Authorize] | ||||||
|  |     public async Task<IActionResult> GetMySubscriptions( | ||||||
|  |         [FromQuery] int offset = 0, | ||||||
|  |         [FromQuery] int take = 20 | ||||||
|  |     ) | ||||||
|  |     { | ||||||
|  |         if (HttpContext.Items["CurrentUser"] is not Account currentUser) | ||||||
|  |             return Unauthorized(); | ||||||
|  |  | ||||||
|  |         var accountId = Guid.Parse(currentUser.Id); | ||||||
|  |  | ||||||
|  |         var query = db.WebFeedSubscriptions | ||||||
|  |             .Where(s => s.AccountId == accountId) | ||||||
|  |             .Include(s => s.Feed) | ||||||
|  |             .ThenInclude(f => f.Publisher) | ||||||
|  |             .OrderByDescending(s => s.CreatedAt); | ||||||
|  |  | ||||||
|  |         var totalCount = await query.CountAsync(); | ||||||
|  |         var subscriptions = await query | ||||||
|  |             .Skip(offset) | ||||||
|  |             .Take(take) | ||||||
|  |             .ToListAsync(); | ||||||
|  |  | ||||||
|  |         Response.Headers["X-Total"] = totalCount.ToString(); | ||||||
|  |         return Ok(subscriptions); | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user