✨ Mini apps in develop
This commit is contained in:
@@ -18,6 +18,7 @@ public class AppDatabase(
|
||||
public DbSet<SnCustomApp> CustomApps { get; set; } = null!;
|
||||
public DbSet<SnCustomAppSecret> CustomAppSecrets { get; set; } = null!;
|
||||
public DbSet<SnBotAccount> BotAccounts { get; set; } = null!;
|
||||
public DbSet<SnMiniApp> MiniApps { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
|
||||
@@ -28,5 +28,9 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DysonNetwork.Shared\DysonNetwork.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="MiniApp\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
382
DysonNetwork.Develop/Migrations/20260117175714_AddMiniApp.Designer.cs
generated
Normal file
382
DysonNetwork.Develop/Migrations/20260117175714_AddMiniApp.Designer.cs
generated
Normal file
@@ -0,0 +1,382 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using DysonNetwork.Develop;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using NodaTime;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace DysonNetwork.Develop.Migrations
|
||||
{
|
||||
[DbContext(typeof(AppDatabase))]
|
||||
[Migration("20260117175714_AddMiniApp")]
|
||||
partial class AddMiniApp
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.1")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnBotAccount", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("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<bool>("IsActive")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_active");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("project_id");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("slug");
|
||||
|
||||
b.Property<Instant>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_bot_accounts");
|
||||
|
||||
b.HasIndex("ProjectId")
|
||||
.HasDatabaseName("ix_bot_accounts_project_id");
|
||||
|
||||
b.ToTable("bot_accounts", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnCustomApp", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<SnCloudFileReferenceObject>("Background")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("background");
|
||||
|
||||
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<SnCustomAppLinks>("Links")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("links");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<SnCustomAppOauthConfig>("OauthConfig")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("oauth_config");
|
||||
|
||||
b.Property<SnCloudFileReferenceObject>("Picture")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("picture");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("project_id");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("slug");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("status");
|
||||
|
||||
b.Property<Instant>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at");
|
||||
|
||||
b.Property<SnVerificationMark>("Verification")
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("verification");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_custom_apps");
|
||||
|
||||
b.HasIndex("ProjectId")
|
||||
.HasDatabaseName("ix_custom_apps_project_id");
|
||||
|
||||
b.ToTable("custom_apps", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnCustomAppSecret", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<Guid>("AppId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("app_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<Instant?>("ExpiredAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("expired_at");
|
||||
|
||||
b.Property<bool>("IsOidc")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("is_oidc");
|
||||
|
||||
b.Property<string>("Secret")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("secret");
|
||||
|
||||
b.Property<Instant>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_custom_app_secrets");
|
||||
|
||||
b.HasIndex("AppId")
|
||||
.HasDatabaseName("ix_custom_app_secrets_app_id");
|
||||
|
||||
b.ToTable("custom_app_secrets", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnDevProject", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("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")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4096)
|
||||
.HasColumnType("character varying(4096)")
|
||||
.HasColumnName("description");
|
||||
|
||||
b.Property<Guid>("DeveloperId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("developer_id");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("slug");
|
||||
|
||||
b.Property<Instant>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_dev_projects");
|
||||
|
||||
b.HasIndex("DeveloperId")
|
||||
.HasDatabaseName("ix_dev_projects_developer_id");
|
||||
|
||||
b.ToTable("dev_projects", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnDeveloper", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<Guid>("PublisherId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("publisher_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_developers");
|
||||
|
||||
b.ToTable("developers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnMiniApp", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("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<MiniAppManifest>("Manifest")
|
||||
.IsRequired()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("manifest");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("project_id");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("slug");
|
||||
|
||||
b.Property<int>("Stage")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("stage");
|
||||
|
||||
b.Property<Instant>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_mini_apps");
|
||||
|
||||
b.HasIndex("ProjectId")
|
||||
.HasDatabaseName("ix_mini_apps_project_id");
|
||||
|
||||
b.ToTable("mini_apps", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnBotAccount", b =>
|
||||
{
|
||||
b.HasOne("DysonNetwork.Shared.Models.SnDevProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_bot_accounts_dev_projects_project_id");
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnCustomApp", b =>
|
||||
{
|
||||
b.HasOne("DysonNetwork.Shared.Models.SnDevProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_custom_apps_dev_projects_project_id");
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnCustomAppSecret", b =>
|
||||
{
|
||||
b.HasOne("DysonNetwork.Shared.Models.SnCustomApp", "App")
|
||||
.WithMany("Secrets")
|
||||
.HasForeignKey("AppId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_custom_app_secrets_custom_apps_app_id");
|
||||
|
||||
b.Navigation("App");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnDevProject", b =>
|
||||
{
|
||||
b.HasOne("DysonNetwork.Shared.Models.SnDeveloper", "Developer")
|
||||
.WithMany("Projects")
|
||||
.HasForeignKey("DeveloperId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_dev_projects_developers_developer_id");
|
||||
|
||||
b.Navigation("Developer");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnMiniApp", b =>
|
||||
{
|
||||
b.HasOne("DysonNetwork.Shared.Models.SnDevProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_mini_apps_dev_projects_project_id");
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnCustomApp", b =>
|
||||
{
|
||||
b.Navigation("Secrets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnDeveloper", b =>
|
||||
{
|
||||
b.Navigation("Projects");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
53
DysonNetwork.Develop/Migrations/20260117175714_AddMiniApp.cs
Normal file
53
DysonNetwork.Develop/Migrations/20260117175714_AddMiniApp.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using NodaTime;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace DysonNetwork.Develop.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddMiniApp : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "mini_apps",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
slug = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: false),
|
||||
stage = table.Column<int>(type: "integer", nullable: false),
|
||||
manifest = table.Column<MiniAppManifest>(type: "jsonb", nullable: false),
|
||||
project_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
|
||||
updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
|
||||
deleted_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("pk_mini_apps", x => x.id);
|
||||
table.ForeignKey(
|
||||
name: "fk_mini_apps_dev_projects_project_id",
|
||||
column: x => x.project_id,
|
||||
principalTable: "dev_projects",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "ix_mini_apps_project_id",
|
||||
table: "mini_apps",
|
||||
column: "project_id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "mini_apps");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,12 +19,12 @@ namespace DysonNetwork.Develop.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.7")
|
||||
.HasAnnotation("ProductVersion", "10.0.1")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Develop.Identity.BotAccount", b =>
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnBotAccount", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -66,7 +66,7 @@ namespace DysonNetwork.Develop.Migrations
|
||||
b.ToTable("bot_accounts", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Develop.Identity.CustomApp", b =>
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnCustomApp", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -139,7 +139,7 @@ namespace DysonNetwork.Develop.Migrations
|
||||
b.ToTable("custom_apps", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Develop.Identity.CustomAppSecret", b =>
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnCustomAppSecret", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -190,24 +190,7 @@ namespace DysonNetwork.Develop.Migrations
|
||||
b.ToTable("custom_app_secrets", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Develop.Identity.Developer", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<Guid>("PublisherId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("publisher_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_developers");
|
||||
|
||||
b.ToTable("developers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Develop.Project.DevProject", b =>
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnDevProject", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
@@ -257,9 +240,73 @@ namespace DysonNetwork.Develop.Migrations
|
||||
b.ToTable("dev_projects", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Develop.Identity.BotAccount", b =>
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnDeveloper", b =>
|
||||
{
|
||||
b.HasOne("DysonNetwork.Develop.Project.DevProject", "Project")
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("id");
|
||||
|
||||
b.Property<Guid>("PublisherId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("publisher_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_developers");
|
||||
|
||||
b.ToTable("developers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnMiniApp", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("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<MiniAppManifest>("Manifest")
|
||||
.IsRequired()
|
||||
.HasColumnType("jsonb")
|
||||
.HasColumnName("manifest");
|
||||
|
||||
b.Property<Guid>("ProjectId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("project_id");
|
||||
|
||||
b.Property<string>("Slug")
|
||||
.IsRequired()
|
||||
.HasMaxLength(1024)
|
||||
.HasColumnType("character varying(1024)")
|
||||
.HasColumnName("slug");
|
||||
|
||||
b.Property<int>("Stage")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("stage");
|
||||
|
||||
b.Property<Instant>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("updated_at");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("pk_mini_apps");
|
||||
|
||||
b.HasIndex("ProjectId")
|
||||
.HasDatabaseName("ix_mini_apps_project_id");
|
||||
|
||||
b.ToTable("mini_apps", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnBotAccount", b =>
|
||||
{
|
||||
b.HasOne("DysonNetwork.Shared.Models.SnDevProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -269,9 +316,9 @@ namespace DysonNetwork.Develop.Migrations
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Develop.Identity.CustomApp", b =>
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnCustomApp", b =>
|
||||
{
|
||||
b.HasOne("DysonNetwork.Develop.Project.DevProject", "Project")
|
||||
b.HasOne("DysonNetwork.Shared.Models.SnDevProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -281,9 +328,9 @@ namespace DysonNetwork.Develop.Migrations
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Develop.Identity.CustomAppSecret", b =>
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnCustomAppSecret", b =>
|
||||
{
|
||||
b.HasOne("DysonNetwork.Develop.Identity.CustomApp", "App")
|
||||
b.HasOne("DysonNetwork.Shared.Models.SnCustomApp", "App")
|
||||
.WithMany("Secrets")
|
||||
.HasForeignKey("AppId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -293,9 +340,9 @@ namespace DysonNetwork.Develop.Migrations
|
||||
b.Navigation("App");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Develop.Project.DevProject", b =>
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnDevProject", b =>
|
||||
{
|
||||
b.HasOne("DysonNetwork.Develop.Identity.Developer", "Developer")
|
||||
b.HasOne("DysonNetwork.Shared.Models.SnDeveloper", "Developer")
|
||||
.WithMany("Projects")
|
||||
.HasForeignKey("DeveloperId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
@@ -305,12 +352,24 @@ namespace DysonNetwork.Develop.Migrations
|
||||
b.Navigation("Developer");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Develop.Identity.CustomApp", b =>
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnMiniApp", b =>
|
||||
{
|
||||
b.HasOne("DysonNetwork.Shared.Models.SnDevProject", "Project")
|
||||
.WithMany()
|
||||
.HasForeignKey("ProjectId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("fk_mini_apps_dev_projects_project_id");
|
||||
|
||||
b.Navigation("Project");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnCustomApp", b =>
|
||||
{
|
||||
b.Navigation("Secrets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("DysonNetwork.Develop.Identity.Developer", b =>
|
||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnDeveloper", b =>
|
||||
{
|
||||
b.Navigation("Projects");
|
||||
});
|
||||
|
||||
185
DysonNetwork.Develop/MiniApp/MiniAppController.cs
Normal file
185
DysonNetwork.Develop/MiniApp/MiniAppController.cs
Normal file
@@ -0,0 +1,185 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using DysonNetwork.Develop.Project;
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace DysonNetwork.Develop.MiniApp;
|
||||
|
||||
[ApiController]
|
||||
[Route("/api/developers/{pubName}/projects/{projectId:guid}/miniapps")]
|
||||
public class MiniAppController(MiniAppService miniAppService, Identity.DeveloperService ds, DevProjectService projectService)
|
||||
: ControllerBase
|
||||
{
|
||||
public record MiniAppRequest(
|
||||
[MaxLength(1024)] string? Slug,
|
||||
MiniAppStage? Stage,
|
||||
MiniAppManifest? Manifest
|
||||
);
|
||||
|
||||
public record CreateMiniAppRequest(
|
||||
[Required]
|
||||
[MinLength(2)]
|
||||
[MaxLength(1024)]
|
||||
[RegularExpression(@"^[A-Za-z0-9_-]+$",
|
||||
ErrorMessage = "Slug can only contain letters, numbers, underscores, and hyphens.")]
|
||||
string Slug,
|
||||
|
||||
MiniAppStage Stage = MiniAppStage.Development,
|
||||
|
||||
[Required] MiniAppManifest Manifest = null!
|
||||
);
|
||||
|
||||
[HttpGet]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> ListMiniApps([FromRoute] string pubName, [FromRoute] Guid projectId)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Shared.Proto.Account currentUser)
|
||||
return Unauthorized();
|
||||
|
||||
var developer = await ds.GetDeveloperByName(pubName);
|
||||
if (developer is null) return NotFound("Developer not found");
|
||||
|
||||
var accountId = Guid.Parse(currentUser.Id);
|
||||
if (!await ds.IsMemberWithRole(developer.PublisherId, accountId, Shared.Proto.PublisherMemberRole.Viewer))
|
||||
return StatusCode(403, "You must be a viewer of the developer to list mini apps");
|
||||
|
||||
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||
if (project is null) return NotFound("Project not found or you don't have access");
|
||||
|
||||
var miniApps = await miniAppService.GetMiniAppsByProjectAsync(projectId);
|
||||
return Ok(miniApps);
|
||||
}
|
||||
|
||||
[HttpGet("{miniAppId:guid}")]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> GetMiniApp([FromRoute] string pubName, [FromRoute] Guid projectId,
|
||||
[FromRoute] Guid miniAppId)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Shared.Proto.Account currentUser)
|
||||
return Unauthorized();
|
||||
|
||||
var developer = await ds.GetDeveloperByName(pubName);
|
||||
if (developer is null) return NotFound("Developer not found");
|
||||
|
||||
var accountId = Guid.Parse(currentUser.Id);
|
||||
if (!await ds.IsMemberWithRole(developer.PublisherId, accountId, Shared.Proto.PublisherMemberRole.Viewer))
|
||||
return StatusCode(403, "You must be a viewer of the developer to view mini app details");
|
||||
|
||||
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||
if (project is null) return NotFound("Project not found or you don't have access");
|
||||
|
||||
var miniApp = await miniAppService.GetMiniAppByIdAsync(miniAppId);
|
||||
if (miniApp == null || miniApp.ProjectId != projectId)
|
||||
return NotFound("Mini app not found");
|
||||
|
||||
return Ok(miniApp);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> CreateMiniApp(
|
||||
[FromRoute] string pubName,
|
||||
[FromRoute] Guid projectId,
|
||||
[FromBody] CreateMiniAppRequest request)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Shared.Proto.Account currentUser)
|
||||
return Unauthorized();
|
||||
|
||||
var developer = await ds.GetDeveloperByName(pubName);
|
||||
if (developer is null)
|
||||
return NotFound("Developer not found");
|
||||
|
||||
if (!await ds.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), Shared.Proto.PublisherMemberRole.Editor))
|
||||
return StatusCode(403, "You must be an editor of the developer to create a mini app");
|
||||
|
||||
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||
if (project is null)
|
||||
return NotFound("Project not found or you don't have access");
|
||||
|
||||
try
|
||||
{
|
||||
var miniApp = await miniAppService.CreateMiniAppAsync(projectId, request.Slug, request.Stage, request.Manifest);
|
||||
return CreatedAtAction(
|
||||
nameof(GetMiniApp),
|
||||
new { pubName, projectId, miniAppId = miniApp.Id },
|
||||
miniApp
|
||||
);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPatch("{miniAppId:guid}")]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> UpdateMiniApp(
|
||||
[FromRoute] string pubName,
|
||||
[FromRoute] Guid projectId,
|
||||
[FromRoute] Guid miniAppId,
|
||||
[FromBody] MiniAppRequest request
|
||||
)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Shared.Proto.Account currentUser)
|
||||
return Unauthorized();
|
||||
|
||||
var developer = await ds.GetDeveloperByName(pubName);
|
||||
if (developer is null)
|
||||
return NotFound("Developer not found");
|
||||
|
||||
if (!await ds.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), Shared.Proto.PublisherMemberRole.Editor))
|
||||
return StatusCode(403, "You must be an editor of the developer to update a mini app");
|
||||
|
||||
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||
if (project is null)
|
||||
return NotFound("Project not found or you don't have access");
|
||||
|
||||
var miniApp = await miniAppService.GetMiniAppByIdAsync(miniAppId);
|
||||
if (miniApp == null || miniApp.ProjectId != projectId)
|
||||
return NotFound("Mini app not found");
|
||||
|
||||
try
|
||||
{
|
||||
miniApp = await miniAppService.UpdateMiniAppAsync(miniApp, request.Slug, request.Stage, request.Manifest);
|
||||
return Ok(miniApp);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{miniAppId:guid}")]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> DeleteMiniApp(
|
||||
[FromRoute] string pubName,
|
||||
[FromRoute] Guid projectId,
|
||||
[FromRoute] Guid miniAppId
|
||||
)
|
||||
{
|
||||
if (HttpContext.Items["CurrentUser"] is not Shared.Proto.Account currentUser)
|
||||
return Unauthorized();
|
||||
|
||||
var developer = await ds.GetDeveloperByName(pubName);
|
||||
if (developer is null)
|
||||
return NotFound("Developer not found");
|
||||
|
||||
if (!await ds.IsMemberWithRole(developer.PublisherId, Guid.Parse(currentUser.Id), Shared.Proto.PublisherMemberRole.Editor))
|
||||
return StatusCode(403, "You must be an editor of the developer to delete a mini app");
|
||||
|
||||
var project = await projectService.GetProjectAsync(projectId, developer.Id);
|
||||
if (project is null)
|
||||
return NotFound("Project not found or you don't have access");
|
||||
|
||||
var miniApp = await miniAppService.GetMiniAppByIdAsync(miniAppId);
|
||||
if (miniApp == null || miniApp.ProjectId != projectId)
|
||||
return NotFound("Mini app not found");
|
||||
|
||||
var result = await miniAppService.DeleteMiniAppAsync(miniAppId);
|
||||
if (!result)
|
||||
return NotFound("Failed to delete mini app");
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
}
|
||||
22
DysonNetwork.Develop/MiniApp/MiniAppPublicController.cs
Normal file
22
DysonNetwork.Develop/MiniApp/MiniAppPublicController.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace DysonNetwork.Develop.MiniApp;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/miniapps")]
|
||||
public class MiniAppPublicController(MiniAppService miniAppService, Identity.DeveloperService developerService) : ControllerBase
|
||||
{
|
||||
[HttpGet("{slug}")]
|
||||
public async Task<ActionResult<SnMiniApp>> GetMiniAppBySlug([FromRoute] string slug)
|
||||
{
|
||||
var miniApp = await miniAppService.GetMiniAppBySlugAsync(slug);
|
||||
if (miniApp is null) return NotFound("Mini app not found");
|
||||
|
||||
var developer = await developerService.GetDeveloperById(miniApp.Project.DeveloperId);
|
||||
if (developer is null) return NotFound("Developer not found");
|
||||
miniApp.Developer = await developerService.LoadDeveloperPublisher(developer);
|
||||
|
||||
return Ok(miniApp);
|
||||
}
|
||||
}
|
||||
92
DysonNetwork.Develop/MiniApp/MiniAppService.cs
Normal file
92
DysonNetwork.Develop/MiniApp/MiniAppService.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using DysonNetwork.Shared.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace DysonNetwork.Develop.MiniApp;
|
||||
|
||||
public class MiniAppService(AppDatabase db)
|
||||
{
|
||||
public async Task<SnMiniApp?> GetMiniAppByIdAsync(Guid id)
|
||||
{
|
||||
return await db.MiniApps
|
||||
.Include(m => m.Project)
|
||||
.FirstOrDefaultAsync(m => m.Id == id);
|
||||
}
|
||||
|
||||
public async Task<SnMiniApp?> GetMiniAppBySlugAsync(string slug)
|
||||
{
|
||||
return await db.MiniApps
|
||||
.Include(m => m.Project)
|
||||
.FirstOrDefaultAsync(m => m.Slug == slug);
|
||||
}
|
||||
|
||||
public async Task<List<SnMiniApp>> GetMiniAppsByProjectAsync(Guid projectId)
|
||||
{
|
||||
return await db.MiniApps
|
||||
.Where(m => m.ProjectId == projectId)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<SnMiniApp> CreateMiniAppAsync(Guid projectId, string slug, MiniAppStage stage, MiniAppManifest manifest)
|
||||
{
|
||||
var project = await db.DevProjects.FindAsync(projectId);
|
||||
if (project == null)
|
||||
throw new ArgumentException("Project not found");
|
||||
|
||||
// Check if a mini app with this slug already exists globally
|
||||
var existingMiniApp = await db.MiniApps
|
||||
.FirstOrDefaultAsync(m => m.Slug == slug);
|
||||
|
||||
if (existingMiniApp != null)
|
||||
throw new InvalidOperationException("A mini app with this slug already exists.");
|
||||
|
||||
var miniApp = new SnMiniApp
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Slug = slug,
|
||||
Stage = stage,
|
||||
Manifest = manifest,
|
||||
ProjectId = projectId,
|
||||
Project = project
|
||||
};
|
||||
|
||||
db.MiniApps.Add(miniApp);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
return miniApp;
|
||||
}
|
||||
|
||||
public async Task<SnMiniApp> UpdateMiniAppAsync(SnMiniApp miniApp, string? slug, MiniAppStage? stage, MiniAppManifest? manifest)
|
||||
{
|
||||
if (slug != null && slug != miniApp.Slug)
|
||||
{
|
||||
// Check if another mini app with this slug already exists globally
|
||||
var existingMiniApp = await db.MiniApps
|
||||
.FirstOrDefaultAsync(m => m.Slug == slug && m.Id != miniApp.Id);
|
||||
|
||||
if (existingMiniApp != null)
|
||||
throw new InvalidOperationException("A mini app with this slug already exists.");
|
||||
|
||||
miniApp.Slug = slug;
|
||||
}
|
||||
|
||||
if (stage.HasValue) miniApp.Stage = stage.Value;
|
||||
if (manifest != null) miniApp.Manifest = manifest;
|
||||
|
||||
db.Update(miniApp);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
return miniApp;
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteMiniAppAsync(Guid id)
|
||||
{
|
||||
var miniApp = await db.MiniApps.FindAsync(id);
|
||||
if (miniApp == null)
|
||||
return false;
|
||||
|
||||
db.MiniApps.Remove(miniApp);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,7 @@ public static class ServiceCollectionExtensions
|
||||
services.AddScoped<CustomAppService>();
|
||||
services.AddScoped<DevProjectService>();
|
||||
services.AddScoped<BotAccountService>();
|
||||
services.AddScoped<MiniApp.MiniAppService>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
31
DysonNetwork.Shared/Models/MiniApp.cs
Normal file
31
DysonNetwork.Shared/Models/MiniApp.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace DysonNetwork.Shared.Models;
|
||||
|
||||
public enum MiniAppStage
|
||||
{
|
||||
Development,
|
||||
Staging,
|
||||
Production
|
||||
}
|
||||
|
||||
public class MiniAppManifest
|
||||
{
|
||||
public string EntryUrl { get; set; } = null!;
|
||||
}
|
||||
|
||||
public class SnMiniApp : ModelBase
|
||||
{
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
[MaxLength(1024)] public string Slug { get; set; } = null!;
|
||||
|
||||
public MiniAppStage Stage { get; set; } = MiniAppStage.Development;
|
||||
|
||||
[Column(TypeName = "jsonb")] public MiniAppManifest Manifest { get; set; } = null!;
|
||||
|
||||
public Guid ProjectId { get; set; }
|
||||
public SnDevProject Project { get; set; } = null!;
|
||||
|
||||
[NotMapped] public SnDeveloper? Developer { get; set; }
|
||||
}
|
||||
@@ -114,7 +114,7 @@ public class PostActionController(
|
||||
Content = request.Content,
|
||||
Visibility = request.Visibility ?? Shared.Models.PostVisibility.Public,
|
||||
PublishedAt = request.PublishedAt,
|
||||
Type = request.Type ?? Shared.Models.PostType.Moment,
|
||||
Type = request.Type ?? PostType.Moment,
|
||||
Metadata = request.Meta,
|
||||
EmbedView = request.EmbedView,
|
||||
Publisher = publisher,
|
||||
|
||||
Reference in New Issue
Block a user