✨ 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<SnCustomApp> CustomApps { get; set; } = null!;
|
||||||
public DbSet<SnCustomAppSecret> CustomAppSecrets { get; set; } = null!;
|
public DbSet<SnCustomAppSecret> CustomAppSecrets { get; set; } = null!;
|
||||||
public DbSet<SnBotAccount> BotAccounts { get; set; } = null!;
|
public DbSet<SnBotAccount> BotAccounts { get; set; } = null!;
|
||||||
|
public DbSet<SnMiniApp> MiniApps { get; set; }
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,5 +28,9 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\DysonNetwork.Shared\DysonNetwork.Shared.csproj" />
|
<ProjectReference Include="..\DysonNetwork.Shared\DysonNetwork.Shared.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="MiniApp\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</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
|
#pragma warning disable 612, 618
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.HasAnnotation("ProductVersion", "9.0.7")
|
.HasAnnotation("ProductVersion", "10.0.1")
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Develop.Identity.BotAccount", b =>
|
modelBuilder.Entity("DysonNetwork.Shared.Models.SnBotAccount", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -66,7 +66,7 @@ namespace DysonNetwork.Develop.Migrations
|
|||||||
b.ToTable("bot_accounts", (string)null);
|
b.ToTable("bot_accounts", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Develop.Identity.CustomApp", b =>
|
modelBuilder.Entity("DysonNetwork.Shared.Models.SnCustomApp", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -139,7 +139,7 @@ namespace DysonNetwork.Develop.Migrations
|
|||||||
b.ToTable("custom_apps", (string)null);
|
b.ToTable("custom_apps", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Develop.Identity.CustomAppSecret", b =>
|
modelBuilder.Entity("DysonNetwork.Shared.Models.SnCustomAppSecret", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -190,24 +190,7 @@ namespace DysonNetwork.Develop.Migrations
|
|||||||
b.ToTable("custom_app_secrets", (string)null);
|
b.ToTable("custom_app_secrets", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Develop.Identity.Developer", b =>
|
modelBuilder.Entity("DysonNetwork.Shared.Models.SnDevProject", 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 =>
|
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -257,9 +240,73 @@ namespace DysonNetwork.Develop.Migrations
|
|||||||
b.ToTable("dev_projects", (string)null);
|
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()
|
.WithMany()
|
||||||
.HasForeignKey("ProjectId")
|
.HasForeignKey("ProjectId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -269,9 +316,9 @@ namespace DysonNetwork.Develop.Migrations
|
|||||||
b.Navigation("Project");
|
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()
|
.WithMany()
|
||||||
.HasForeignKey("ProjectId")
|
.HasForeignKey("ProjectId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -281,9 +328,9 @@ namespace DysonNetwork.Develop.Migrations
|
|||||||
b.Navigation("Project");
|
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")
|
.WithMany("Secrets")
|
||||||
.HasForeignKey("AppId")
|
.HasForeignKey("AppId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -293,9 +340,9 @@ namespace DysonNetwork.Develop.Migrations
|
|||||||
b.Navigation("App");
|
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")
|
.WithMany("Projects")
|
||||||
.HasForeignKey("DeveloperId")
|
.HasForeignKey("DeveloperId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -305,12 +352,24 @@ namespace DysonNetwork.Develop.Migrations
|
|||||||
b.Navigation("Developer");
|
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");
|
b.Navigation("Secrets");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Develop.Identity.Developer", b =>
|
modelBuilder.Entity("DysonNetwork.Shared.Models.SnDeveloper", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Projects");
|
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<CustomAppService>();
|
||||||
services.AddScoped<DevProjectService>();
|
services.AddScoped<DevProjectService>();
|
||||||
services.AddScoped<BotAccountService>();
|
services.AddScoped<BotAccountService>();
|
||||||
|
services.AddScoped<MiniApp.MiniAppService>();
|
||||||
|
|
||||||
return services;
|
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,
|
Content = request.Content,
|
||||||
Visibility = request.Visibility ?? Shared.Models.PostVisibility.Public,
|
Visibility = request.Visibility ?? Shared.Models.PostVisibility.Public,
|
||||||
PublishedAt = request.PublishedAt,
|
PublishedAt = request.PublishedAt,
|
||||||
Type = request.Type ?? Shared.Models.PostType.Moment,
|
Type = request.Type ?? PostType.Moment,
|
||||||
Metadata = request.Meta,
|
Metadata = request.Meta,
|
||||||
EmbedView = request.EmbedView,
|
EmbedView = request.EmbedView,
|
||||||
Publisher = publisher,
|
Publisher = publisher,
|
||||||
|
|||||||
Reference in New Issue
Block a user