✨ Posting
This commit is contained in:
parent
0e3b88c51c
commit
fb1de3da9e
@ -1,9 +1,11 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Account;
|
namespace DysonNetwork.Sphere.Account;
|
||||||
|
|
||||||
|
[Index(nameof(Name), IsUnique = true)]
|
||||||
public class Account : ModelBase
|
public class Account : ModelBase
|
||||||
{
|
{
|
||||||
public long Id { get; set; }
|
public long Id { get; set; }
|
||||||
|
@ -31,6 +31,9 @@ public class AppDatabase(
|
|||||||
public DbSet<Post.PublisherMember> PublisherMembers { get; set; }
|
public DbSet<Post.PublisherMember> PublisherMembers { get; set; }
|
||||||
public DbSet<Post.Post> Posts { get; set; }
|
public DbSet<Post.Post> Posts { get; set; }
|
||||||
public DbSet<Post.PostReaction> PostReactions { get; set; }
|
public DbSet<Post.PostReaction> PostReactions { get; set; }
|
||||||
|
public DbSet<Post.PostTag> PostTags { get; set; }
|
||||||
|
public DbSet<Post.PostCategory> PostCategories { get; set; }
|
||||||
|
public DbSet<Post.PostCollection> PostCollections { get; set; }
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
{
|
{
|
||||||
@ -80,16 +83,32 @@ public class AppDatabase(
|
|||||||
.HasForeignKey(pm => pm.AccountId)
|
.HasForeignKey(pm => pm.AccountId)
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
modelBuilder.Entity<Post.Post>()
|
||||||
|
.HasOne(p => p.ThreadedPost)
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey<Post.Post>(p => p.ThreadedPostId);
|
||||||
modelBuilder.Entity<Post.Post>()
|
modelBuilder.Entity<Post.Post>()
|
||||||
.HasOne(p => p.RepliedPost)
|
.HasOne(p => p.RepliedPost)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("RepliedPostId")
|
.HasForeignKey(p => p.RepliedPostId)
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
modelBuilder.Entity<Post.Post>()
|
modelBuilder.Entity<Post.Post>()
|
||||||
.HasOne(p => p.ForwardedPost)
|
.HasOne(p => p.ForwardedPost)
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("ForwardedPostId")
|
.HasForeignKey(p => p.ForwardedPostId)
|
||||||
.OnDelete(DeleteBehavior.Restrict);
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
modelBuilder.Entity<Post.Post>()
|
||||||
|
.HasMany(p => p.Tags)
|
||||||
|
.WithMany(t => t.Posts)
|
||||||
|
.UsingEntity(j => j.ToTable("post_tag_links"));
|
||||||
|
modelBuilder.Entity<Post.Post>()
|
||||||
|
.HasMany(p => p.Categories)
|
||||||
|
.WithMany(c => c.Posts)
|
||||||
|
.UsingEntity(j => j.ToTable("post_category_links"));
|
||||||
|
modelBuilder.Entity<Post.Post>()
|
||||||
|
.HasMany(p => p.Collections)
|
||||||
|
.WithMany(c => c.Posts)
|
||||||
|
.UsingEntity(j => j.ToTable("post_collection_links"));
|
||||||
|
|
||||||
// Automatically apply soft-delete filter to all entities inheriting BaseModel
|
// Automatically apply soft-delete filter to all entities inheriting BaseModel
|
||||||
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
|
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
|
||||||
|
@ -14,7 +14,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|||||||
namespace DysonNetwork.Sphere.Migrations
|
namespace DysonNetwork.Sphere.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(AppDatabase))]
|
[DbContext(typeof(AppDatabase))]
|
||||||
[Migration("20250419062728_AddPost")]
|
[Migration("20250419115230_AddPost")]
|
||||||
partial class AddPost
|
partial class AddPost
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -73,6 +73,10 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("pk_accounts");
|
.HasName("pk_accounts");
|
||||||
|
|
||||||
|
b.HasIndex("Name")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("ix_accounts_name");
|
||||||
|
|
||||||
b.ToTable("accounts", (string)null);
|
b.ToTable("accounts", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -419,10 +423,27 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnType("integer")
|
.HasColumnType("integer")
|
||||||
.HasColumnName("downvotes");
|
.HasColumnName("downvotes");
|
||||||
|
|
||||||
|
b.Property<Instant?>("EditedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("edited_at");
|
||||||
|
|
||||||
b.Property<long?>("ForwardedPostId")
|
b.Property<long?>("ForwardedPostId")
|
||||||
.HasColumnType("bigint")
|
.HasColumnType("bigint")
|
||||||
.HasColumnName("forwarded_post_id");
|
.HasColumnName("forwarded_post_id");
|
||||||
|
|
||||||
|
b.Property<string>("Language")
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("character varying(128)")
|
||||||
|
.HasColumnName("language");
|
||||||
|
|
||||||
|
b.Property<Dictionary<string, object>>("Meta")
|
||||||
|
.HasColumnType("jsonb")
|
||||||
|
.HasColumnName("meta");
|
||||||
|
|
||||||
|
b.Property<Instant?>("PublishedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("published_at");
|
||||||
|
|
||||||
b.Property<long>("PublisherId")
|
b.Property<long>("PublisherId")
|
||||||
.HasColumnType("bigint")
|
.HasColumnType("bigint")
|
||||||
.HasColumnName("publisher_id");
|
.HasColumnName("publisher_id");
|
||||||
@ -431,6 +452,10 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnType("bigint")
|
.HasColumnType("bigint")
|
||||||
.HasColumnName("replied_post_id");
|
.HasColumnName("replied_post_id");
|
||||||
|
|
||||||
|
b.Property<long?>("ThreadedPostId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("threaded_post_id");
|
||||||
|
|
||||||
b.Property<string>("Title")
|
b.Property<string>("Title")
|
||||||
.HasMaxLength(1024)
|
.HasMaxLength(1024)
|
||||||
.HasColumnType("character varying(1024)")
|
.HasColumnType("character varying(1024)")
|
||||||
@ -456,6 +481,10 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnType("integer")
|
.HasColumnType("integer")
|
||||||
.HasColumnName("views_unique");
|
.HasColumnName("views_unique");
|
||||||
|
|
||||||
|
b.Property<int>("Visibility")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("visibility");
|
||||||
|
|
||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("pk_posts");
|
.HasName("pk_posts");
|
||||||
|
|
||||||
@ -468,9 +497,101 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.HasIndex("RepliedPostId")
|
b.HasIndex("RepliedPostId")
|
||||||
.HasDatabaseName("ix_posts_replied_post_id");
|
.HasDatabaseName("ix_posts_replied_post_id");
|
||||||
|
|
||||||
|
b.HasIndex("ThreadedPostId")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("ix_posts_threaded_post_id");
|
||||||
|
|
||||||
b.ToTable("posts", (string)null);
|
b.ToTable("posts", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DysonNetwork.Sphere.Post.PostCategory", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("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<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)")
|
||||||
|
.HasColumnName("name");
|
||||||
|
|
||||||
|
b.Property<string>("Slug")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("character varying(128)")
|
||||||
|
.HasColumnName("slug");
|
||||||
|
|
||||||
|
b.Property<Instant>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated_at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_post_categories");
|
||||||
|
|
||||||
|
b.ToTable("post_categories", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DysonNetwork.Sphere.Post.PostCollection", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("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<string>("Description")
|
||||||
|
.HasMaxLength(4096)
|
||||||
|
.HasColumnType("character varying(4096)")
|
||||||
|
.HasColumnName("description");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)")
|
||||||
|
.HasColumnName("name");
|
||||||
|
|
||||||
|
b.Property<long>("PublisherId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("publisher_id");
|
||||||
|
|
||||||
|
b.Property<string>("Slug")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("character varying(128)")
|
||||||
|
.HasColumnName("slug");
|
||||||
|
|
||||||
|
b.Property<Instant>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated_at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_post_collections");
|
||||||
|
|
||||||
|
b.HasIndex("PublisherId")
|
||||||
|
.HasDatabaseName("ix_post_collections_publisher_id");
|
||||||
|
|
||||||
|
b.ToTable("post_collections", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Post.PostReaction", b =>
|
modelBuilder.Entity("DysonNetwork.Sphere.Post.PostReaction", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("Id")
|
b.Property<long>("Id")
|
||||||
@ -522,6 +643,44 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.ToTable("post_reactions", (string)null);
|
b.ToTable("post_reactions", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DysonNetwork.Sphere.Post.PostTag", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("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<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)")
|
||||||
|
.HasColumnName("name");
|
||||||
|
|
||||||
|
b.Property<string>("Slug")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("character varying(128)")
|
||||||
|
.HasColumnName("slug");
|
||||||
|
|
||||||
|
b.Property<Instant>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated_at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_post_tags");
|
||||||
|
|
||||||
|
b.ToTable("post_tags", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Post.Publisher", b =>
|
modelBuilder.Entity("DysonNetwork.Sphere.Post.Publisher", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("Id")
|
b.Property<long>("Id")
|
||||||
@ -585,6 +744,10 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.HasIndex("BackgroundId")
|
b.HasIndex("BackgroundId")
|
||||||
.HasDatabaseName("ix_publishers_background_id");
|
.HasDatabaseName("ix_publishers_background_id");
|
||||||
|
|
||||||
|
b.HasIndex("Name")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("ix_publishers_name");
|
||||||
|
|
||||||
b.HasIndex("PictureId")
|
b.HasIndex("PictureId")
|
||||||
.HasDatabaseName("ix_publishers_picture_id");
|
.HasDatabaseName("ix_publishers_picture_id");
|
||||||
|
|
||||||
@ -714,6 +877,63 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.ToTable("files", (string)null);
|
b.ToTable("files", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PostPostCategory", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("CategoriesId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("categories_id");
|
||||||
|
|
||||||
|
b.Property<long>("PostsId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("posts_id");
|
||||||
|
|
||||||
|
b.HasKey("CategoriesId", "PostsId")
|
||||||
|
.HasName("pk_post_category_links");
|
||||||
|
|
||||||
|
b.HasIndex("PostsId")
|
||||||
|
.HasDatabaseName("ix_post_category_links_posts_id");
|
||||||
|
|
||||||
|
b.ToTable("post_category_links", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PostPostCollection", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("CollectionsId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("collections_id");
|
||||||
|
|
||||||
|
b.Property<long>("PostsId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("posts_id");
|
||||||
|
|
||||||
|
b.HasKey("CollectionsId", "PostsId")
|
||||||
|
.HasName("pk_post_collection_links");
|
||||||
|
|
||||||
|
b.HasIndex("PostsId")
|
||||||
|
.HasDatabaseName("ix_post_collection_links_posts_id");
|
||||||
|
|
||||||
|
b.ToTable("post_collection_links", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PostPostTag", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("PostsId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("posts_id");
|
||||||
|
|
||||||
|
b.Property<long>("TagsId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("tags_id");
|
||||||
|
|
||||||
|
b.HasKey("PostsId", "TagsId")
|
||||||
|
.HasName("pk_post_tag_links");
|
||||||
|
|
||||||
|
b.HasIndex("TagsId")
|
||||||
|
.HasDatabaseName("ix_post_tag_links_tags_id");
|
||||||
|
|
||||||
|
b.ToTable("post_tag_links", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Account.AccountAuthFactor", b =>
|
modelBuilder.Entity("DysonNetwork.Sphere.Account.AccountAuthFactor", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("DysonNetwork.Sphere.Account.Account", "Account")
|
b.HasOne("DysonNetwork.Sphere.Account.Account", "Account")
|
||||||
@ -839,11 +1059,30 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Restrict)
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
.HasConstraintName("fk_posts_posts_replied_post_id");
|
.HasConstraintName("fk_posts_posts_replied_post_id");
|
||||||
|
|
||||||
|
b.HasOne("DysonNetwork.Sphere.Post.Post", "ThreadedPost")
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("DysonNetwork.Sphere.Post.Post", "ThreadedPostId")
|
||||||
|
.HasConstraintName("fk_posts_posts_threaded_post_id");
|
||||||
|
|
||||||
b.Navigation("ForwardedPost");
|
b.Navigation("ForwardedPost");
|
||||||
|
|
||||||
b.Navigation("Publisher");
|
b.Navigation("Publisher");
|
||||||
|
|
||||||
b.Navigation("RepliedPost");
|
b.Navigation("RepliedPost");
|
||||||
|
|
||||||
|
b.Navigation("ThreadedPost");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DysonNetwork.Sphere.Post.PostCollection", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DysonNetwork.Sphere.Post.Publisher", "Publisher")
|
||||||
|
.WithMany("Collections")
|
||||||
|
.HasForeignKey("PublisherId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired()
|
||||||
|
.HasConstraintName("fk_post_collections_publishers_publisher_id");
|
||||||
|
|
||||||
|
b.Navigation("Publisher");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Post.PostReaction", b =>
|
modelBuilder.Entity("DysonNetwork.Sphere.Post.PostReaction", b =>
|
||||||
@ -929,6 +1168,57 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.Navigation("Account");
|
b.Navigation("Account");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PostPostCategory", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DysonNetwork.Sphere.Post.PostCategory", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CategoriesId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired()
|
||||||
|
.HasConstraintName("fk_post_category_links_post_categories_categories_id");
|
||||||
|
|
||||||
|
b.HasOne("DysonNetwork.Sphere.Post.Post", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PostsId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired()
|
||||||
|
.HasConstraintName("fk_post_category_links_posts_posts_id");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PostPostCollection", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DysonNetwork.Sphere.Post.PostCollection", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CollectionsId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired()
|
||||||
|
.HasConstraintName("fk_post_collection_links_post_collections_collections_id");
|
||||||
|
|
||||||
|
b.HasOne("DysonNetwork.Sphere.Post.Post", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PostsId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired()
|
||||||
|
.HasConstraintName("fk_post_collection_links_posts_posts_id");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PostPostTag", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DysonNetwork.Sphere.Post.Post", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PostsId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired()
|
||||||
|
.HasConstraintName("fk_post_tag_links_posts_posts_id");
|
||||||
|
|
||||||
|
b.HasOne("DysonNetwork.Sphere.Post.PostTag", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TagsId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired()
|
||||||
|
.HasConstraintName("fk_post_tag_links_post_tags_tags_id");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Account.Account", b =>
|
modelBuilder.Entity("DysonNetwork.Sphere.Account.Account", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("AuthFactors");
|
b.Navigation("AuthFactors");
|
||||||
@ -956,6 +1246,8 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Post.Publisher", b =>
|
modelBuilder.Entity("DysonNetwork.Sphere.Post.Publisher", b =>
|
||||||
{
|
{
|
||||||
|
b.Navigation("Collections");
|
||||||
|
|
||||||
b.Navigation("Members");
|
b.Navigation("Members");
|
||||||
|
|
||||||
b.Navigation("Posts");
|
b.Navigation("Posts");
|
@ -1,4 +1,5 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
@ -18,6 +19,40 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
type: "bigint",
|
type: "bigint",
|
||||||
nullable: true);
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "post_categories",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
slug = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
||||||
|
name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
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_post_categories", x => x.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "post_tags",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
slug = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
||||||
|
name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
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_post_tags", x => x.id);
|
||||||
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "publishers",
|
name: "publishers",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
@ -55,6 +90,31 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
principalColumn: "id");
|
principalColumn: "id");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "post_collections",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
slug = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
|
||||||
|
name = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
|
||||||
|
description = table.Column<string>(type: "character varying(4096)", maxLength: 4096, nullable: true),
|
||||||
|
publisher_id = table.Column<long>(type: "bigint", 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_post_collections", x => x.id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "fk_post_collections_publishers_publisher_id",
|
||||||
|
column: x => x.publisher_id,
|
||||||
|
principalTable: "publishers",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "posts",
|
name: "posts",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
@ -63,12 +123,18 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
title = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: true),
|
title = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: true),
|
||||||
description = table.Column<string>(type: "character varying(4096)", maxLength: 4096, nullable: true),
|
description = table.Column<string>(type: "character varying(4096)", maxLength: 4096, nullable: true),
|
||||||
|
language = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: true),
|
||||||
|
edited_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true),
|
||||||
|
published_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true),
|
||||||
|
visibility = table.Column<int>(type: "integer", nullable: false),
|
||||||
content = table.Column<string>(type: "text", nullable: true),
|
content = table.Column<string>(type: "text", nullable: true),
|
||||||
type = table.Column<int>(type: "integer", nullable: false),
|
type = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
meta = table.Column<Dictionary<string, object>>(type: "jsonb", nullable: true),
|
||||||
views_unique = table.Column<int>(type: "integer", nullable: false),
|
views_unique = table.Column<int>(type: "integer", nullable: false),
|
||||||
views_total = table.Column<int>(type: "integer", nullable: false),
|
views_total = table.Column<int>(type: "integer", nullable: false),
|
||||||
upvotes = table.Column<int>(type: "integer", nullable: false),
|
upvotes = table.Column<int>(type: "integer", nullable: false),
|
||||||
downvotes = table.Column<int>(type: "integer", nullable: false),
|
downvotes = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
threaded_post_id = table.Column<long>(type: "bigint", nullable: true),
|
||||||
replied_post_id = table.Column<long>(type: "bigint", nullable: true),
|
replied_post_id = table.Column<long>(type: "bigint", nullable: true),
|
||||||
forwarded_post_id = table.Column<long>(type: "bigint", nullable: true),
|
forwarded_post_id = table.Column<long>(type: "bigint", nullable: true),
|
||||||
publisher_id = table.Column<long>(type: "bigint", nullable: false),
|
publisher_id = table.Column<long>(type: "bigint", nullable: false),
|
||||||
@ -91,6 +157,11 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
principalTable: "posts",
|
principalTable: "posts",
|
||||||
principalColumn: "id",
|
principalColumn: "id",
|
||||||
onDelete: ReferentialAction.Restrict);
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "fk_posts_posts_threaded_post_id",
|
||||||
|
column: x => x.threaded_post_id,
|
||||||
|
principalTable: "posts",
|
||||||
|
principalColumn: "id");
|
||||||
table.ForeignKey(
|
table.ForeignKey(
|
||||||
name: "fk_posts_publishers_publisher_id",
|
name: "fk_posts_publishers_publisher_id",
|
||||||
column: x => x.publisher_id,
|
column: x => x.publisher_id,
|
||||||
@ -128,6 +199,54 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "post_category_links",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
categories_id = table.Column<long>(type: "bigint", nullable: false),
|
||||||
|
posts_id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_post_category_links", x => new { x.categories_id, x.posts_id });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "fk_post_category_links_post_categories_categories_id",
|
||||||
|
column: x => x.categories_id,
|
||||||
|
principalTable: "post_categories",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "fk_post_category_links_posts_posts_id",
|
||||||
|
column: x => x.posts_id,
|
||||||
|
principalTable: "posts",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "post_collection_links",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
collections_id = table.Column<long>(type: "bigint", nullable: false),
|
||||||
|
posts_id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_post_collection_links", x => new { x.collections_id, x.posts_id });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "fk_post_collection_links_post_collections_collections_id",
|
||||||
|
column: x => x.collections_id,
|
||||||
|
principalTable: "post_collections",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "fk_post_collection_links_posts_posts_id",
|
||||||
|
column: x => x.posts_id,
|
||||||
|
principalTable: "posts",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "post_reactions",
|
name: "post_reactions",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
@ -159,11 +278,56 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "post_tag_links",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
posts_id = table.Column<long>(type: "bigint", nullable: false),
|
||||||
|
tags_id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_post_tag_links", x => new { x.posts_id, x.tags_id });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "fk_post_tag_links_post_tags_tags_id",
|
||||||
|
column: x => x.tags_id,
|
||||||
|
principalTable: "post_tags",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "fk_post_tag_links_posts_posts_id",
|
||||||
|
column: x => x.posts_id,
|
||||||
|
principalTable: "posts",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "ix_files_post_id",
|
name: "ix_files_post_id",
|
||||||
table: "files",
|
table: "files",
|
||||||
column: "post_id");
|
column: "post_id");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_accounts_name",
|
||||||
|
table: "accounts",
|
||||||
|
column: "name",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_post_category_links_posts_id",
|
||||||
|
table: "post_category_links",
|
||||||
|
column: "posts_id");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_post_collection_links_posts_id",
|
||||||
|
table: "post_collection_links",
|
||||||
|
column: "posts_id");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_post_collections_publisher_id",
|
||||||
|
table: "post_collections",
|
||||||
|
column: "publisher_id");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "ix_post_reactions_account_id",
|
name: "ix_post_reactions_account_id",
|
||||||
table: "post_reactions",
|
table: "post_reactions",
|
||||||
@ -174,6 +338,11 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
table: "post_reactions",
|
table: "post_reactions",
|
||||||
column: "post_id");
|
column: "post_id");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_post_tag_links_tags_id",
|
||||||
|
table: "post_tag_links",
|
||||||
|
column: "tags_id");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "ix_posts_forwarded_post_id",
|
name: "ix_posts_forwarded_post_id",
|
||||||
table: "posts",
|
table: "posts",
|
||||||
@ -189,6 +358,12 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
table: "posts",
|
table: "posts",
|
||||||
column: "replied_post_id");
|
column: "replied_post_id");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_posts_threaded_post_id",
|
||||||
|
table: "posts",
|
||||||
|
column: "threaded_post_id",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "ix_publisher_members_account_id",
|
name: "ix_publisher_members_account_id",
|
||||||
table: "publisher_members",
|
table: "publisher_members",
|
||||||
@ -204,6 +379,12 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
table: "publishers",
|
table: "publishers",
|
||||||
column: "background_id");
|
column: "background_id");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_publishers_name",
|
||||||
|
table: "publishers",
|
||||||
|
column: "name",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "ix_publishers_picture_id",
|
name: "ix_publishers_picture_id",
|
||||||
table: "publishers",
|
table: "publishers",
|
||||||
@ -224,12 +405,30 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
name: "fk_files_posts_post_id",
|
name: "fk_files_posts_post_id",
|
||||||
table: "files");
|
table: "files");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "post_category_links");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "post_collection_links");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "post_reactions");
|
name: "post_reactions");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "post_tag_links");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "publisher_members");
|
name: "publisher_members");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "post_categories");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "post_collections");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "post_tags");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "posts");
|
name: "posts");
|
||||||
|
|
||||||
@ -240,6 +439,10 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
name: "ix_files_post_id",
|
name: "ix_files_post_id",
|
||||||
table: "files");
|
table: "files");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "ix_accounts_name",
|
||||||
|
table: "accounts");
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
migrationBuilder.DropColumn(
|
||||||
name: "post_id",
|
name: "post_id",
|
||||||
table: "files");
|
table: "files");
|
@ -70,6 +70,10 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("pk_accounts");
|
.HasName("pk_accounts");
|
||||||
|
|
||||||
|
b.HasIndex("Name")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("ix_accounts_name");
|
||||||
|
|
||||||
b.ToTable("accounts", (string)null);
|
b.ToTable("accounts", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -416,10 +420,27 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnType("integer")
|
.HasColumnType("integer")
|
||||||
.HasColumnName("downvotes");
|
.HasColumnName("downvotes");
|
||||||
|
|
||||||
|
b.Property<Instant?>("EditedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("edited_at");
|
||||||
|
|
||||||
b.Property<long?>("ForwardedPostId")
|
b.Property<long?>("ForwardedPostId")
|
||||||
.HasColumnType("bigint")
|
.HasColumnType("bigint")
|
||||||
.HasColumnName("forwarded_post_id");
|
.HasColumnName("forwarded_post_id");
|
||||||
|
|
||||||
|
b.Property<string>("Language")
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("character varying(128)")
|
||||||
|
.HasColumnName("language");
|
||||||
|
|
||||||
|
b.Property<Dictionary<string, object>>("Meta")
|
||||||
|
.HasColumnType("jsonb")
|
||||||
|
.HasColumnName("meta");
|
||||||
|
|
||||||
|
b.Property<Instant?>("PublishedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("published_at");
|
||||||
|
|
||||||
b.Property<long>("PublisherId")
|
b.Property<long>("PublisherId")
|
||||||
.HasColumnType("bigint")
|
.HasColumnType("bigint")
|
||||||
.HasColumnName("publisher_id");
|
.HasColumnName("publisher_id");
|
||||||
@ -428,6 +449,10 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnType("bigint")
|
.HasColumnType("bigint")
|
||||||
.HasColumnName("replied_post_id");
|
.HasColumnName("replied_post_id");
|
||||||
|
|
||||||
|
b.Property<long?>("ThreadedPostId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("threaded_post_id");
|
||||||
|
|
||||||
b.Property<string>("Title")
|
b.Property<string>("Title")
|
||||||
.HasMaxLength(1024)
|
.HasMaxLength(1024)
|
||||||
.HasColumnType("character varying(1024)")
|
.HasColumnType("character varying(1024)")
|
||||||
@ -453,6 +478,10 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.HasColumnType("integer")
|
.HasColumnType("integer")
|
||||||
.HasColumnName("views_unique");
|
.HasColumnName("views_unique");
|
||||||
|
|
||||||
|
b.Property<int>("Visibility")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("visibility");
|
||||||
|
|
||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("pk_posts");
|
.HasName("pk_posts");
|
||||||
|
|
||||||
@ -465,9 +494,101 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.HasIndex("RepliedPostId")
|
b.HasIndex("RepliedPostId")
|
||||||
.HasDatabaseName("ix_posts_replied_post_id");
|
.HasDatabaseName("ix_posts_replied_post_id");
|
||||||
|
|
||||||
|
b.HasIndex("ThreadedPostId")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("ix_posts_threaded_post_id");
|
||||||
|
|
||||||
b.ToTable("posts", (string)null);
|
b.ToTable("posts", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DysonNetwork.Sphere.Post.PostCategory", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("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<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)")
|
||||||
|
.HasColumnName("name");
|
||||||
|
|
||||||
|
b.Property<string>("Slug")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("character varying(128)")
|
||||||
|
.HasColumnName("slug");
|
||||||
|
|
||||||
|
b.Property<Instant>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated_at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_post_categories");
|
||||||
|
|
||||||
|
b.ToTable("post_categories", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DysonNetwork.Sphere.Post.PostCollection", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("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<string>("Description")
|
||||||
|
.HasMaxLength(4096)
|
||||||
|
.HasColumnType("character varying(4096)")
|
||||||
|
.HasColumnName("description");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)")
|
||||||
|
.HasColumnName("name");
|
||||||
|
|
||||||
|
b.Property<long>("PublisherId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("publisher_id");
|
||||||
|
|
||||||
|
b.Property<string>("Slug")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("character varying(128)")
|
||||||
|
.HasColumnName("slug");
|
||||||
|
|
||||||
|
b.Property<Instant>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated_at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_post_collections");
|
||||||
|
|
||||||
|
b.HasIndex("PublisherId")
|
||||||
|
.HasDatabaseName("ix_post_collections_publisher_id");
|
||||||
|
|
||||||
|
b.ToTable("post_collections", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Post.PostReaction", b =>
|
modelBuilder.Entity("DysonNetwork.Sphere.Post.PostReaction", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("Id")
|
b.Property<long>("Id")
|
||||||
@ -519,6 +640,44 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.ToTable("post_reactions", (string)null);
|
b.ToTable("post_reactions", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DysonNetwork.Sphere.Post.PostTag", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("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<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)")
|
||||||
|
.HasColumnName("name");
|
||||||
|
|
||||||
|
b.Property<string>("Slug")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("character varying(128)")
|
||||||
|
.HasColumnName("slug");
|
||||||
|
|
||||||
|
b.Property<Instant>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated_at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_post_tags");
|
||||||
|
|
||||||
|
b.ToTable("post_tags", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Post.Publisher", b =>
|
modelBuilder.Entity("DysonNetwork.Sphere.Post.Publisher", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("Id")
|
b.Property<long>("Id")
|
||||||
@ -582,6 +741,10 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.HasIndex("BackgroundId")
|
b.HasIndex("BackgroundId")
|
||||||
.HasDatabaseName("ix_publishers_background_id");
|
.HasDatabaseName("ix_publishers_background_id");
|
||||||
|
|
||||||
|
b.HasIndex("Name")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("ix_publishers_name");
|
||||||
|
|
||||||
b.HasIndex("PictureId")
|
b.HasIndex("PictureId")
|
||||||
.HasDatabaseName("ix_publishers_picture_id");
|
.HasDatabaseName("ix_publishers_picture_id");
|
||||||
|
|
||||||
@ -711,6 +874,63 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.ToTable("files", (string)null);
|
b.ToTable("files", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PostPostCategory", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("CategoriesId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("categories_id");
|
||||||
|
|
||||||
|
b.Property<long>("PostsId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("posts_id");
|
||||||
|
|
||||||
|
b.HasKey("CategoriesId", "PostsId")
|
||||||
|
.HasName("pk_post_category_links");
|
||||||
|
|
||||||
|
b.HasIndex("PostsId")
|
||||||
|
.HasDatabaseName("ix_post_category_links_posts_id");
|
||||||
|
|
||||||
|
b.ToTable("post_category_links", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PostPostCollection", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("CollectionsId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("collections_id");
|
||||||
|
|
||||||
|
b.Property<long>("PostsId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("posts_id");
|
||||||
|
|
||||||
|
b.HasKey("CollectionsId", "PostsId")
|
||||||
|
.HasName("pk_post_collection_links");
|
||||||
|
|
||||||
|
b.HasIndex("PostsId")
|
||||||
|
.HasDatabaseName("ix_post_collection_links_posts_id");
|
||||||
|
|
||||||
|
b.ToTable("post_collection_links", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PostPostTag", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("PostsId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("posts_id");
|
||||||
|
|
||||||
|
b.Property<long>("TagsId")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("tags_id");
|
||||||
|
|
||||||
|
b.HasKey("PostsId", "TagsId")
|
||||||
|
.HasName("pk_post_tag_links");
|
||||||
|
|
||||||
|
b.HasIndex("TagsId")
|
||||||
|
.HasDatabaseName("ix_post_tag_links_tags_id");
|
||||||
|
|
||||||
|
b.ToTable("post_tag_links", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Account.AccountAuthFactor", b =>
|
modelBuilder.Entity("DysonNetwork.Sphere.Account.AccountAuthFactor", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("DysonNetwork.Sphere.Account.Account", "Account")
|
b.HasOne("DysonNetwork.Sphere.Account.Account", "Account")
|
||||||
@ -836,11 +1056,30 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Restrict)
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
.HasConstraintName("fk_posts_posts_replied_post_id");
|
.HasConstraintName("fk_posts_posts_replied_post_id");
|
||||||
|
|
||||||
|
b.HasOne("DysonNetwork.Sphere.Post.Post", "ThreadedPost")
|
||||||
|
.WithOne()
|
||||||
|
.HasForeignKey("DysonNetwork.Sphere.Post.Post", "ThreadedPostId")
|
||||||
|
.HasConstraintName("fk_posts_posts_threaded_post_id");
|
||||||
|
|
||||||
b.Navigation("ForwardedPost");
|
b.Navigation("ForwardedPost");
|
||||||
|
|
||||||
b.Navigation("Publisher");
|
b.Navigation("Publisher");
|
||||||
|
|
||||||
b.Navigation("RepliedPost");
|
b.Navigation("RepliedPost");
|
||||||
|
|
||||||
|
b.Navigation("ThreadedPost");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DysonNetwork.Sphere.Post.PostCollection", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DysonNetwork.Sphere.Post.Publisher", "Publisher")
|
||||||
|
.WithMany("Collections")
|
||||||
|
.HasForeignKey("PublisherId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired()
|
||||||
|
.HasConstraintName("fk_post_collections_publishers_publisher_id");
|
||||||
|
|
||||||
|
b.Navigation("Publisher");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Post.PostReaction", b =>
|
modelBuilder.Entity("DysonNetwork.Sphere.Post.PostReaction", b =>
|
||||||
@ -926,6 +1165,57 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
b.Navigation("Account");
|
b.Navigation("Account");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PostPostCategory", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DysonNetwork.Sphere.Post.PostCategory", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CategoriesId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired()
|
||||||
|
.HasConstraintName("fk_post_category_links_post_categories_categories_id");
|
||||||
|
|
||||||
|
b.HasOne("DysonNetwork.Sphere.Post.Post", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PostsId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired()
|
||||||
|
.HasConstraintName("fk_post_category_links_posts_posts_id");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PostPostCollection", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DysonNetwork.Sphere.Post.PostCollection", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CollectionsId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired()
|
||||||
|
.HasConstraintName("fk_post_collection_links_post_collections_collections_id");
|
||||||
|
|
||||||
|
b.HasOne("DysonNetwork.Sphere.Post.Post", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PostsId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired()
|
||||||
|
.HasConstraintName("fk_post_collection_links_posts_posts_id");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PostPostTag", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DysonNetwork.Sphere.Post.Post", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PostsId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired()
|
||||||
|
.HasConstraintName("fk_post_tag_links_posts_posts_id");
|
||||||
|
|
||||||
|
b.HasOne("DysonNetwork.Sphere.Post.PostTag", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TagsId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired()
|
||||||
|
.HasConstraintName("fk_post_tag_links_post_tags_tags_id");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Account.Account", b =>
|
modelBuilder.Entity("DysonNetwork.Sphere.Account.Account", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("AuthFactors");
|
b.Navigation("AuthFactors");
|
||||||
@ -953,6 +1243,8 @@ namespace DysonNetwork.Sphere.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Sphere.Post.Publisher", b =>
|
modelBuilder.Entity("DysonNetwork.Sphere.Post.Publisher", b =>
|
||||||
{
|
{
|
||||||
|
b.Navigation("Collections");
|
||||||
|
|
||||||
b.Navigation("Members");
|
b.Navigation("Members");
|
||||||
|
|
||||||
b.Navigation("Posts");
|
b.Navigation("Posts");
|
||||||
|
@ -2,6 +2,7 @@ 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.Sphere.Storage;
|
using DysonNetwork.Sphere.Storage;
|
||||||
|
using NodaTime;
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Post;
|
namespace DysonNetwork.Sphere.Post;
|
||||||
|
|
||||||
@ -12,28 +13,78 @@ public enum PostType
|
|||||||
Video
|
Video
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum PostVisibility
|
||||||
|
{
|
||||||
|
Public,
|
||||||
|
Friends,
|
||||||
|
Unlisted,
|
||||||
|
Private
|
||||||
|
}
|
||||||
|
|
||||||
public class Post : ModelBase
|
public class Post : ModelBase
|
||||||
{
|
{
|
||||||
public long Id { get; set; }
|
public long Id { get; set; }
|
||||||
[MaxLength(1024)] public string? Title { get; set; }
|
[MaxLength(1024)] public string? Title { get; set; }
|
||||||
[MaxLength(4096)] public string? Description { get; set; }
|
[MaxLength(4096)] public string? Description { get; set; }
|
||||||
|
[MaxLength(128)] public string? Language { get; set; }
|
||||||
|
public Instant? EditedAt { get; set; }
|
||||||
|
public Instant? PublishedAt { get; set; }
|
||||||
|
public PostVisibility Visibility { get; set; } = PostVisibility.Public;
|
||||||
|
|
||||||
// ReSharper disable once EntityFramework.ModelValidation.UnlimitedStringLength
|
// ReSharper disable once EntityFramework.ModelValidation.UnlimitedStringLength
|
||||||
public string? Content { get; set; }
|
public string? Content { get; set; }
|
||||||
|
|
||||||
public PostType Type { get; set; }
|
public PostType Type { get; set; }
|
||||||
[Column(TypeName = "jsonb")] Dictionary<string, object>? Meta { get; set; }
|
[Column(TypeName = "jsonb")] public Dictionary<string, object>? Meta { get; set; }
|
||||||
|
|
||||||
public int ViewsUnique { get; set; }
|
public int ViewsUnique { get; set; }
|
||||||
public int ViewsTotal { get; set; }
|
public int ViewsTotal { get; set; }
|
||||||
public int Upvotes { get; set; }
|
public int Upvotes { get; set; }
|
||||||
public int Downvotes { get; set; }
|
public int Downvotes { get; set; }
|
||||||
|
|
||||||
|
public long? ThreadedPostId { get; set; }
|
||||||
|
public Post? ThreadedPost { get; set; }
|
||||||
|
public long? RepliedPostId { get; set; }
|
||||||
public Post? RepliedPost { get; set; }
|
public Post? RepliedPost { get; set; }
|
||||||
|
public long? ForwardedPostId { get; set; }
|
||||||
public Post? ForwardedPost { get; set; }
|
public Post? ForwardedPost { get; set; }
|
||||||
public ICollection<CloudFile> Attachments { get; set; } = new List<CloudFile>();
|
public ICollection<CloudFile> Attachments { get; set; } = new List<CloudFile>();
|
||||||
|
|
||||||
public ICollection<PostReaction> Reactions { get; set; } = new List<PostReaction>();
|
|
||||||
public Publisher Publisher { get; set; } = null!;
|
public Publisher Publisher { get; set; } = null!;
|
||||||
|
public ICollection<PostReaction> Reactions { get; set; } = new List<PostReaction>();
|
||||||
|
public ICollection<PostTag> Tags { get; set; } = new List<PostTag>();
|
||||||
|
public ICollection<PostCategory> Categories { get; set; } = new List<PostCategory>();
|
||||||
|
public ICollection<PostCollection> Collections { get; set; } = new List<PostCollection>();
|
||||||
|
|
||||||
|
public bool Empty => Content?.Trim() is { Length: 0 } && Attachments.Count == 0 && ForwardedPostId == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PostTag : ModelBase
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
[MaxLength(128)] public string Slug { get; set; } = null!;
|
||||||
|
[MaxLength(256)] public string? Name { get; set; }
|
||||||
|
public ICollection<Post> Posts { get; set; } = new List<Post>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PostCategory : ModelBase
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
[MaxLength(128)] public string Slug { get; set; } = null!;
|
||||||
|
[MaxLength(256)] public string? Name { get; set; }
|
||||||
|
public ICollection<Post> Posts { get; set; } = new List<Post>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PostCollection : ModelBase
|
||||||
|
{
|
||||||
|
public long Id { get; set; }
|
||||||
|
[MaxLength(128)] public string Slug { get; set; } = null!;
|
||||||
|
[MaxLength(256)] public string? Name { get; set; }
|
||||||
|
[MaxLength(4096)] public string? Description { get; set; }
|
||||||
|
|
||||||
|
public Publisher Publisher { get; set; } = null!;
|
||||||
|
|
||||||
|
public ICollection<Post> Posts { get; set; } = new List<Post>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum PostReactionAttitude
|
public enum PostReactionAttitude
|
||||||
|
225
DysonNetwork.Sphere/Post/PostController.cs
Normal file
225
DysonNetwork.Sphere/Post/PostController.cs
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace DysonNetwork.Sphere.Post;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("/posts")]
|
||||||
|
public class PostController(AppDatabase db, PostService ps) : ControllerBase
|
||||||
|
{
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<ActionResult<List<Post>>> ListPosts([FromQuery] int offset = 0, [FromQuery] int take = 20)
|
||||||
|
{
|
||||||
|
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
|
||||||
|
var currentUser = currentUserValue as Account.Account;
|
||||||
|
|
||||||
|
var totalCount = await db.Posts
|
||||||
|
.CountAsync();
|
||||||
|
var posts = await db.Posts
|
||||||
|
.Include(e => e.Publisher)
|
||||||
|
.Include(e => e.ThreadedPost)
|
||||||
|
.Include(e => e.ForwardedPost)
|
||||||
|
.Include(e => e.Attachments)
|
||||||
|
.Include(e => e.Categories)
|
||||||
|
.Include(e => e.Tags)
|
||||||
|
.FilterWithVisibility(currentUser, isListing: true)
|
||||||
|
.Skip(offset)
|
||||||
|
.Take(take)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
Response.Headers["X-Total"] = totalCount.ToString();
|
||||||
|
|
||||||
|
return Ok(posts);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id:long}")]
|
||||||
|
public async Task<ActionResult<Post>> GetPost(long id)
|
||||||
|
{
|
||||||
|
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
|
||||||
|
var currentUser = currentUserValue as Account.Account;
|
||||||
|
|
||||||
|
var post = await db.Posts
|
||||||
|
.Where(e => e.Id == id)
|
||||||
|
.Include(e => e.Publisher)
|
||||||
|
.Include(e => e.RepliedPost)
|
||||||
|
.Include(e => e.ThreadedPost)
|
||||||
|
.Include(e => e.ForwardedPost)
|
||||||
|
.Include(e => e.Tags)
|
||||||
|
.Include(e => e.Categories)
|
||||||
|
.Include(e => e.Attachments)
|
||||||
|
.FilterWithVisibility(currentUser)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (post is null) return NotFound();
|
||||||
|
|
||||||
|
return Ok(post);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id:long}/replies")]
|
||||||
|
public async Task<ActionResult<List<Post>>> ListReplies(long id, [FromQuery] int offset = 0,
|
||||||
|
[FromQuery] int take = 20)
|
||||||
|
{
|
||||||
|
HttpContext.Items.TryGetValue("CurrentUser", out var currentUserValue);
|
||||||
|
var currentUser = currentUserValue as Account.Account;
|
||||||
|
|
||||||
|
var post = await db.Posts
|
||||||
|
.Where(e => e.Id == id)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (post is null) return NotFound();
|
||||||
|
|
||||||
|
var totalCount = await db.Posts
|
||||||
|
.Where(e => e.RepliedPostId == post.Id)
|
||||||
|
.CountAsync();
|
||||||
|
var posts = await db.Posts
|
||||||
|
.Where(e => e.RepliedPostId == id)
|
||||||
|
.Include(e => e.Publisher)
|
||||||
|
.Include(e => e.ThreadedPost)
|
||||||
|
.Include(e => e.ForwardedPost)
|
||||||
|
.Include(e => e.Attachments)
|
||||||
|
.Include(e => e.Categories)
|
||||||
|
.Include(e => e.Tags)
|
||||||
|
.FilterWithVisibility(currentUser, isListing: true)
|
||||||
|
.Skip(offset)
|
||||||
|
.Take(take)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
Response.Headers["X-Total"] = totalCount.ToString();
|
||||||
|
|
||||||
|
return Ok(posts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PostRequest
|
||||||
|
{
|
||||||
|
[MaxLength(1024)] public string? Title { get; set; }
|
||||||
|
[MaxLength(4096)] public string? Description { get; set; }
|
||||||
|
public string? Content { get; set; }
|
||||||
|
public PostVisibility? Visibility { get; set; }
|
||||||
|
public PostType? Type { get; set; }
|
||||||
|
[MaxLength(16)] public List<string>? Tags { get; set; }
|
||||||
|
[MaxLength(8)] public List<string>? Categories { get; set; }
|
||||||
|
[MaxLength(32)] public List<string>? Attachments { get; set; }
|
||||||
|
public Dictionary<string, object>? Meta { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<ActionResult<Post>> CreatePost(
|
||||||
|
[FromBody] PostRequest request,
|
||||||
|
[FromHeader(Name = "X-Pub")] string? publisherName
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||||
|
|
||||||
|
Publisher? publisher;
|
||||||
|
if (publisherName is null)
|
||||||
|
{
|
||||||
|
// Use the first personal publisher
|
||||||
|
publisher = await db.Publishers.FirstOrDefaultAsync(e =>
|
||||||
|
e.AccountId == currentUser.Id && e.PublisherType == PublisherType.Individual);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
publisher = await db.Publishers.FirstOrDefaultAsync(e => e.Name == publisherName);
|
||||||
|
if (publisher is null) return BadRequest("Publisher was not found.");
|
||||||
|
var member =
|
||||||
|
await db.PublisherMembers.FirstOrDefaultAsync(e =>
|
||||||
|
e.AccountId == currentUser.Id && e.PublisherId == publisher.Id);
|
||||||
|
if (member is null) return StatusCode(403, "You even wasn't a member of the publisher you specified.");
|
||||||
|
if (member.Role < PublisherMemberRole.Editor)
|
||||||
|
return StatusCode(403, "You need at least be an editor to post as this publisher.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (publisher is null) return BadRequest("Publisher was not found.");
|
||||||
|
|
||||||
|
var post = new Post
|
||||||
|
{
|
||||||
|
Title = request.Title,
|
||||||
|
Description = request.Description,
|
||||||
|
Content = request.Content,
|
||||||
|
Visibility = request.Visibility ?? PostVisibility.Public,
|
||||||
|
Type = request.Type ?? PostType.Moment,
|
||||||
|
Meta = request.Meta,
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
post = await ps.PostAsync(
|
||||||
|
post,
|
||||||
|
attachments: request.Attachments,
|
||||||
|
tags: request.Tags,
|
||||||
|
categories: request.Categories
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException err)
|
||||||
|
{
|
||||||
|
return BadRequest(err.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return post;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPatch("{id:long}")]
|
||||||
|
public async Task<ActionResult<Post>> UpdatePost(long id, [FromBody] PostRequest request)
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||||
|
|
||||||
|
var post = await db.Posts
|
||||||
|
.Where(e => e.Id == id)
|
||||||
|
.Include(e => e.Publisher)
|
||||||
|
.Include(e => e.Attachments)
|
||||||
|
.Include(e => e.Categories)
|
||||||
|
.Include(e => e.Tags)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (post is null) return NotFound();
|
||||||
|
|
||||||
|
var member = await db.PublisherMembers
|
||||||
|
.FirstOrDefaultAsync(e => e.AccountId == currentUser.Id && e.PublisherId == post.Publisher.Id);
|
||||||
|
if (member is null) return StatusCode(403, "You even wasn't a member of the publisher you specified.");
|
||||||
|
if (member.Role < PublisherMemberRole.Editor)
|
||||||
|
return StatusCode(403, "You need at least be an editor to edit this publisher's post.");
|
||||||
|
|
||||||
|
if (request.Title is not null) post.Title = request.Title;
|
||||||
|
if (request.Description is not null) post.Description = request.Description;
|
||||||
|
if (request.Content is not null) post.Content = request.Content;
|
||||||
|
if (request.Visibility is not null) post.Visibility = request.Visibility.Value;
|
||||||
|
if (request.Type is not null) post.Type = request.Type.Value;
|
||||||
|
if (request.Meta is not null) post.Meta = request.Meta;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
post = await ps.UpdatePostAsync(
|
||||||
|
post,
|
||||||
|
attachments: request.Attachments,
|
||||||
|
tags: request.Tags,
|
||||||
|
categories: request.Categories
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException err)
|
||||||
|
{
|
||||||
|
return BadRequest(err.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(post);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("{id:long}")]
|
||||||
|
public async Task<ActionResult<Post>> DeletePost(long id)
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||||
|
|
||||||
|
var post = await db.Posts
|
||||||
|
.Where(e => e.Id == id)
|
||||||
|
.Include(e => e.Attachments)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (post is null) return NotFound();
|
||||||
|
|
||||||
|
var member = await db.PublisherMembers
|
||||||
|
.FirstOrDefaultAsync(e => e.AccountId == currentUser.Id && e.PublisherId == post.Publisher.Id);
|
||||||
|
if (member is null) return StatusCode(403, "You even wasn't a member of the publisher you specified.");
|
||||||
|
if (member.Role < PublisherMemberRole.Editor)
|
||||||
|
return StatusCode(403, "You need at least be an editor to delete the publisher's post.");
|
||||||
|
|
||||||
|
await ps.DeletePostAsync(post);
|
||||||
|
|
||||||
|
return NoContent();
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,157 @@
|
|||||||
|
using DysonNetwork.Sphere.Storage;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using NodaTime;
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Post;
|
namespace DysonNetwork.Sphere.Post;
|
||||||
|
|
||||||
public class PostService(AppDatabase db)
|
public class PostService(AppDatabase db, FileService fs)
|
||||||
{
|
{
|
||||||
|
public async Task<Post> PostAsync(
|
||||||
|
Post post,
|
||||||
|
List<string>? attachments = null,
|
||||||
|
List<string>? tags = null,
|
||||||
|
List<string>? categories = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (attachments is not null)
|
||||||
|
{
|
||||||
|
post.Attachments = await db.Files.Where(e => attachments.Contains(e.Id)).ToListAsync();
|
||||||
|
// Re-order the list to match the id list places
|
||||||
|
post.Attachments = attachments
|
||||||
|
.Select(id => post.Attachments.First(a => a.Id == id))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tags is not null)
|
||||||
|
{
|
||||||
|
var existingTags = await db.PostTags.Where(e => tags.Contains(e.Slug)).ToListAsync();
|
||||||
|
|
||||||
|
// Determine missing slugs
|
||||||
|
var existingSlugs = existingTags.Select(t => t.Slug).ToHashSet();
|
||||||
|
var missingSlugs = tags.Where(slug => !existingSlugs.Contains(slug)).ToList();
|
||||||
|
|
||||||
|
var newTags = missingSlugs.Select(slug => new PostTag { Slug = slug }).ToList();
|
||||||
|
if (newTags.Count > 0)
|
||||||
|
{
|
||||||
|
await db.PostTags.AddRangeAsync(newTags);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
post.Tags = existingTags.Concat(newTags).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (categories is not null)
|
||||||
|
{
|
||||||
|
post.Categories = await db.PostCategories.Where(e => categories.Contains(e.Slug)).ToListAsync();
|
||||||
|
if (post.Categories.Count != categories.Distinct().Count())
|
||||||
|
throw new InvalidOperationException("Categories contains one or more categories that wasn't exists.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (post.Empty)
|
||||||
|
throw new InvalidOperationException("Cannot create a post with barely no content.");
|
||||||
|
|
||||||
|
// TODO Notify the subscribers
|
||||||
|
|
||||||
|
db.Posts.Add(post);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
await fs.MarkUsageRangeAsync(post.Attachments, 1);
|
||||||
|
|
||||||
|
return post;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Post> UpdatePostAsync(
|
||||||
|
Post post,
|
||||||
|
List<string>? attachments = null,
|
||||||
|
List<string>? tags = null,
|
||||||
|
List<string>? categories = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
post.EditedAt = Instant.FromDateTimeUtc(DateTime.UtcNow);
|
||||||
|
|
||||||
|
if (attachments is not null)
|
||||||
|
{
|
||||||
|
var records = await db.Files.Where(e => attachments.Contains(e.Id)).ToListAsync();
|
||||||
|
|
||||||
|
var previous = post.Attachments.ToDictionary(f => f.Id);
|
||||||
|
var current = records.ToDictionary(f => f.Id);
|
||||||
|
|
||||||
|
// Detect added files
|
||||||
|
var added = current.Keys.Except(previous.Keys).Select(id => current[id]).ToList();
|
||||||
|
// Detect removed files
|
||||||
|
var removed = previous.Keys.Except(current.Keys).Select(id => previous[id]).ToList();
|
||||||
|
|
||||||
|
// Update attachments
|
||||||
|
post.Attachments = attachments.Select(id => current[id]).ToList();
|
||||||
|
|
||||||
|
// Call mark usage
|
||||||
|
await fs.MarkUsageRangeAsync(added, 1);
|
||||||
|
await fs.MarkUsageRangeAsync(removed, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tags is not null)
|
||||||
|
{
|
||||||
|
var existingTags = await db.PostTags.Where(e => tags.Contains(e.Slug)).ToListAsync();
|
||||||
|
|
||||||
|
// Determine missing slugs
|
||||||
|
var existingSlugs = existingTags.Select(t => t.Slug).ToHashSet();
|
||||||
|
var missingSlugs = tags.Where(slug => !existingSlugs.Contains(slug)).ToList();
|
||||||
|
|
||||||
|
var newTags = missingSlugs.Select(slug => new PostTag { Slug = slug }).ToList();
|
||||||
|
if (newTags.Count > 0)
|
||||||
|
{
|
||||||
|
await db.PostTags.AddRangeAsync(newTags);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
post.Tags = existingTags.Concat(newTags).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (categories is not null)
|
||||||
|
{
|
||||||
|
post.Categories = await db.PostCategories.Where(e => categories.Contains(e.Slug)).ToListAsync();
|
||||||
|
if (post.Categories.Count != categories.Distinct().Count())
|
||||||
|
throw new InvalidOperationException("Categories contains one or more categories that wasn't exists.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (post.Empty)
|
||||||
|
throw new InvalidOperationException("Cannot edit a post to barely no content.");
|
||||||
|
|
||||||
|
db.Update(post);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
|
return post;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeletePostAsync(Post post)
|
||||||
|
{
|
||||||
|
db.Posts.Remove(post);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
await fs.MarkUsageRangeAsync(post.Attachments, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PostQueryExtensions
|
||||||
|
{
|
||||||
|
public static IQueryable<Post> FilterWithVisibility(this IQueryable<Post> source, Account.Account? currentUser,
|
||||||
|
bool isListing = false)
|
||||||
|
{
|
||||||
|
var now = Instant.FromDateTimeUtc(DateTime.UtcNow);
|
||||||
|
|
||||||
|
source = isListing switch
|
||||||
|
{
|
||||||
|
true when currentUser is not null => source.Where(e =>
|
||||||
|
e.Visibility != PostVisibility.Unlisted || e.Publisher.AccountId == currentUser.Id),
|
||||||
|
true => source.Where(e => e.Visibility != PostVisibility.Unlisted),
|
||||||
|
_ => source
|
||||||
|
};
|
||||||
|
|
||||||
|
if (currentUser is null)
|
||||||
|
return source
|
||||||
|
.Where(e => e.PublishedAt != null && now >= e.PublishedAt)
|
||||||
|
.Where(e => e.Visibility == PostVisibility.Public);
|
||||||
|
|
||||||
|
return source
|
||||||
|
.Where(e => e.PublishedAt != null && now >= e.PublishedAt && e.Publisher.AccountId == currentUser.Id)
|
||||||
|
.Where(e => e.Visibility != PostVisibility.Private || e.Publisher.AccountId == currentUser.Id);
|
||||||
|
}
|
||||||
}
|
}
|
@ -2,6 +2,7 @@ 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.Sphere.Storage;
|
using DysonNetwork.Sphere.Storage;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
|
|
||||||
namespace DysonNetwork.Sphere.Post;
|
namespace DysonNetwork.Sphere.Post;
|
||||||
@ -12,6 +13,7 @@ public enum PublisherType
|
|||||||
Organizational
|
Organizational
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Index(nameof(Name), IsUnique = true)]
|
||||||
public class Publisher : ModelBase
|
public class Publisher : ModelBase
|
||||||
{
|
{
|
||||||
public long Id { get; set; }
|
public long Id { get; set; }
|
||||||
@ -24,7 +26,10 @@ public class Publisher : ModelBase
|
|||||||
public CloudFile? Background { get; set; }
|
public CloudFile? Background { get; set; }
|
||||||
|
|
||||||
[JsonIgnore] public ICollection<Post> Posts { get; set; } = new List<Post>();
|
[JsonIgnore] public ICollection<Post> Posts { get; set; } = new List<Post>();
|
||||||
|
[JsonIgnore] public ICollection<PostCollection> Collections { get; set; } = new List<PostCollection>();
|
||||||
[JsonIgnore] public ICollection<PublisherMember> Members { get; set; } = new List<PublisherMember>();
|
[JsonIgnore] public ICollection<PublisherMember> Members { get; set; } = new List<PublisherMember>();
|
||||||
|
|
||||||
|
public long? AccountId { get; set; }
|
||||||
[JsonIgnore] public Account.Account? Account { get; set; }
|
[JsonIgnore] public Account.Account? Account { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Casbin;
|
||||||
using DysonNetwork.Sphere.Storage;
|
using DysonNetwork.Sphere.Storage;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
@ -9,13 +10,13 @@ namespace DysonNetwork.Sphere.Post;
|
|||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("/publishers")]
|
[Route("/publishers")]
|
||||||
public class PublisherController(AppDatabase db, PublisherService ps, FileService fs) : ControllerBase
|
public class PublisherController(AppDatabase db, PublisherService ps, FileService fs, IEnforcer enforcer)
|
||||||
|
: ControllerBase
|
||||||
{
|
{
|
||||||
[HttpGet("{name}")]
|
[HttpGet("{name}")]
|
||||||
public async Task<ActionResult<Publisher>> GetPublisher(string name)
|
public async Task<ActionResult<Publisher>> GetPublisher(string name)
|
||||||
{
|
{
|
||||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||||
var userId = currentUser.Id;
|
|
||||||
|
|
||||||
var publisher = await db.Publishers
|
var publisher = await db.Publishers
|
||||||
.Where(e => e.Name == name)
|
.Where(e => e.Name == name)
|
||||||
@ -56,7 +57,7 @@ public class PublisherController(AppDatabase db, PublisherService ps, FileServic
|
|||||||
|
|
||||||
return members.ToList();
|
return members.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PublisherMemberRequest
|
public class PublisherMemberRequest
|
||||||
{
|
{
|
||||||
[Required] public long RelatedUserId { get; set; }
|
[Required] public long RelatedUserId { get; set; }
|
||||||
@ -65,11 +66,12 @@ public class PublisherController(AppDatabase db, PublisherService ps, FileServic
|
|||||||
|
|
||||||
[HttpPost("invites/{name}")]
|
[HttpPost("invites/{name}")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public async Task<ActionResult<PublisherMember>> InviteMember(string name, [FromBody] PublisherMemberRequest request)
|
public async Task<ActionResult<PublisherMember>> InviteMember(string name,
|
||||||
|
[FromBody] PublisherMemberRequest request)
|
||||||
{
|
{
|
||||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||||
var userId = currentUser.Id;
|
var userId = currentUser.Id;
|
||||||
|
|
||||||
var relatedUser = await db.Accounts.FindAsync(request.RelatedUserId);
|
var relatedUser = await db.Accounts.FindAsync(request.RelatedUserId);
|
||||||
if (relatedUser is null) return BadRequest("Related user was not found");
|
if (relatedUser is null) return BadRequest("Related user was not found");
|
||||||
|
|
||||||
@ -86,7 +88,8 @@ public class PublisherController(AppDatabase db, PublisherService ps, FileServic
|
|||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (member is null) return StatusCode(403, "You are not even a member of the targeted publisher.");
|
if (member is null) return StatusCode(403, "You are not even a member of the targeted publisher.");
|
||||||
if (member.Role < PublisherMemberRole.Manager)
|
if (member.Role < PublisherMemberRole.Manager)
|
||||||
return StatusCode(403, "You need at least be a manager to invite other members to collaborate this publisher.");
|
return StatusCode(403,
|
||||||
|
"You need at least be a manager to invite other members to collaborate this publisher.");
|
||||||
if (member.Role < request.Role)
|
if (member.Role < request.Role)
|
||||||
return StatusCode(403, "You cannot invite member has higher permission than yours.");
|
return StatusCode(403, "You cannot invite member has higher permission than yours.");
|
||||||
|
|
||||||
@ -98,10 +101,10 @@ public class PublisherController(AppDatabase db, PublisherService ps, FileServic
|
|||||||
PublisherId = publisher.Id,
|
PublisherId = publisher.Id,
|
||||||
Role = request.Role,
|
Role = request.Role,
|
||||||
};
|
};
|
||||||
|
|
||||||
db.PublisherMembers.Add(newMember);
|
db.PublisherMembers.Add(newMember);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
return Ok(newMember);
|
return Ok(newMember);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,35 +114,35 @@ public class PublisherController(AppDatabase db, PublisherService ps, FileServic
|
|||||||
{
|
{
|
||||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||||
var userId = currentUser.Id;
|
var userId = currentUser.Id;
|
||||||
|
|
||||||
var member = await db.PublisherMembers
|
var member = await db.PublisherMembers
|
||||||
.Where(m => m.AccountId == userId)
|
.Where(m => m.AccountId == userId)
|
||||||
.Where(m => m.Publisher.Name == name)
|
.Where(m => m.Publisher.Name == name)
|
||||||
.Where(m => m.JoinedAt == null)
|
.Where(m => m.JoinedAt == null)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (member is null) return NotFound();
|
if (member is null) return NotFound();
|
||||||
|
|
||||||
member.JoinedAt = Instant.FromDateTimeUtc(DateTime.UtcNow);
|
member.JoinedAt = Instant.FromDateTimeUtc(DateTime.UtcNow);
|
||||||
db.Update(member);
|
db.Update(member);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
return Ok(member);
|
return Ok(member);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("invites/{name}/decline")]
|
[HttpPost("invites/{name}/decline")]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public async Task<ActionResult> DeclineMemberInvite(string name)
|
public async Task<ActionResult> DeclineMemberInvite(string name)
|
||||||
{
|
{
|
||||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||||
var userId = currentUser.Id;
|
var userId = currentUser.Id;
|
||||||
|
|
||||||
var member = await db.PublisherMembers
|
var member = await db.PublisherMembers
|
||||||
.Where(m => m.AccountId == userId)
|
.Where(m => m.AccountId == userId)
|
||||||
.Where(m => m.Publisher.Name == name)
|
.Where(m => m.Publisher.Name == name)
|
||||||
.Where(m => m.JoinedAt == null)
|
.Where(m => m.JoinedAt == null)
|
||||||
.FirstOrDefaultAsync();
|
.FirstOrDefaultAsync();
|
||||||
if (member is null) return NotFound();
|
if (member is null) return NotFound();
|
||||||
|
|
||||||
db.PublisherMembers.Remove(member);
|
db.PublisherMembers.Remove(member);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
@ -161,6 +164,8 @@ public class PublisherController(AppDatabase db, PublisherService ps, FileServic
|
|||||||
public async Task<ActionResult<Publisher>> CreatePublisherIndividual(PublisherRequest request)
|
public async Task<ActionResult<Publisher>> CreatePublisherIndividual(PublisherRequest request)
|
||||||
{
|
{
|
||||||
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
if (HttpContext.Items["CurrentUser"] is not Account.Account currentUser) return Unauthorized();
|
||||||
|
if (!await enforcer.EnforceAsync(currentUser.Id.ToString(), "global", "publishers", "create"))
|
||||||
|
return StatusCode(403);
|
||||||
|
|
||||||
var takenName = request.Name ?? currentUser.Name;
|
var takenName = request.Name ?? currentUser.Name;
|
||||||
var duplicateNameCount = await db.Publishers
|
var duplicateNameCount = await db.Publishers
|
||||||
@ -276,10 +281,10 @@ public class PublisherController(AppDatabase db, PublisherService ps, FileServic
|
|||||||
await fs.MarkUsageAsync(publisher.Picture, -1);
|
await fs.MarkUsageAsync(publisher.Picture, -1);
|
||||||
if (publisher.Background is not null)
|
if (publisher.Background is not null)
|
||||||
await fs.MarkUsageAsync(publisher.Background, -1);
|
await fs.MarkUsageAsync(publisher.Background, -1);
|
||||||
|
|
||||||
db.Publishers.Remove(publisher);
|
db.Publishers.Remove(publisher);
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -158,10 +158,9 @@ public class FileService(AppDatabase db, IConfiguration configuration)
|
|||||||
);
|
);
|
||||||
|
|
||||||
file.UploadedAt = Instant.FromDateTimeUtc(DateTime.UtcNow);
|
file.UploadedAt = Instant.FromDateTimeUtc(DateTime.UtcNow);
|
||||||
await db.Files.Where(f => f.Id == file.Id).ExecuteUpdateAsync(
|
await db.Files.Where(f => f.Id == file.Id).ExecuteUpdateAsync(setter => setter
|
||||||
setter => setter
|
.SetProperty(f => f.UploadedAt, file.UploadedAt)
|
||||||
.SetProperty(f => f.UploadedAt, file.UploadedAt)
|
.SetProperty(f => f.UploadedTo, file.UploadedTo)
|
||||||
.SetProperty(f => f.UploadedTo, file.UploadedTo)
|
|
||||||
);
|
);
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
@ -215,8 +214,18 @@ public class FileService(AppDatabase db, IConfiguration configuration)
|
|||||||
public async Task MarkUsageAsync(CloudFile file, int delta)
|
public async Task MarkUsageAsync(CloudFile file, int delta)
|
||||||
{
|
{
|
||||||
await db.Files.Where(o => o.Id == file.Id)
|
await db.Files.Where(o => o.Id == file.Id)
|
||||||
.ExecuteUpdateAsync(
|
.ExecuteUpdateAsync(setter => setter.SetProperty(
|
||||||
setter => setter.SetProperty(
|
b => b.UsedCount,
|
||||||
|
b => b.UsedCount + delta
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task MarkUsageRangeAsync(ICollection<CloudFile> files, int delta)
|
||||||
|
{
|
||||||
|
var ids = files.Select(f => f.Id).ToArray();
|
||||||
|
await db.Files.Where(o => ids.Contains(o.Id))
|
||||||
|
.ExecuteUpdateAsync(setter => setter.SetProperty(
|
||||||
b => b.UsedCount,
|
b => b.UsedCount,
|
||||||
b => b.UsedCount + delta
|
b => b.UsedCount + delta
|
||||||
)
|
)
|
||||||
|
@ -10,7 +10,9 @@
|
|||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADbContext_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fa0b45f29f34f594814a7b1fbc25fe5ef3c18257956ed4f4fbfa68717db58_003FDbContext_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADbContext_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fa0b45f29f34f594814a7b1fbc25fe5ef3c18257956ed4f4fbfa68717db58_003FDbContext_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADirectory_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb6f0571a6bc744b0b551fd4578292582e54c00_003Fde_003F94973e27_003FDirectory_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADirectory_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb6f0571a6bc744b0b551fd4578292582e54c00_003Fde_003F94973e27_003FDirectory_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEndpointConventionBuilderExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8bb08a178b5b43c5bac20a5a54159a5b2a800_003F8a_003F101938e3_003FEndpointConventionBuilderExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEndpointConventionBuilderExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8bb08a178b5b43c5bac20a5a54159a5b2a800_003F8a_003F101938e3_003FEndpointConventionBuilderExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnforcerExtension_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fbb4a120e56464fc6abd8c30969ef70864ba00_003Fb5_003F180850e0_003FEnforcerExtension_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkQueryableExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fe096e6f12c5d6b49356bc34ff1ea08738f910c0929c9d717c9cba7f44288_003FEntityFrameworkQueryableExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkQueryableExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fe096e6f12c5d6b49356bc34ff1ea08738f910c0929c9d717c9cba7f44288_003FEntityFrameworkQueryableExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkQueryableExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fcb0587797ea44bd6915ede69888c6766291038_003F55_003F277f2d4c_003FEntityFrameworkQueryableExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkServiceCollectionExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F4a28847852ee9ba45fd3107526c0a749a733bd4f4ebf33aa3c9a59737a3f758_003FEntityFrameworkServiceCollectionExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntityFrameworkServiceCollectionExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F4a28847852ee9ba45fd3107526c0a749a733bd4f4ebf33aa3c9a59737a3f758_003FEntityFrameworkServiceCollectionExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnumerable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F832399abc13b45b6bdbabfa022e4a28487e00_003F7f_003F7aece4dd_003FEnumerable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnumerable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F832399abc13b45b6bdbabfa022e4a28487e00_003F7f_003F7aece4dd_003FEnumerable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEvents_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8bb08a178b5b43c5bac20a5a54159a5b2a800_003F20_003F86914b63_003FEvents_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEvents_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F8bb08a178b5b43c5bac20a5a54159a5b2a800_003F20_003F86914b63_003FEvents_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
@ -30,6 +32,7 @@
|
|||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOptionsConfigurationServiceCollectionExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6622dea924b14dc7aa3ee69d7c84e5735000_003Fe0_003F024ba0b7_003FOptionsConfigurationServiceCollectionExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AOptionsConfigurationServiceCollectionExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6622dea924b14dc7aa3ee69d7c84e5735000_003Fe0_003F024ba0b7_003FOptionsConfigurationServiceCollectionExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APath_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb6f0571a6bc744b0b551fd4578292582e54c00_003Fd3_003F7b05b2bd_003FPath_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APath_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fb6f0571a6bc744b0b551fd4578292582e54c00_003Fd3_003F7b05b2bd_003FPath_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APresignedGetObjectArgs_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F0df26a9d89e29319e9efcaea0a8489db9e97bc1aedcca3f7e360cc50f8f4ea_003FPresignedGetObjectArgs_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APresignedGetObjectArgs_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F0df26a9d89e29319e9efcaea0a8489db9e97bc1aedcca3f7e360cc50f8f4ea_003FPresignedGetObjectArgs_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AQueryable_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F42d8f09d6a294d00a6f49efc989927492fe00_003F4e_003F26d1ee34_003FQueryable_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASecuritySchemeType_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F29898ce74e3763a786ac1bd9a6db2152e1af75769440b1e53b9cbdf1dda1bd99_003FSecuritySchemeType_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASecuritySchemeType_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F29898ce74e3763a786ac1bd9a6db2152e1af75769440b1e53b9cbdf1dda1bd99_003FSecuritySchemeType_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AServiceCollectionContainerBuilderExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fc0e30e11d8f5456cb7a11b21ebee6c5a35c00_003F60_003F78b485f5_003FServiceCollectionContainerBuilderExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AServiceCollectionContainerBuilderExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003Fc0e30e11d8f5456cb7a11b21ebee6c5a35c00_003F60_003F78b485f5_003FServiceCollectionContainerBuilderExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASetPropertyCalls_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F458b5f22476b4599b87176214d5e4026c2327b148f4d3f885ee92362b4dac3_003FSetPropertyCalls_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASetPropertyCalls_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F458b5f22476b4599b87176214d5e4026c2327b148f4d3f885ee92362b4dac3_003FSetPropertyCalls_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user