Merge pull request #457
Updated dependencies. Refactored to async scheme
This commit is contained in:
@@ -10,15 +10,15 @@ await startup.Run(args, pluginLoader.Instances);
|
|||||||
|
|
||||||
var cs = new Startup();
|
var cs = new Startup();
|
||||||
|
|
||||||
await cs.Initialize(args, pluginLoader.Instances);
|
await cs.InitializeAsync(args, pluginLoader.Instances);
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
await cs.AddMoonlight(builder);
|
await cs.AddMoonlightAsync(builder);
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
await cs.AddMoonlight(app);
|
await cs.AddMoonlightAsync(app);
|
||||||
|
|
||||||
// Handle setup of wasm app hosting in the runtime
|
// Handle setup of wasm app hosting in the runtime
|
||||||
// so the Moonlight.ApiServer doesn't need the wasm package
|
// so the Moonlight.ApiServer doesn't need the wasm package
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ public record AppConfiguration
|
|||||||
public record FilesData
|
public record FilesData
|
||||||
{
|
{
|
||||||
[YamlMember(Description = "The maximum file size limit a combine operation is allowed to process")]
|
[YamlMember(Description = "The maximum file size limit a combine operation is allowed to process")]
|
||||||
public long CombineLimit { get; set; } = ByteConverter.FromGigaBytes(5).MegaBytes;
|
public double CombineLimit { get; set; } = ByteConverter.FromGigaBytes(5).MegaBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public record FrontendData
|
public record FrontendData
|
||||||
|
|||||||
@@ -1,34 +1,47 @@
|
|||||||
using Hangfire.EntityFrameworkCore;
|
using Hangfire.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using MoonCore.Extended.SingleDb;
|
|
||||||
using Moonlight.ApiServer.Configuration;
|
using Moonlight.ApiServer.Configuration;
|
||||||
using Moonlight.ApiServer.Database.Entities;
|
using Moonlight.ApiServer.Database.Entities;
|
||||||
using Moonlight.ApiServer.Models;
|
using Moonlight.ApiServer.Models;
|
||||||
|
|
||||||
namespace Moonlight.ApiServer.Database;
|
namespace Moonlight.ApiServer.Database;
|
||||||
|
|
||||||
public class CoreDataContext : DatabaseContext
|
public class CoreDataContext : DbContext
|
||||||
{
|
{
|
||||||
public override string Prefix { get; } = "Core";
|
private readonly AppConfiguration Configuration;
|
||||||
|
|
||||||
public DbSet<User> Users { get; set; }
|
public DbSet<User> Users { get; set; }
|
||||||
public DbSet<ApiKey> ApiKeys { get; set; }
|
public DbSet<ApiKey> ApiKeys { get; set; }
|
||||||
public DbSet<Theme> Themes { get; set; }
|
public DbSet<Theme> Themes { get; set; }
|
||||||
|
|
||||||
public CoreDataContext(AppConfiguration configuration)
|
public CoreDataContext(AppConfiguration configuration)
|
||||||
{
|
{
|
||||||
Options = new()
|
Configuration = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
|
{
|
||||||
|
if(optionsBuilder.IsConfigured)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var database = Configuration.Database;
|
||||||
|
|
||||||
|
var connectionString = $"Host={database.Host};" +
|
||||||
|
$"Port={database.Port};" +
|
||||||
|
$"Database={database.Database};" +
|
||||||
|
$"Username={database.Username};" +
|
||||||
|
$"Password={database.Password}";
|
||||||
|
|
||||||
|
optionsBuilder.UseNpgsql(connectionString, builder =>
|
||||||
{
|
{
|
||||||
Host = configuration.Database.Host,
|
builder.MigrationsHistoryTable("MigrationsHistory", "core");
|
||||||
Port = configuration.Database.Port,
|
});
|
||||||
Username = configuration.Database.Username,
|
|
||||||
Password = configuration.Database.Password,
|
|
||||||
Database = configuration.Database.Database
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
|
modelBuilder.Model.SetDefaultSchema("core");
|
||||||
|
|
||||||
base.OnModelCreating(modelBuilder);
|
base.OnModelCreating(modelBuilder);
|
||||||
modelBuilder.OnHangfireModelCreating();
|
modelBuilder.OnHangfireModelCreating();
|
||||||
|
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using Moonlight.ApiServer.Database;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.ApiServer.Database.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(CoreDataContext))]
|
|
||||||
[Migration("20250226080942_AddedUsersAndApiKey")]
|
|
||||||
partial class AddedUsersAndApiKey
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasAnnotation("ProductVersion", "8.0.11")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.ApiKey", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<DateTime>("ExpiresAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("PermissionsJson")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("jsonb");
|
|
||||||
|
|
||||||
b.Property<string>("Secret")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Core_ApiKeys", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.User", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Password")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("PermissionsJson")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("jsonb");
|
|
||||||
|
|
||||||
b.Property<DateTime>("TokenValidTimestamp")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Username")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Core_Users", (string)null);
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.ApiServer.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddedUsersAndApiKey : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Core_ApiKeys",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
Secret = table.Column<string>(type: "text", nullable: false),
|
|
||||||
Description = table.Column<string>(type: "text", nullable: false),
|
|
||||||
PermissionsJson = table.Column<string>(type: "jsonb", nullable: false),
|
|
||||||
ExpiresAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Core_ApiKeys", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Core_Users",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
Username = table.Column<string>(type: "text", nullable: false),
|
|
||||||
Email = table.Column<string>(type: "text", nullable: false),
|
|
||||||
Password = table.Column<string>(type: "text", nullable: false),
|
|
||||||
TokenValidTimestamp = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
|
||||||
PermissionsJson = table.Column<string>(type: "jsonb", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Core_Users", x => x.Id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Core_ApiKeys");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Core_Users");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using Moonlight.ApiServer.Database;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.ApiServer.Database.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(CoreDataContext))]
|
|
||||||
[Migration("20250314095412_ModifiedApiKeyEntity")]
|
|
||||||
partial class ModifiedApiKeyEntity
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasAnnotation("ProductVersion", "8.0.11")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.ApiKey", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<DateTime>("ExpiresAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("PermissionsJson")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("jsonb");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Core_ApiKeys", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.User", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Password")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("PermissionsJson")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("jsonb");
|
|
||||||
|
|
||||||
b.Property<DateTime>("TokenValidTimestamp")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Username")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Core_Users", (string)null);
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.ApiServer.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class ModifiedApiKeyEntity : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "Secret",
|
|
||||||
table: "Core_ApiKeys");
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<DateTime>(
|
|
||||||
name: "CreatedAt",
|
|
||||||
table: "Core_ApiKeys",
|
|
||||||
type: "timestamp with time zone",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "CreatedAt",
|
|
||||||
table: "Core_ApiKeys");
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<string>(
|
|
||||||
name: "Secret",
|
|
||||||
table: "Core_ApiKeys",
|
|
||||||
type: "text",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,393 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using Moonlight.ApiServer.Database;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.ApiServer.Database.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(CoreDataContext))]
|
|
||||||
[Migration("20250405172522_AddedHangfireTables")]
|
|
||||||
partial class AddedHangfireTables
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasAnnotation("ProductVersion", "8.0.11")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireCounter", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTime?>("ExpireAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<long>("Value")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ExpireAt");
|
|
||||||
|
|
||||||
b.HasIndex("Key", "Value");
|
|
||||||
|
|
||||||
b.ToTable("HangfireCounter");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireHash", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("Field")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("ExpireAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("Key", "Field");
|
|
||||||
|
|
||||||
b.HasIndex("ExpireAt");
|
|
||||||
|
|
||||||
b.ToTable("HangfireHash");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("ExpireAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("InvocationData")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<long?>("StateId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<string>("StateName")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ExpireAt");
|
|
||||||
|
|
||||||
b.HasIndex("StateId");
|
|
||||||
|
|
||||||
b.HasIndex("StateName");
|
|
||||||
|
|
||||||
b.ToTable("HangfireJob");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJobParameter", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("JobId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("JobId", "Name");
|
|
||||||
|
|
||||||
b.ToTable("HangfireJobParameter");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireList", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<int>("Position")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("ExpireAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("Key", "Position");
|
|
||||||
|
|
||||||
b.HasIndex("ExpireAt");
|
|
||||||
|
|
||||||
b.ToTable("HangfireList");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireLock", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("Id")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("AcquiredAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("HangfireLock");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireQueuedJob", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTime?>("FetchedAt")
|
|
||||||
.IsConcurrencyToken()
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<long>("JobId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<string>("Queue")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("JobId");
|
|
||||||
|
|
||||||
b.HasIndex("Queue", "FetchedAt");
|
|
||||||
|
|
||||||
b.ToTable("HangfireQueuedJob");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireServer", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("Id")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("Heartbeat")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Queues")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<DateTime>("StartedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int>("WorkerCount")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("Heartbeat");
|
|
||||||
|
|
||||||
b.ToTable("HangfireServer");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireSet", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("character varying(100)");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("ExpireAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<double>("Score")
|
|
||||||
.HasColumnType("double precision");
|
|
||||||
|
|
||||||
b.HasKey("Key", "Value");
|
|
||||||
|
|
||||||
b.HasIndex("ExpireAt");
|
|
||||||
|
|
||||||
b.HasIndex("Key", "Score");
|
|
||||||
|
|
||||||
b.ToTable("HangfireSet");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireState", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Data")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<long>("JobId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("Reason")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("JobId");
|
|
||||||
|
|
||||||
b.ToTable("HangfireState");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.ApiKey", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<DateTime>("ExpiresAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("PermissionsJson")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("jsonb");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Core_ApiKeys", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.User", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Password")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("PermissionsJson")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("jsonb");
|
|
||||||
|
|
||||||
b.Property<DateTime>("TokenValidTimestamp")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Username")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Core_Users", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Hangfire.EntityFrameworkCore.HangfireState", "State")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("StateId");
|
|
||||||
|
|
||||||
b.Navigation("State");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJobParameter", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Hangfire.EntityFrameworkCore.HangfireJob", "Job")
|
|
||||||
.WithMany("Parameters")
|
|
||||||
.HasForeignKey("JobId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Job");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireQueuedJob", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Hangfire.EntityFrameworkCore.HangfireJob", "Job")
|
|
||||||
.WithMany("QueuedJobs")
|
|
||||||
.HasForeignKey("JobId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Job");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireState", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Hangfire.EntityFrameworkCore.HangfireJob", "Job")
|
|
||||||
.WithMany("States")
|
|
||||||
.HasForeignKey("JobId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Job");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Parameters");
|
|
||||||
|
|
||||||
b.Navigation("QueuedJobs");
|
|
||||||
|
|
||||||
b.Navigation("States");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,393 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using Moonlight.ApiServer.Database;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.ApiServer.Database.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(CoreDataContext))]
|
|
||||||
[Migration("20250712202608_SwitchedToPgArraysForPermissions")]
|
|
||||||
partial class SwitchedToPgArraysForPermissions
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasAnnotation("ProductVersion", "9.0.7")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireCounter", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTime?>("ExpireAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<long>("Value")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ExpireAt");
|
|
||||||
|
|
||||||
b.HasIndex("Key", "Value");
|
|
||||||
|
|
||||||
b.ToTable("HangfireCounter");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireHash", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("Field")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("ExpireAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("Key", "Field");
|
|
||||||
|
|
||||||
b.HasIndex("ExpireAt");
|
|
||||||
|
|
||||||
b.ToTable("HangfireHash");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("ExpireAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("InvocationData")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<long?>("StateId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<string>("StateName")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ExpireAt");
|
|
||||||
|
|
||||||
b.HasIndex("StateId");
|
|
||||||
|
|
||||||
b.HasIndex("StateName");
|
|
||||||
|
|
||||||
b.ToTable("HangfireJob");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJobParameter", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("JobId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("JobId", "Name");
|
|
||||||
|
|
||||||
b.ToTable("HangfireJobParameter");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireList", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<int>("Position")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("ExpireAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("Key", "Position");
|
|
||||||
|
|
||||||
b.HasIndex("ExpireAt");
|
|
||||||
|
|
||||||
b.ToTable("HangfireList");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireLock", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("Id")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("AcquiredAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("HangfireLock");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireQueuedJob", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTime?>("FetchedAt")
|
|
||||||
.IsConcurrencyToken()
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<long>("JobId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<string>("Queue")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("JobId");
|
|
||||||
|
|
||||||
b.HasIndex("Queue", "FetchedAt");
|
|
||||||
|
|
||||||
b.ToTable("HangfireQueuedJob");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireServer", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("Id")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<DateTime>("Heartbeat")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Queues")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<DateTime>("StartedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int>("WorkerCount")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("Heartbeat");
|
|
||||||
|
|
||||||
b.ToTable("HangfireServer");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireSet", b =>
|
|
||||||
{
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("character varying(100)");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("ExpireAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<double>("Score")
|
|
||||||
.HasColumnType("double precision");
|
|
||||||
|
|
||||||
b.HasKey("Key", "Value");
|
|
||||||
|
|
||||||
b.HasIndex("ExpireAt");
|
|
||||||
|
|
||||||
b.HasIndex("Key", "Score");
|
|
||||||
|
|
||||||
b.ToTable("HangfireSet");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireState", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Data")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<long>("JobId")
|
|
||||||
.HasColumnType("bigint");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("Reason")
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("JobId");
|
|
||||||
|
|
||||||
b.ToTable("HangfireState");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.ApiKey", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("ExpiresAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.PrimitiveCollection<string[]>("Permissions")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Core_ApiKeys", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.User", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.Property<string>("Password")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.PrimitiveCollection<string[]>("Permissions")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("TokenValidTimestamp")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Username")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Core_Users", (string)null);
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Hangfire.EntityFrameworkCore.HangfireState", "State")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("StateId");
|
|
||||||
|
|
||||||
b.Navigation("State");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJobParameter", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Hangfire.EntityFrameworkCore.HangfireJob", "Job")
|
|
||||||
.WithMany("Parameters")
|
|
||||||
.HasForeignKey("JobId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Job");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireQueuedJob", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Hangfire.EntityFrameworkCore.HangfireJob", "Job")
|
|
||||||
.WithMany("QueuedJobs")
|
|
||||||
.HasForeignKey("JobId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Job");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireState", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Hangfire.EntityFrameworkCore.HangfireJob", "Job")
|
|
||||||
.WithMany("States")
|
|
||||||
.HasForeignKey("JobId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Job");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Parameters");
|
|
||||||
|
|
||||||
b.Navigation("QueuedJobs");
|
|
||||||
|
|
||||||
b.Navigation("States");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.ApiServer.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class SwitchedToPgArraysForPermissions : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "PermissionsJson",
|
|
||||||
table: "Core_Users");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "PermissionsJson",
|
|
||||||
table: "Core_ApiKeys");
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<string[]>(
|
|
||||||
name: "Permissions",
|
|
||||||
table: "Core_Users",
|
|
||||||
type: "text[]",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: new string[0]);
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<string[]>(
|
|
||||||
name: "Permissions",
|
|
||||||
table: "Core_ApiKeys",
|
|
||||||
type: "text[]",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: new string[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "Permissions",
|
|
||||||
table: "Core_Users");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "Permissions",
|
|
||||||
table: "Core_ApiKeys");
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<string>(
|
|
||||||
name: "PermissionsJson",
|
|
||||||
table: "Core_Users",
|
|
||||||
type: "jsonb",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: "");
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<string>(
|
|
||||||
name: "PermissionsJson",
|
|
||||||
table: "Core_ApiKeys",
|
|
||||||
type: "jsonb",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.ApiServer.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddedThemes : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Core_Themes",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
IsEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
|
||||||
Name = table.Column<string>(type: "text", nullable: false),
|
|
||||||
Author = table.Column<string>(type: "text", nullable: false),
|
|
||||||
Version = table.Column<string>(type: "text", nullable: false),
|
|
||||||
UpdateUrl = table.Column<string>(type: "text", nullable: true),
|
|
||||||
DonateUrl = table.Column<string>(type: "text", nullable: true),
|
|
||||||
Content = table.Column<string>(type: "jsonb", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Core_Themes", x => x.Id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Core_Themes");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,15 +12,16 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|||||||
namespace Moonlight.ApiServer.Database.Migrations
|
namespace Moonlight.ApiServer.Database.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(CoreDataContext))]
|
[DbContext(typeof(CoreDataContext))]
|
||||||
[Migration("20250720203346_AddedThemes")]
|
[Migration("20250919201409_RecreatedMigrationsForChangeOfSchema")]
|
||||||
partial class AddedThemes
|
partial class RecreatedMigrationsForChangeOfSchema
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.HasAnnotation("ProductVersion", "9.0.7")
|
.HasDefaultSchema("core")
|
||||||
|
.HasAnnotation("ProductVersion", "9.0.8")
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
@@ -50,7 +51,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("Key", "Value");
|
b.HasIndex("Key", "Value");
|
||||||
|
|
||||||
b.ToTable("HangfireCounter");
|
b.ToTable("HangfireCounter", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireHash", b =>
|
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireHash", b =>
|
||||||
@@ -73,7 +74,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("ExpireAt");
|
b.HasIndex("ExpireAt");
|
||||||
|
|
||||||
b.ToTable("HangfireHash");
|
b.ToTable("HangfireHash", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b =>
|
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b =>
|
||||||
@@ -109,7 +110,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("StateName");
|
b.HasIndex("StateName");
|
||||||
|
|
||||||
b.ToTable("HangfireJob");
|
b.ToTable("HangfireJob", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJobParameter", b =>
|
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJobParameter", b =>
|
||||||
@@ -126,7 +127,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasKey("JobId", "Name");
|
b.HasKey("JobId", "Name");
|
||||||
|
|
||||||
b.ToTable("HangfireJobParameter");
|
b.ToTable("HangfireJobParameter", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireList", b =>
|
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireList", b =>
|
||||||
@@ -148,7 +149,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("ExpireAt");
|
b.HasIndex("ExpireAt");
|
||||||
|
|
||||||
b.ToTable("HangfireList");
|
b.ToTable("HangfireList", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireLock", b =>
|
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireLock", b =>
|
||||||
@@ -162,7 +163,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.ToTable("HangfireLock");
|
b.ToTable("HangfireLock", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireQueuedJob", b =>
|
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireQueuedJob", b =>
|
||||||
@@ -191,7 +192,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("Queue", "FetchedAt");
|
b.HasIndex("Queue", "FetchedAt");
|
||||||
|
|
||||||
b.ToTable("HangfireQueuedJob");
|
b.ToTable("HangfireQueuedJob", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireServer", b =>
|
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireServer", b =>
|
||||||
@@ -217,7 +218,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("Heartbeat");
|
b.HasIndex("Heartbeat");
|
||||||
|
|
||||||
b.ToTable("HangfireServer");
|
b.ToTable("HangfireServer", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireSet", b =>
|
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireSet", b =>
|
||||||
@@ -242,7 +243,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("Key", "Score");
|
b.HasIndex("Key", "Score");
|
||||||
|
|
||||||
b.ToTable("HangfireSet");
|
b.ToTable("HangfireSet", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireState", b =>
|
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireState", b =>
|
||||||
@@ -275,7 +276,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("JobId");
|
b.HasIndex("JobId");
|
||||||
|
|
||||||
b.ToTable("HangfireState");
|
b.ToTable("HangfireState", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.ApiKey", b =>
|
modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.ApiKey", b =>
|
||||||
@@ -302,7 +303,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.ToTable("Core_ApiKeys", (string)null);
|
b.ToTable("ApiKeys", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.Theme", b =>
|
modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.Theme", b =>
|
||||||
@@ -336,7 +337,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.ToTable("Core_Themes", (string)null);
|
b.ToTable("Themes", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.User", b =>
|
modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.User", b =>
|
||||||
@@ -368,7 +369,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.ToTable("Core_Users", (string)null);
|
b.ToTable("Users", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b =>
|
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b =>
|
||||||
@@ -515,11 +516,11 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
b1.Property<float>("Depth")
|
b1.Property<int>("Depth")
|
||||||
.HasColumnType("real");
|
.HasColumnType("integer");
|
||||||
|
|
||||||
b1.Property<float>("Noise")
|
b1.Property<int>("Noise")
|
||||||
.HasColumnType("real");
|
.HasColumnType("integer");
|
||||||
|
|
||||||
b1.Property<float>("RadiusBox")
|
b1.Property<float>("RadiusBox")
|
||||||
.HasColumnType("real");
|
.HasColumnType("real");
|
||||||
@@ -538,7 +539,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b1.HasKey("ThemeId");
|
b1.HasKey("ThemeId");
|
||||||
|
|
||||||
b1.ToTable("Core_Themes");
|
b1.ToTable("Themes", "core");
|
||||||
|
|
||||||
b1.ToJson("Content");
|
b1.ToJson("Content");
|
||||||
|
|
||||||
@@ -7,13 +7,34 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|||||||
namespace Moonlight.ApiServer.Database.Migrations
|
namespace Moonlight.ApiServer.Database.Migrations
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class AddedHangfireTables : Migration
|
public partial class RecreatedMigrationsForChangeOfSchema : Migration
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
|
migrationBuilder.EnsureSchema(
|
||||||
|
name: "core");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ApiKeys",
|
||||||
|
schema: "core",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
Description = table.Column<string>(type: "text", nullable: false),
|
||||||
|
Permissions = table.Column<string[]>(type: "text[]", nullable: false),
|
||||||
|
ExpiresAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||||
|
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ApiKeys", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "HangfireCounter",
|
name: "HangfireCounter",
|
||||||
|
schema: "core",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
Id = table.Column<long>(type: "bigint", nullable: false)
|
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
@@ -29,6 +50,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "HangfireHash",
|
name: "HangfireHash",
|
||||||
|
schema: "core",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
Key = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
Key = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||||
@@ -43,6 +65,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "HangfireList",
|
name: "HangfireList",
|
||||||
|
schema: "core",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
Key = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
Key = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||||
@@ -57,6 +80,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "HangfireLock",
|
name: "HangfireLock",
|
||||||
|
schema: "core",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
Id = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
Id = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||||
@@ -69,6 +93,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "HangfireServer",
|
name: "HangfireServer",
|
||||||
|
schema: "core",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
Id = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
Id = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
||||||
@@ -84,6 +109,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "HangfireSet",
|
name: "HangfireSet",
|
||||||
|
schema: "core",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
Key = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
Key = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
||||||
@@ -96,8 +122,47 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
table.PrimaryKey("PK_HangfireSet", x => new { x.Key, x.Value });
|
table.PrimaryKey("PK_HangfireSet", x => new { x.Key, x.Value });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Themes",
|
||||||
|
schema: "core",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
IsEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
Name = table.Column<string>(type: "text", nullable: false),
|
||||||
|
Author = table.Column<string>(type: "text", nullable: false),
|
||||||
|
Version = table.Column<string>(type: "text", nullable: false),
|
||||||
|
UpdateUrl = table.Column<string>(type: "text", nullable: true),
|
||||||
|
DonateUrl = table.Column<string>(type: "text", nullable: true),
|
||||||
|
Content = table.Column<string>(type: "jsonb", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Themes", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Users",
|
||||||
|
schema: "core",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
Username = table.Column<string>(type: "text", nullable: false),
|
||||||
|
Email = table.Column<string>(type: "text", nullable: false),
|
||||||
|
Password = table.Column<string>(type: "text", nullable: false),
|
||||||
|
TokenValidTimestamp = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
||||||
|
Permissions = table.Column<string[]>(type: "text[]", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Users", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "HangfireJob",
|
name: "HangfireJob",
|
||||||
|
schema: "core",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
Id = table.Column<long>(type: "bigint", nullable: false)
|
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
@@ -115,6 +180,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "HangfireJobParameter",
|
name: "HangfireJobParameter",
|
||||||
|
schema: "core",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
JobId = table.Column<long>(type: "bigint", nullable: false),
|
JobId = table.Column<long>(type: "bigint", nullable: false),
|
||||||
@@ -127,6 +193,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
table.ForeignKey(
|
table.ForeignKey(
|
||||||
name: "FK_HangfireJobParameter_HangfireJob_JobId",
|
name: "FK_HangfireJobParameter_HangfireJob_JobId",
|
||||||
column: x => x.JobId,
|
column: x => x.JobId,
|
||||||
|
principalSchema: "core",
|
||||||
principalTable: "HangfireJob",
|
principalTable: "HangfireJob",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade);
|
||||||
@@ -134,6 +201,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "HangfireQueuedJob",
|
name: "HangfireQueuedJob",
|
||||||
|
schema: "core",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
Id = table.Column<long>(type: "bigint", nullable: false)
|
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
@@ -148,6 +216,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
table.ForeignKey(
|
table.ForeignKey(
|
||||||
name: "FK_HangfireQueuedJob_HangfireJob_JobId",
|
name: "FK_HangfireQueuedJob_HangfireJob_JobId",
|
||||||
column: x => x.JobId,
|
column: x => x.JobId,
|
||||||
|
principalSchema: "core",
|
||||||
principalTable: "HangfireJob",
|
principalTable: "HangfireJob",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade);
|
||||||
@@ -155,6 +224,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "HangfireState",
|
name: "HangfireState",
|
||||||
|
schema: "core",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
Id = table.Column<long>(type: "bigint", nullable: false)
|
Id = table.Column<long>(type: "bigint", nullable: false)
|
||||||
@@ -171,6 +241,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
table.ForeignKey(
|
table.ForeignKey(
|
||||||
name: "FK_HangfireState_HangfireJob_JobId",
|
name: "FK_HangfireState_HangfireJob_JobId",
|
||||||
column: x => x.JobId,
|
column: x => x.JobId,
|
||||||
|
principalSchema: "core",
|
||||||
principalTable: "HangfireJob",
|
principalTable: "HangfireJob",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Cascade);
|
onDelete: ReferentialAction.Cascade);
|
||||||
@@ -178,73 +249,88 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_HangfireCounter_ExpireAt",
|
name: "IX_HangfireCounter_ExpireAt",
|
||||||
|
schema: "core",
|
||||||
table: "HangfireCounter",
|
table: "HangfireCounter",
|
||||||
column: "ExpireAt");
|
column: "ExpireAt");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_HangfireCounter_Key_Value",
|
name: "IX_HangfireCounter_Key_Value",
|
||||||
|
schema: "core",
|
||||||
table: "HangfireCounter",
|
table: "HangfireCounter",
|
||||||
columns: new[] { "Key", "Value" });
|
columns: new[] { "Key", "Value" });
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_HangfireHash_ExpireAt",
|
name: "IX_HangfireHash_ExpireAt",
|
||||||
|
schema: "core",
|
||||||
table: "HangfireHash",
|
table: "HangfireHash",
|
||||||
column: "ExpireAt");
|
column: "ExpireAt");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_HangfireJob_ExpireAt",
|
name: "IX_HangfireJob_ExpireAt",
|
||||||
|
schema: "core",
|
||||||
table: "HangfireJob",
|
table: "HangfireJob",
|
||||||
column: "ExpireAt");
|
column: "ExpireAt");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_HangfireJob_StateId",
|
name: "IX_HangfireJob_StateId",
|
||||||
|
schema: "core",
|
||||||
table: "HangfireJob",
|
table: "HangfireJob",
|
||||||
column: "StateId");
|
column: "StateId");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_HangfireJob_StateName",
|
name: "IX_HangfireJob_StateName",
|
||||||
|
schema: "core",
|
||||||
table: "HangfireJob",
|
table: "HangfireJob",
|
||||||
column: "StateName");
|
column: "StateName");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_HangfireList_ExpireAt",
|
name: "IX_HangfireList_ExpireAt",
|
||||||
|
schema: "core",
|
||||||
table: "HangfireList",
|
table: "HangfireList",
|
||||||
column: "ExpireAt");
|
column: "ExpireAt");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_HangfireQueuedJob_JobId",
|
name: "IX_HangfireQueuedJob_JobId",
|
||||||
|
schema: "core",
|
||||||
table: "HangfireQueuedJob",
|
table: "HangfireQueuedJob",
|
||||||
column: "JobId");
|
column: "JobId");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_HangfireQueuedJob_Queue_FetchedAt",
|
name: "IX_HangfireQueuedJob_Queue_FetchedAt",
|
||||||
|
schema: "core",
|
||||||
table: "HangfireQueuedJob",
|
table: "HangfireQueuedJob",
|
||||||
columns: new[] { "Queue", "FetchedAt" });
|
columns: new[] { "Queue", "FetchedAt" });
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_HangfireServer_Heartbeat",
|
name: "IX_HangfireServer_Heartbeat",
|
||||||
|
schema: "core",
|
||||||
table: "HangfireServer",
|
table: "HangfireServer",
|
||||||
column: "Heartbeat");
|
column: "Heartbeat");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_HangfireSet_ExpireAt",
|
name: "IX_HangfireSet_ExpireAt",
|
||||||
|
schema: "core",
|
||||||
table: "HangfireSet",
|
table: "HangfireSet",
|
||||||
column: "ExpireAt");
|
column: "ExpireAt");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_HangfireSet_Key_Score",
|
name: "IX_HangfireSet_Key_Score",
|
||||||
|
schema: "core",
|
||||||
table: "HangfireSet",
|
table: "HangfireSet",
|
||||||
columns: new[] { "Key", "Score" });
|
columns: new[] { "Key", "Score" });
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_HangfireState_JobId",
|
name: "IX_HangfireState_JobId",
|
||||||
|
schema: "core",
|
||||||
table: "HangfireState",
|
table: "HangfireState",
|
||||||
column: "JobId");
|
column: "JobId");
|
||||||
|
|
||||||
migrationBuilder.AddForeignKey(
|
migrationBuilder.AddForeignKey(
|
||||||
name: "FK_HangfireJob_HangfireState_StateId",
|
name: "FK_HangfireJob_HangfireState_StateId",
|
||||||
|
schema: "core",
|
||||||
table: "HangfireJob",
|
table: "HangfireJob",
|
||||||
column: "StateId",
|
column: "StateId",
|
||||||
|
principalSchema: "core",
|
||||||
principalTable: "HangfireState",
|
principalTable: "HangfireState",
|
||||||
principalColumn: "Id");
|
principalColumn: "Id");
|
||||||
}
|
}
|
||||||
@@ -254,37 +340,60 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
{
|
{
|
||||||
migrationBuilder.DropForeignKey(
|
migrationBuilder.DropForeignKey(
|
||||||
name: "FK_HangfireJob_HangfireState_StateId",
|
name: "FK_HangfireJob_HangfireState_StateId",
|
||||||
|
schema: "core",
|
||||||
table: "HangfireJob");
|
table: "HangfireJob");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "HangfireCounter");
|
name: "ApiKeys",
|
||||||
|
schema: "core");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "HangfireHash");
|
name: "HangfireCounter",
|
||||||
|
schema: "core");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "HangfireJobParameter");
|
name: "HangfireHash",
|
||||||
|
schema: "core");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "HangfireList");
|
name: "HangfireJobParameter",
|
||||||
|
schema: "core");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "HangfireLock");
|
name: "HangfireList",
|
||||||
|
schema: "core");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "HangfireQueuedJob");
|
name: "HangfireLock",
|
||||||
|
schema: "core");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "HangfireServer");
|
name: "HangfireQueuedJob",
|
||||||
|
schema: "core");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "HangfireSet");
|
name: "HangfireServer",
|
||||||
|
schema: "core");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "HangfireState");
|
name: "HangfireSet",
|
||||||
|
schema: "core");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "HangfireJob");
|
name: "Themes",
|
||||||
|
schema: "core");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Users",
|
||||||
|
schema: "core");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "HangfireState",
|
||||||
|
schema: "core");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "HangfireJob",
|
||||||
|
schema: "core");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,8 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.HasAnnotation("ProductVersion", "9.0.7")
|
.HasDefaultSchema("core")
|
||||||
|
.HasAnnotation("ProductVersion", "9.0.8")
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
@@ -47,7 +48,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("Key", "Value");
|
b.HasIndex("Key", "Value");
|
||||||
|
|
||||||
b.ToTable("HangfireCounter");
|
b.ToTable("HangfireCounter", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireHash", b =>
|
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireHash", b =>
|
||||||
@@ -70,7 +71,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("ExpireAt");
|
b.HasIndex("ExpireAt");
|
||||||
|
|
||||||
b.ToTable("HangfireHash");
|
b.ToTable("HangfireHash", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b =>
|
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b =>
|
||||||
@@ -106,7 +107,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("StateName");
|
b.HasIndex("StateName");
|
||||||
|
|
||||||
b.ToTable("HangfireJob");
|
b.ToTable("HangfireJob", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJobParameter", b =>
|
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJobParameter", b =>
|
||||||
@@ -123,7 +124,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasKey("JobId", "Name");
|
b.HasKey("JobId", "Name");
|
||||||
|
|
||||||
b.ToTable("HangfireJobParameter");
|
b.ToTable("HangfireJobParameter", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireList", b =>
|
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireList", b =>
|
||||||
@@ -145,7 +146,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("ExpireAt");
|
b.HasIndex("ExpireAt");
|
||||||
|
|
||||||
b.ToTable("HangfireList");
|
b.ToTable("HangfireList", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireLock", b =>
|
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireLock", b =>
|
||||||
@@ -159,7 +160,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.ToTable("HangfireLock");
|
b.ToTable("HangfireLock", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireQueuedJob", b =>
|
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireQueuedJob", b =>
|
||||||
@@ -188,7 +189,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("Queue", "FetchedAt");
|
b.HasIndex("Queue", "FetchedAt");
|
||||||
|
|
||||||
b.ToTable("HangfireQueuedJob");
|
b.ToTable("HangfireQueuedJob", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireServer", b =>
|
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireServer", b =>
|
||||||
@@ -214,7 +215,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("Heartbeat");
|
b.HasIndex("Heartbeat");
|
||||||
|
|
||||||
b.ToTable("HangfireServer");
|
b.ToTable("HangfireServer", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireSet", b =>
|
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireSet", b =>
|
||||||
@@ -239,7 +240,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("Key", "Score");
|
b.HasIndex("Key", "Score");
|
||||||
|
|
||||||
b.ToTable("HangfireSet");
|
b.ToTable("HangfireSet", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireState", b =>
|
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireState", b =>
|
||||||
@@ -272,7 +273,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("JobId");
|
b.HasIndex("JobId");
|
||||||
|
|
||||||
b.ToTable("HangfireState");
|
b.ToTable("HangfireState", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.ApiKey", b =>
|
modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.ApiKey", b =>
|
||||||
@@ -299,7 +300,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.ToTable("Core_ApiKeys", (string)null);
|
b.ToTable("ApiKeys", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.Theme", b =>
|
modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.Theme", b =>
|
||||||
@@ -333,7 +334,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.ToTable("Core_Themes", (string)null);
|
b.ToTable("Themes", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.User", b =>
|
modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.User", b =>
|
||||||
@@ -365,7 +366,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.ToTable("Core_Users", (string)null);
|
b.ToTable("Users", "core");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b =>
|
modelBuilder.Entity("Hangfire.EntityFrameworkCore.HangfireJob", b =>
|
||||||
@@ -512,11 +513,11 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
b1.Property<float>("Depth")
|
b1.Property<int>("Depth")
|
||||||
.HasColumnType("real");
|
.HasColumnType("integer");
|
||||||
|
|
||||||
b1.Property<float>("Noise")
|
b1.Property<int>("Noise")
|
||||||
.HasColumnType("real");
|
.HasColumnType("integer");
|
||||||
|
|
||||||
b1.Property<float>("RadiusBox")
|
b1.Property<float>("RadiusBox")
|
||||||
.HasColumnType("real");
|
.HasColumnType("real");
|
||||||
@@ -535,7 +536,7 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
b1.HasKey("ThemeId");
|
b1.HasKey("ThemeId");
|
||||||
|
|
||||||
b1.ToTable("Core_Themes");
|
b1.ToTable("Themes", "core");
|
||||||
|
|
||||||
b1.ToJson("Content");
|
b1.ToJson("Content");
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using MoonCore.Exceptions;
|
|
||||||
using MoonCore.Extended.Abstractions;
|
using MoonCore.Extended.Abstractions;
|
||||||
using MoonCore.Extended.Models;
|
|
||||||
using MoonCore.Models;
|
using MoonCore.Models;
|
||||||
using Moonlight.ApiServer.Database.Entities;
|
using Moonlight.ApiServer.Database.Entities;
|
||||||
|
using Moonlight.ApiServer.Mappers;
|
||||||
using Moonlight.ApiServer.Services;
|
using Moonlight.ApiServer.Services;
|
||||||
using Moonlight.Shared.Http.Requests.Admin.ApiKeys;
|
using Moonlight.Shared.Http.Requests.Admin.ApiKeys;
|
||||||
using Moonlight.Shared.Http.Responses.Admin.ApiKeys;
|
using Moonlight.Shared.Http.Responses.Admin.ApiKeys;
|
||||||
@@ -28,69 +26,82 @@ public class ApiKeysController : Controller
|
|||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Authorize(Policy = "permissions:admin.apikeys.get")]
|
[Authorize(Policy = "permissions:admin.apikeys.get")]
|
||||||
public async Task<IPagedData<ApiKeyResponse>> Get([FromQuery] PagedOptions options)
|
public async Task<ActionResult<ICountedData<ApiKeyResponse>>> GetAsync(
|
||||||
|
[FromQuery] int startIndex,
|
||||||
|
[FromQuery] int count,
|
||||||
|
[FromQuery] string? orderBy,
|
||||||
|
[FromQuery] string? filter,
|
||||||
|
[FromQuery] string orderByDir = "asc"
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var count = await ApiKeyRepository.Get().CountAsync();
|
if (count > 100)
|
||||||
|
return Problem("You cannot fetch more items than 100 at a time", statusCode: 400);
|
||||||
|
|
||||||
|
IQueryable<ApiKey> query = ApiKeyRepository.Get();
|
||||||
|
|
||||||
var apiKeys = await ApiKeyRepository
|
query = orderBy switch
|
||||||
.Get()
|
{
|
||||||
.OrderBy(x => x.Id)
|
nameof(ApiKey.Id) => orderByDir == "desc"
|
||||||
.Skip(options.Page * options.PageSize)
|
? query.OrderByDescending(x => x.Id)
|
||||||
.Take(options.PageSize)
|
: query.OrderBy(x => x.Id),
|
||||||
|
|
||||||
|
nameof(ApiKey.ExpiresAt) => orderByDir == "desc"
|
||||||
|
? query.OrderByDescending(x => x.ExpiresAt)
|
||||||
|
: query.OrderBy(x => x.ExpiresAt),
|
||||||
|
|
||||||
|
nameof(ApiKey.CreatedAt) => orderByDir == "desc"
|
||||||
|
? query.OrderByDescending(x => x.CreatedAt)
|
||||||
|
: query.OrderBy(x => x.CreatedAt),
|
||||||
|
|
||||||
|
_ => query.OrderBy(x => x.Id)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(filter))
|
||||||
|
{
|
||||||
|
query = query.Where(x =>
|
||||||
|
EF.Functions.ILike(x.Description, $"%{filter}%")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalCount = await query.CountAsync();
|
||||||
|
|
||||||
|
var items = await query
|
||||||
|
.Skip(startIndex)
|
||||||
|
.Take(count)
|
||||||
|
.AsNoTracking()
|
||||||
|
.ProjectToResponse()
|
||||||
.ToArrayAsync();
|
.ToArrayAsync();
|
||||||
|
|
||||||
var mappedApiKey = apiKeys
|
return new CountedData<ApiKeyResponse>()
|
||||||
.Select(x => new ApiKeyResponse()
|
|
||||||
{
|
|
||||||
Id = x.Id,
|
|
||||||
Permissions = x.Permissions,
|
|
||||||
Description = x.Description,
|
|
||||||
ExpiresAt = x.ExpiresAt
|
|
||||||
})
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
return new PagedData<ApiKeyResponse>()
|
|
||||||
{
|
{
|
||||||
CurrentPage = options.Page,
|
Items = items,
|
||||||
Items = mappedApiKey,
|
TotalCount = totalCount
|
||||||
PageSize = options.PageSize,
|
|
||||||
TotalItems = count,
|
|
||||||
TotalPages = count == 0 ? 0 : (count - 1) / options.PageSize
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id:int}")]
|
||||||
[Authorize(Policy = "permissions:admin.apikeys.get")]
|
[Authorize(Policy = "permissions:admin.apikeys.get")]
|
||||||
public async Task<ApiKeyResponse> GetSingle(int id)
|
public async Task<ActionResult<ApiKeyResponse>> GetSingleAsync(int id)
|
||||||
{
|
{
|
||||||
var apiKey = await ApiKeyRepository
|
var apiKey = await ApiKeyRepository
|
||||||
.Get()
|
.Get()
|
||||||
|
.AsNoTracking()
|
||||||
|
.ProjectToResponse()
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
.FirstOrDefaultAsync(x => x.Id == id);
|
||||||
|
|
||||||
if (apiKey == null)
|
if (apiKey == null)
|
||||||
throw new HttpApiException("No api key with that id found", 404);
|
return Problem("No api key with that id found", statusCode: 404);
|
||||||
|
|
||||||
return new ApiKeyResponse()
|
return apiKey;
|
||||||
{
|
|
||||||
Id = apiKey.Id,
|
|
||||||
Permissions = apiKey.Permissions,
|
|
||||||
Description = apiKey.Description,
|
|
||||||
ExpiresAt = apiKey.ExpiresAt
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Authorize(Policy = "permissions:admin.apikeys.create")]
|
[Authorize(Policy = "permissions:admin.apikeys.create")]
|
||||||
public async Task<CreateApiKeyResponse> Create([FromBody] CreateApiKeyRequest request)
|
public async Task<CreateApiKeyResponse> CreateAsync([FromBody] CreateApiKeyRequest request)
|
||||||
{
|
{
|
||||||
var apiKey = new ApiKey()
|
var apiKey = ApiKeyMapper.ToApiKey(request);
|
||||||
{
|
|
||||||
Description = request.Description,
|
var finalApiKey = await ApiKeyRepository.AddAsync(apiKey);
|
||||||
Permissions = request.Permissions,
|
|
||||||
ExpiresAt = request.ExpiresAt
|
|
||||||
};
|
|
||||||
|
|
||||||
var finalApiKey = await ApiKeyRepository.Add(apiKey);
|
|
||||||
|
|
||||||
var response = new CreateApiKeyResponse
|
var response = new CreateApiKeyResponse
|
||||||
{
|
{
|
||||||
@@ -104,41 +115,36 @@ public class ApiKeysController : Controller
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPatch("{id}")]
|
[HttpPatch("{id:int}")]
|
||||||
[Authorize(Policy = "permissions:admin.apikeys.update")]
|
[Authorize(Policy = "permissions:admin.apikeys.update")]
|
||||||
public async Task<ApiKeyResponse> Update([FromRoute] int id, [FromBody] UpdateApiKeyRequest request)
|
public async Task<ActionResult<ApiKeyResponse>> UpdateAsync([FromRoute] int id, [FromBody] UpdateApiKeyRequest request)
|
||||||
{
|
{
|
||||||
var apiKey = await ApiKeyRepository
|
var apiKey = await ApiKeyRepository
|
||||||
.Get()
|
.Get()
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
.FirstOrDefaultAsync(x => x.Id == id);
|
||||||
|
|
||||||
if (apiKey == null)
|
if (apiKey == null)
|
||||||
throw new HttpApiException("No api key with that id found", 404);
|
return Problem("No api key with that id found", statusCode: 404);
|
||||||
|
|
||||||
apiKey.Description = request.Description;
|
ApiKeyMapper.Merge(apiKey, request);
|
||||||
|
|
||||||
await ApiKeyRepository.Update(apiKey);
|
await ApiKeyRepository.UpdateAsync(apiKey);
|
||||||
|
|
||||||
return new ApiKeyResponse()
|
return ApiKeyMapper.ToResponse(apiKey);
|
||||||
{
|
|
||||||
Id = apiKey.Id,
|
|
||||||
Description = apiKey.Description,
|
|
||||||
Permissions = apiKey.Permissions,
|
|
||||||
ExpiresAt = apiKey.ExpiresAt
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id:int}")]
|
||||||
[Authorize(Policy = "permissions:admin.apikeys.delete")]
|
[Authorize(Policy = "permissions:admin.apikeys.delete")]
|
||||||
public async Task Delete([FromRoute] int id)
|
public async Task<ActionResult> DeleteAsync([FromRoute] int id)
|
||||||
{
|
{
|
||||||
var apiKey = await ApiKeyRepository
|
var apiKey = await ApiKeyRepository
|
||||||
.Get()
|
.Get()
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
.FirstOrDefaultAsync(x => x.Id == id);
|
||||||
|
|
||||||
if (apiKey == null)
|
if (apiKey == null)
|
||||||
throw new HttpApiException("No api key with that id found", 404);
|
return Problem("No api key with that id found", statusCode: 404);
|
||||||
|
|
||||||
await ApiKeyRepository.Remove(apiKey);
|
await ApiKeyRepository.RemoveAsync(apiKey);
|
||||||
|
return NoContent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19,9 +19,9 @@ public class AdvancedController : Controller
|
|||||||
|
|
||||||
[HttpGet("frontend")]
|
[HttpGet("frontend")]
|
||||||
[Authorize(Policy = "permissions:admin.system.advanced.frontend")]
|
[Authorize(Policy = "permissions:admin.system.advanced.frontend")]
|
||||||
public async Task Frontend()
|
public async Task FrontendAsync()
|
||||||
{
|
{
|
||||||
var stream = await FrontendService.GenerateZip();
|
var stream = await FrontendService.GenerateZipAsync();
|
||||||
await Results.File(stream, fileDownloadName: "frontend.zip").ExecuteAsync(HttpContext);
|
await Results.File(stream, fileDownloadName: "frontend.zip").ExecuteAsync(HttpContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,65 +26,96 @@ public class ThemesController : Controller
|
|||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Authorize(Policy = "permissions:admin.system.customisation.themes.read")]
|
[Authorize(Policy = "permissions:admin.system.customisation.themes.read")]
|
||||||
public async Task<PagedData<ThemeResponse>> Get([FromQuery] PagedOptions options)
|
public async Task<ActionResult<ICountedData<ThemeResponse>>> GetAsync(
|
||||||
|
[FromQuery] int startIndex,
|
||||||
|
[FromQuery] int count,
|
||||||
|
[FromQuery] string? orderBy,
|
||||||
|
[FromQuery] string? filter,
|
||||||
|
[FromQuery] string orderByDir = "asc"
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var count = await ThemeRepository.Get().CountAsync();
|
if (count > 100)
|
||||||
|
return Problem("You cannot fetch more items than 100 at a time", statusCode: 400);
|
||||||
|
|
||||||
var items = await ThemeRepository
|
IQueryable<Theme> query = ThemeRepository.Get();
|
||||||
.Get()
|
|
||||||
.Skip(options.Page * options.PageSize)
|
query = orderBy switch
|
||||||
.Take(options.PageSize)
|
|
||||||
.ToArrayAsync();
|
|
||||||
|
|
||||||
var mappedItems = items
|
|
||||||
.Select(ThemeMapper.ToResponse)
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
return new PagedData<ThemeResponse>()
|
|
||||||
{
|
{
|
||||||
CurrentPage = options.Page,
|
nameof(Theme.Id) => orderByDir == "desc"
|
||||||
Items = mappedItems,
|
? query.OrderByDescending(x => x.Id)
|
||||||
PageSize = options.PageSize,
|
: query.OrderBy(x => x.Id),
|
||||||
TotalItems = count,
|
|
||||||
TotalPages = count == 0 ? 0 : (count - 1) / options.PageSize
|
nameof(Theme.Name) => orderByDir == "desc"
|
||||||
|
? query.OrderByDescending(x => x.Name)
|
||||||
|
: query.OrderBy(x => x.Name),
|
||||||
|
|
||||||
|
nameof(Theme.Version) => orderByDir == "desc"
|
||||||
|
? query.OrderByDescending(x => x.Version)
|
||||||
|
: query.OrderBy(x => x.Version),
|
||||||
|
|
||||||
|
_ => query.OrderBy(x => x.Id)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(filter))
|
||||||
|
{
|
||||||
|
query = query.Where(x =>
|
||||||
|
EF.Functions.ILike(x.Name, $"%{filter}%")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalCount = await query.CountAsync();
|
||||||
|
|
||||||
|
var items = await query
|
||||||
|
.Skip(startIndex)
|
||||||
|
.Take(count)
|
||||||
|
.AsNoTracking()
|
||||||
|
.ProjectToResponse()
|
||||||
|
.ToArrayAsync();
|
||||||
|
|
||||||
|
return new CountedData<ThemeResponse>()
|
||||||
|
{
|
||||||
|
Items = items,
|
||||||
|
TotalCount = totalCount
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{id:int}")]
|
[HttpGet("{id:int}")]
|
||||||
[Authorize(Policy = "permissions:admin.system.customisation.themes.read")]
|
[Authorize(Policy = "permissions:admin.system.customisation.themes.read")]
|
||||||
public async Task<ThemeResponse> GetSingle([FromRoute] int id)
|
public async Task<ActionResult<ThemeResponse>> GetSingleAsync([FromRoute] int id)
|
||||||
{
|
{
|
||||||
var theme = await ThemeRepository
|
var theme = await ThemeRepository
|
||||||
.Get()
|
.Get()
|
||||||
|
.AsNoTracking()
|
||||||
|
.ProjectToResponse()
|
||||||
.FirstOrDefaultAsync(t => t.Id == id);
|
.FirstOrDefaultAsync(t => t.Id == id);
|
||||||
|
|
||||||
if (theme == null)
|
if (theme == null)
|
||||||
throw new HttpApiException("Theme with this id not found", 404);
|
return Problem("Theme with this id not found", statusCode: 404);
|
||||||
|
|
||||||
return ThemeMapper.ToResponse(theme);
|
return theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Authorize(Policy = "permissions:admin.system.customisation.themes.write")]
|
[Authorize(Policy = "permissions:admin.system.customisation.themes.write")]
|
||||||
public async Task<ThemeResponse> Create([FromBody] CreateThemeRequest request)
|
public async Task<ActionResult<ThemeResponse>> CreateAsync([FromBody] CreateThemeRequest request)
|
||||||
{
|
{
|
||||||
var theme = ThemeMapper.ToTheme(request);
|
var theme = ThemeMapper.ToTheme(request);
|
||||||
|
|
||||||
var finalTheme = await ThemeRepository.Add(theme);
|
var finalTheme = await ThemeRepository.AddAsync(theme);
|
||||||
|
|
||||||
return ThemeMapper.ToResponse(finalTheme);
|
return ThemeMapper.ToResponse(finalTheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPatch("{id:int}")]
|
[HttpPatch("{id:int}")]
|
||||||
[Authorize(Policy = "permissions:admin.system.customisation.themes.write")]
|
[Authorize(Policy = "permissions:admin.system.customisation.themes.write")]
|
||||||
public async Task<ThemeResponse> Update([FromRoute] int id, [FromBody] UpdateThemeRequest request)
|
public async Task<ActionResult<ThemeResponse>> UpdateAsync([FromRoute] int id, [FromBody] UpdateThemeRequest request)
|
||||||
{
|
{
|
||||||
var theme = await ThemeRepository
|
var theme = await ThemeRepository
|
||||||
.Get()
|
.Get()
|
||||||
.FirstOrDefaultAsync(t => t.Id == id);
|
.FirstOrDefaultAsync(t => t.Id == id);
|
||||||
|
|
||||||
if (theme == null)
|
if (theme == null)
|
||||||
throw new HttpApiException("Theme with this id not found", 404);
|
return Problem("Theme with this id not found", statusCode: 404);
|
||||||
|
|
||||||
// Disable all other enabled themes if we are enabling the current theme.
|
// Disable all other enabled themes if we are enabling the current theme.
|
||||||
// This ensures only one theme is enabled at the time
|
// This ensures only one theme is enabled at the time
|
||||||
@@ -98,29 +129,28 @@ public class ThemesController : Controller
|
|||||||
foreach (var otherTheme in otherThemes)
|
foreach (var otherTheme in otherThemes)
|
||||||
otherTheme.IsEnabled = false;
|
otherTheme.IsEnabled = false;
|
||||||
|
|
||||||
await ThemeRepository.RunTransaction(set =>
|
await ThemeRepository.RunTransactionAsync(set => { set.UpdateRange(otherThemes); });
|
||||||
{
|
|
||||||
set.UpdateRange(otherThemes);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ThemeMapper.Merge(theme, request);
|
ThemeMapper.Merge(theme, request);
|
||||||
await ThemeRepository.Update(theme);
|
|
||||||
|
|
||||||
|
await ThemeRepository.UpdateAsync(theme);
|
||||||
|
|
||||||
return ThemeMapper.ToResponse(theme);
|
return ThemeMapper.ToResponse(theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("{id:int}")]
|
[HttpDelete("{id:int}")]
|
||||||
[Authorize(Policy = "permissions:admin.system.customisation.themes.write")]
|
[Authorize(Policy = "permissions:admin.system.customisation.themes.write")]
|
||||||
public async Task Delete([FromRoute] int id)
|
public async Task<ActionResult> DeleteAsync([FromRoute] int id)
|
||||||
{
|
{
|
||||||
var theme = await ThemeRepository
|
var theme = await ThemeRepository
|
||||||
.Get()
|
.Get()
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
.FirstOrDefaultAsync(x => x.Id == id);
|
||||||
|
|
||||||
if (theme == null)
|
if (theme == null)
|
||||||
throw new HttpApiException("Theme with this id not found", 404);
|
return Problem("Theme with this id not found", statusCode: 404);
|
||||||
|
|
||||||
await ThemeRepository.Remove(theme);
|
await ThemeRepository.RemoveAsync(theme);
|
||||||
|
return NoContent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,7 +21,7 @@ public class DiagnoseController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<ActionResult> Diagnose([FromBody] GenerateDiagnoseRequest request)
|
public async Task<ActionResult> DiagnoseAsync([FromBody] GenerateDiagnoseRequest request)
|
||||||
{
|
{
|
||||||
var stream = await DiagnoseService.GenerateDiagnoseAsync(request.Providers);
|
var stream = await DiagnoseService.GenerateDiagnoseAsync(request.Providers);
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ public class DiagnoseController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("providers")]
|
[HttpGet("providers")]
|
||||||
public async Task<ActionResult<DiagnoseProvideResponse[]>> GetProviders()
|
public async Task<ActionResult<DiagnoseProvideResponse[]>> GetProvidersAsync()
|
||||||
{
|
{
|
||||||
return await DiagnoseService.GetProvidersAsync();
|
return await DiagnoseService.GetProvidersAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public class CombineController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("combine")]
|
[HttpPost("combine")]
|
||||||
public async Task<IResult> Combine([FromBody] CombineRequest request)
|
public async Task<IResult> CombineAsync([FromBody] CombineRequest request)
|
||||||
{
|
{
|
||||||
// Validate file lenght
|
// Validate file lenght
|
||||||
if (request.Files.Length < 2)
|
if (request.Files.Length < 2)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public class CompressController : Controller
|
|||||||
private const string BaseDirectory = "storage";
|
private const string BaseDirectory = "storage";
|
||||||
|
|
||||||
[HttpPost("compress")]
|
[HttpPost("compress")]
|
||||||
public async Task<IResult> Compress([FromBody] CompressRequest request)
|
public async Task<IResult> CompressAsync([FromBody] CompressRequest request)
|
||||||
{
|
{
|
||||||
// Validate item length
|
// Validate item length
|
||||||
if (request.Items.Length == 0)
|
if (request.Items.Length == 0)
|
||||||
@@ -48,11 +48,11 @@ public class CompressController : Controller
|
|||||||
switch (request.Format)
|
switch (request.Format)
|
||||||
{
|
{
|
||||||
case "tar.gz":
|
case "tar.gz":
|
||||||
await CompressTarGz(destinationPath, itemsPaths, rootPath);
|
await CompressTarGzAsync(destinationPath, itemsPaths, rootPath);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "zip":
|
case "zip":
|
||||||
await CompressZip(destinationPath, itemsPaths, rootPath);
|
await CompressZipAsync(destinationPath, itemsPaths, rootPath);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -66,14 +66,14 @@ public class CompressController : Controller
|
|||||||
|
|
||||||
#region Tar Gz
|
#region Tar Gz
|
||||||
|
|
||||||
private async Task CompressTarGz(string destination, IEnumerable<string> items, string root)
|
private async Task CompressTarGzAsync(string destination, IEnumerable<string> items, string root)
|
||||||
{
|
{
|
||||||
await using var outStream = System.IO.File.Create(destination);
|
await using var outStream = System.IO.File.Create(destination);
|
||||||
await using var gzoStream = new GZipOutputStream(outStream);
|
await using var gzoStream = new GZipOutputStream(outStream);
|
||||||
await using var tarStream = new TarOutputStream(gzoStream, Encoding.UTF8);
|
await using var tarStream = new TarOutputStream(gzoStream, Encoding.UTF8);
|
||||||
|
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
await CompressItemToTarGz(tarStream, item, root);
|
await CompressItemToTarGzAsync(tarStream, item, root);
|
||||||
|
|
||||||
await tarStream.FlushAsync();
|
await tarStream.FlushAsync();
|
||||||
await gzoStream.FlushAsync();
|
await gzoStream.FlushAsync();
|
||||||
@@ -84,7 +84,7 @@ public class CompressController : Controller
|
|||||||
outStream.Close();
|
outStream.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CompressItemToTarGz(TarOutputStream tarOutputStream, string item, string root)
|
private async Task CompressItemToTarGzAsync(TarOutputStream tarOutputStream, string item, string root)
|
||||||
{
|
{
|
||||||
if (System.IO.File.Exists(item))
|
if (System.IO.File.Exists(item))
|
||||||
{
|
{
|
||||||
@@ -117,7 +117,7 @@ public class CompressController : Controller
|
|||||||
if (Directory.Exists(item))
|
if (Directory.Exists(item))
|
||||||
{
|
{
|
||||||
foreach (var fsEntry in Directory.EnumerateFileSystemEntries(item))
|
foreach (var fsEntry in Directory.EnumerateFileSystemEntries(item))
|
||||||
await CompressItemToTarGz(tarOutputStream, fsEntry, root);
|
await CompressItemToTarGzAsync(tarOutputStream, fsEntry, root);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,13 +125,13 @@ public class CompressController : Controller
|
|||||||
|
|
||||||
#region ZIP
|
#region ZIP
|
||||||
|
|
||||||
private async Task CompressZip(string destination, IEnumerable<string> items, string root)
|
private async Task CompressZipAsync(string destination, IEnumerable<string> items, string root)
|
||||||
{
|
{
|
||||||
await using var outStream = System.IO.File.Create(destination);
|
await using var outStream = System.IO.File.Create(destination);
|
||||||
await using var zipOutputStream = new ZipOutputStream(outStream);
|
await using var zipOutputStream = new ZipOutputStream(outStream);
|
||||||
|
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
await AddItemToZip(zipOutputStream, item, root);
|
await AddItemToZipAsync(zipOutputStream, item, root);
|
||||||
|
|
||||||
await zipOutputStream.FlushAsync();
|
await zipOutputStream.FlushAsync();
|
||||||
await outStream.FlushAsync();
|
await outStream.FlushAsync();
|
||||||
@@ -140,7 +140,7 @@ public class CompressController : Controller
|
|||||||
outStream.Close();
|
outStream.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AddItemToZip(ZipOutputStream outputStream, string item, string root)
|
private async Task AddItemToZipAsync(ZipOutputStream outputStream, string item, string root)
|
||||||
{
|
{
|
||||||
if (System.IO.File.Exists(item))
|
if (System.IO.File.Exists(item))
|
||||||
{
|
{
|
||||||
@@ -175,7 +175,7 @@ public class CompressController : Controller
|
|||||||
if (Directory.Exists(item))
|
if (Directory.Exists(item))
|
||||||
{
|
{
|
||||||
foreach (var subItem in Directory.EnumerateFileSystemEntries(item))
|
foreach (var subItem in Directory.EnumerateFileSystemEntries(item))
|
||||||
await AddItemToZip(outputStream, subItem, root);
|
await AddItemToZipAsync(outputStream, subItem, root);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public class DecompressController : Controller
|
|||||||
private const string BaseDirectory = "storage";
|
private const string BaseDirectory = "storage";
|
||||||
|
|
||||||
[HttpPost("decompress")]
|
[HttpPost("decompress")]
|
||||||
public async Task Decompress([FromBody] DecompressRequest request)
|
public async Task DecompressAsync([FromBody] DecompressRequest request)
|
||||||
{
|
{
|
||||||
var path = Path.Combine(BaseDirectory, FilePathHelper.SanitizePath(request.Path));
|
var path = Path.Combine(BaseDirectory, FilePathHelper.SanitizePath(request.Path));
|
||||||
var destination = Path.Combine(BaseDirectory, FilePathHelper.SanitizePath(request.Destination));
|
var destination = Path.Combine(BaseDirectory, FilePathHelper.SanitizePath(request.Destination));
|
||||||
@@ -25,18 +25,18 @@ public class DecompressController : Controller
|
|||||||
switch (request.Format)
|
switch (request.Format)
|
||||||
{
|
{
|
||||||
case "tar.gz":
|
case "tar.gz":
|
||||||
await DecompressTarGz(path, destination);
|
await DecompressTarGzAsync(path, destination);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "zip":
|
case "zip":
|
||||||
await DecompressZip(path, destination);
|
await DecompressZipAsync(path, destination);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Tar Gz
|
#region Tar Gz
|
||||||
|
|
||||||
private async Task DecompressTarGz(string path, string destination)
|
private async Task DecompressTarGzAsync(string path, string destination)
|
||||||
{
|
{
|
||||||
await using var fs = System.IO.File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
await using var fs = System.IO.File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||||
await using var gzipInputStream = new GZipInputStream(fs);
|
await using var gzipInputStream = new GZipInputStream(fs);
|
||||||
@@ -74,7 +74,7 @@ public class DecompressController : Controller
|
|||||||
|
|
||||||
#region Zip
|
#region Zip
|
||||||
|
|
||||||
private async Task DecompressZip(string path, string destination)
|
private async Task DecompressZipAsync(string path, string destination)
|
||||||
{
|
{
|
||||||
await using var fs = System.IO.File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
await using var fs = System.IO.File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||||
await using var zipInputStream = new ZipInputStream(fs);
|
await using var zipInputStream = new ZipInputStream(fs);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public class DownloadUrlController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task Get([FromQuery] string path)
|
public async Task GetAsync([FromQuery] string path)
|
||||||
{
|
{
|
||||||
var physicalPath = Path.Combine(BaseDirectory, FilePathHelper.SanitizePath(path));
|
var physicalPath = Path.Combine(BaseDirectory, FilePathHelper.SanitizePath(path));
|
||||||
var name = Path.GetFileName(physicalPath);
|
var name = Path.GetFileName(physicalPath);
|
||||||
@@ -55,7 +55,7 @@ public class DownloadUrlController : Controller
|
|||||||
await using var zipStream = new ZipOutputStream(Response.Body);
|
await using var zipStream = new ZipOutputStream(Response.Body);
|
||||||
zipStream.IsStreamOwner = false;
|
zipStream.IsStreamOwner = false;
|
||||||
|
|
||||||
await StreamFolderAsZip(zipStream, physicalPath, baseDirectory, HttpContext.RequestAborted);
|
await StreamFolderAsZipAsync(zipStream, physicalPath, baseDirectory, HttpContext.RequestAborted);
|
||||||
}
|
}
|
||||||
catch (ZipException)
|
catch (ZipException)
|
||||||
{
|
{
|
||||||
@@ -68,7 +68,7 @@ public class DownloadUrlController : Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task StreamFolderAsZip(
|
private async Task StreamFolderAsZipAsync(
|
||||||
ZipOutputStream zipStream,
|
ZipOutputStream zipStream,
|
||||||
string path, string rootPath,
|
string path, string rootPath,
|
||||||
CancellationToken cancellationToken
|
CancellationToken cancellationToken
|
||||||
@@ -102,7 +102,7 @@ public class DownloadUrlController : Controller
|
|||||||
if (HttpContext.RequestAborted.IsCancellationRequested)
|
if (HttpContext.RequestAborted.IsCancellationRequested)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await StreamFolderAsZip(zipStream, directory, rootPath, cancellationToken);
|
await StreamFolderAsZipAsync(zipStream, directory, rootPath, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ public class DownloadUrlController : Controller
|
|||||||
// Yes I know we can just create that url on the client as the exist validation is done on both endpoints,
|
// Yes I know we can just create that url on the client as the exist validation is done on both endpoints,
|
||||||
// but we leave it here for future modifications. E.g. using a distributed file provider or smth like that
|
// but we leave it here for future modifications. E.g. using a distributed file provider or smth like that
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public Task<DownloadUrlResponse> Post([FromQuery] string path)
|
public Task<DownloadUrlResponse> PostAsync([FromQuery] string path)
|
||||||
{
|
{
|
||||||
var safePath = FilePathHelper.SanitizePath(path);
|
var safePath = FilePathHelper.SanitizePath(path);
|
||||||
var physicalPath = Path.Combine(BaseDirectory, safePath);
|
var physicalPath = Path.Combine(BaseDirectory, safePath);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public class FilesController : Controller
|
|||||||
private const string BaseDirectory = "storage";
|
private const string BaseDirectory = "storage";
|
||||||
|
|
||||||
[HttpPost("touch")]
|
[HttpPost("touch")]
|
||||||
public async Task CreateFile([FromQuery] string path)
|
public async Task CreateFileAsync([FromQuery] string path)
|
||||||
{
|
{
|
||||||
var safePath = FilePathHelper.SanitizePath(path);
|
var safePath = FilePathHelper.SanitizePath(path);
|
||||||
var physicalPath = Path.Combine(BaseDirectory, safePath);
|
var physicalPath = Path.Combine(BaseDirectory, safePath);
|
||||||
@@ -31,7 +31,7 @@ public class FilesController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("mkdir")]
|
[HttpPost("mkdir")]
|
||||||
public Task CreateFolder([FromQuery] string path)
|
public Task CreateFolderAsync([FromQuery] string path)
|
||||||
{
|
{
|
||||||
var safePath = FilePathHelper.SanitizePath(path);
|
var safePath = FilePathHelper.SanitizePath(path);
|
||||||
var physicalPath = Path.Combine(BaseDirectory, safePath);
|
var physicalPath = Path.Combine(BaseDirectory, safePath);
|
||||||
@@ -47,7 +47,7 @@ public class FilesController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("list")]
|
[HttpGet("list")]
|
||||||
public Task<FileSystemEntryResponse[]> List([FromQuery] string path)
|
public Task<FileSystemEntryResponse[]> ListAsync([FromQuery] string path)
|
||||||
{
|
{
|
||||||
var safePath = FilePathHelper.SanitizePath(path);
|
var safePath = FilePathHelper.SanitizePath(path);
|
||||||
var physicalPath = Path.Combine(BaseDirectory, safePath);
|
var physicalPath = Path.Combine(BaseDirectory, safePath);
|
||||||
@@ -92,7 +92,7 @@ public class FilesController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("move")]
|
[HttpPost("move")]
|
||||||
public Task Move([FromQuery] string oldPath, [FromQuery] string newPath)
|
public Task MoveAsync([FromQuery] string oldPath, [FromQuery] string newPath)
|
||||||
{
|
{
|
||||||
var oldSafePath = FilePathHelper.SanitizePath(oldPath);
|
var oldSafePath = FilePathHelper.SanitizePath(oldPath);
|
||||||
var newSafePath = FilePathHelper.SanitizePath(newPath);
|
var newSafePath = FilePathHelper.SanitizePath(newPath);
|
||||||
@@ -123,7 +123,7 @@ public class FilesController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("delete")]
|
[HttpDelete("delete")]
|
||||||
public Task Delete([FromQuery] string path)
|
public Task DeleteAsync([FromQuery] string path)
|
||||||
{
|
{
|
||||||
var safePath = FilePathHelper.SanitizePath(path);
|
var safePath = FilePathHelper.SanitizePath(path);
|
||||||
var physicalDirPath = Path.Combine(BaseDirectory, safePath);
|
var physicalDirPath = Path.Combine(BaseDirectory, safePath);
|
||||||
@@ -141,7 +141,7 @@ public class FilesController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("upload")]
|
[HttpPost("upload")]
|
||||||
public async Task<IResult> Upload([FromQuery] string path)
|
public async Task<IResult> UploadAsync([FromQuery] string path)
|
||||||
{
|
{
|
||||||
if (Request.Form.Files.Count != 1)
|
if (Request.Form.Files.Count != 1)
|
||||||
return Results.Problem("Only one file is allowed in the request", statusCode: 400);
|
return Results.Problem("Only one file is allowed in the request", statusCode: 400);
|
||||||
@@ -179,7 +179,7 @@ public class FilesController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("download")]
|
[HttpGet("download")]
|
||||||
public async Task Download([FromQuery] string path)
|
public async Task DownloadAsync([FromQuery] string path)
|
||||||
{
|
{
|
||||||
var safePath = FilePathHelper.SanitizePath(path);
|
var safePath = FilePathHelper.SanitizePath(path);
|
||||||
var physicalPath = Path.Combine(BaseDirectory, safePath);
|
var physicalPath = Path.Combine(BaseDirectory, safePath);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public class HangfireController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("stats")]
|
[HttpGet("stats")]
|
||||||
public Task<HangfireStatsResponse> GetStats()
|
public Task<HangfireStatsResponse> GetStatsAsync()
|
||||||
{
|
{
|
||||||
var statistics = JobStorage.GetMonitoringApi().GetStatistics();
|
var statistics = JobStorage.GetMonitoringApi().GetStatistics();
|
||||||
|
|
||||||
|
|||||||
@@ -18,21 +18,21 @@ public class SystemController : Controller
|
|||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Authorize(Policy = "permissions:admin.system.overview")]
|
[Authorize(Policy = "permissions:admin.system.overview")]
|
||||||
public async Task<SystemOverviewResponse> GetOverview()
|
public async Task<SystemOverviewResponse> GetOverviewAsync()
|
||||||
{
|
{
|
||||||
return new()
|
return new()
|
||||||
{
|
{
|
||||||
Uptime = await ApplicationService.GetUptime(),
|
Uptime = await ApplicationService.GetUptimeAsync(),
|
||||||
CpuUsage = await ApplicationService.GetCpuUsage(),
|
CpuUsage = await ApplicationService.GetCpuUsageAsync(),
|
||||||
MemoryUsage = await ApplicationService.GetMemoryUsage(),
|
MemoryUsage = await ApplicationService.GetMemoryUsageAsync(),
|
||||||
OperatingSystem = await ApplicationService.GetOsName()
|
OperatingSystem = await ApplicationService.GetOsNameAsync()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("shutdown")]
|
[HttpPost("shutdown")]
|
||||||
[Authorize(Policy = "permissions:admin.system.shutdown")]
|
[Authorize(Policy = "permissions:admin.system.shutdown")]
|
||||||
public async Task Shutdown()
|
public async Task ShutdownAsync()
|
||||||
{
|
{
|
||||||
await ApplicationService.Shutdown();
|
await ApplicationService.ShutdownAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@@ -6,10 +5,10 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
using MoonCore.Exceptions;
|
using MoonCore.Exceptions;
|
||||||
using MoonCore.Extended.Abstractions;
|
using MoonCore.Extended.Abstractions;
|
||||||
using MoonCore.Extended.Helpers;
|
using MoonCore.Extended.Helpers;
|
||||||
using MoonCore.Extended.Models;
|
|
||||||
using MoonCore.Models;
|
using MoonCore.Models;
|
||||||
using Moonlight.ApiServer.Database.Entities;
|
using Moonlight.ApiServer.Database.Entities;
|
||||||
using Moonlight.ApiServer.Services;
|
using Moonlight.ApiServer.Services;
|
||||||
|
using Moonlight.ApiServer.Mappers;
|
||||||
using Moonlight.Shared.Http.Requests.Admin.Users;
|
using Moonlight.Shared.Http.Requests.Admin.Users;
|
||||||
using Moonlight.Shared.Http.Responses.Admin.Users;
|
using Moonlight.Shared.Http.Responses.Admin.Users;
|
||||||
|
|
||||||
@@ -28,60 +27,78 @@ public class UsersController : Controller
|
|||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Authorize(Policy = "permissions:admin.users.get")]
|
[Authorize(Policy = "permissions:admin.users.get")]
|
||||||
public async Task<IPagedData<UserResponse>> Get([FromQuery] PagedOptions options)
|
public async Task<ActionResult<ICountedData<UserResponse>>> GetAsync(
|
||||||
|
[FromQuery] int startIndex,
|
||||||
|
[FromQuery] int count,
|
||||||
|
[FromQuery] string? orderBy,
|
||||||
|
[FromQuery] string? filter,
|
||||||
|
[FromQuery] string orderByDir = "asc"
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var count = await UserRepository.Get().CountAsync();
|
if (count > 100)
|
||||||
|
return Problem("You cannot fetch more items than 100 at a time", statusCode: 400);
|
||||||
|
|
||||||
|
IQueryable<User> query = UserRepository.Get();
|
||||||
|
|
||||||
var users = await UserRepository
|
query = orderBy switch
|
||||||
.Get()
|
{
|
||||||
.OrderBy(x => x.Id)
|
nameof(Database.Entities.User.Id) => orderByDir == "desc"
|
||||||
.Skip(options.Page * options.PageSize)
|
? query.OrderByDescending(x => x.Id)
|
||||||
.Take(options.PageSize)
|
: query.OrderBy(x => x.Id),
|
||||||
|
|
||||||
|
nameof(Database.Entities.User.Username) => orderByDir == "desc"
|
||||||
|
? query.OrderByDescending(x => x.Username)
|
||||||
|
: query.OrderBy(x => x.Username),
|
||||||
|
|
||||||
|
nameof(Database.Entities.User.Email) => orderByDir == "desc"
|
||||||
|
? query.OrderByDescending(x => x.Email)
|
||||||
|
: query.OrderBy(x => x.Email),
|
||||||
|
|
||||||
|
_ => query.OrderBy(x => x.Id)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(filter))
|
||||||
|
{
|
||||||
|
query = query.Where(x =>
|
||||||
|
EF.Functions.ILike(x.Username, $"%{filter}%") ||
|
||||||
|
EF.Functions.ILike(x.Email, $"%{filter}%")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalCount = await query.CountAsync();
|
||||||
|
|
||||||
|
var items = await query
|
||||||
|
.Skip(startIndex)
|
||||||
|
.Take(count)
|
||||||
|
.AsNoTracking()
|
||||||
|
.ProjectToResponse()
|
||||||
.ToArrayAsync();
|
.ToArrayAsync();
|
||||||
|
|
||||||
var mappedUsers = users
|
return new CountedData<UserResponse>()
|
||||||
.Select(x => new UserResponse()
|
|
||||||
{
|
|
||||||
Id = x.Id,
|
|
||||||
Email = x.Email,
|
|
||||||
Username = x.Username,
|
|
||||||
Permissions = x.Permissions
|
|
||||||
})
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
return new PagedData<UserResponse>()
|
|
||||||
{
|
{
|
||||||
CurrentPage = options.Page,
|
Items = items,
|
||||||
Items = mappedUsers,
|
TotalCount = totalCount
|
||||||
PageSize = options.PageSize,
|
|
||||||
TotalItems = count,
|
|
||||||
TotalPages = count == 0 ? 0 : (count - 1) / options.PageSize
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
[Authorize(Policy = "permissions:admin.users.get")]
|
[Authorize(Policy = "permissions:admin.users.get")]
|
||||||
public async Task<UserResponse> GetSingle(int id)
|
public async Task<ActionResult<UserResponse>> GetSingleAsync(int id)
|
||||||
{
|
{
|
||||||
var user = await UserRepository
|
var user = await UserRepository
|
||||||
.Get()
|
.Get()
|
||||||
|
.ProjectToResponse()
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
.FirstOrDefaultAsync(x => x.Id == id);
|
||||||
|
|
||||||
if (user == null)
|
if (user == null)
|
||||||
throw new HttpApiException("No user with that id found", 404);
|
return Problem("No user with that id found", statusCode: 404);
|
||||||
|
|
||||||
return new UserResponse()
|
return user;
|
||||||
{
|
|
||||||
Id = user.Id,
|
|
||||||
Email = user.Email,
|
|
||||||
Username = user.Username,
|
|
||||||
Permissions = user.Permissions
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Authorize(Policy = "permissions:admin.users.create")]
|
[Authorize(Policy = "permissions:admin.users.create")]
|
||||||
public async Task<UserResponse> Create([FromBody] CreateUserRequest request)
|
public async Task<ActionResult<UserResponse>> CreateAsync([FromBody] CreateUserRequest request)
|
||||||
{
|
{
|
||||||
// Reformat values
|
// Reformat values
|
||||||
request.Username = request.Username.ToLower().Trim();
|
request.Username = request.Username.ToLower().Trim();
|
||||||
@@ -89,10 +106,10 @@ public class UsersController : Controller
|
|||||||
|
|
||||||
// Check for users with the same values
|
// Check for users with the same values
|
||||||
if (UserRepository.Get().Any(x => x.Username == request.Username))
|
if (UserRepository.Get().Any(x => x.Username == request.Username))
|
||||||
throw new HttpApiException("A user with that username already exists", 400);
|
return Problem("A user with that username already exists", statusCode: 400);
|
||||||
|
|
||||||
if (UserRepository.Get().Any(x => x.Email == request.Email))
|
if (UserRepository.Get().Any(x => x.Email == request.Email))
|
||||||
throw new HttpApiException("A user with that email address already exists", 400);
|
return Problem("A user with that email address already exists", statusCode: 400);
|
||||||
|
|
||||||
var hashedPassword = HashHelper.Hash(request.Password);
|
var hashedPassword = HashHelper.Hash(request.Password);
|
||||||
|
|
||||||
@@ -104,27 +121,21 @@ public class UsersController : Controller
|
|||||||
Permissions = request.Permissions
|
Permissions = request.Permissions
|
||||||
};
|
};
|
||||||
|
|
||||||
var finalUser = await UserRepository.Add(user);
|
var finalUser = await UserRepository.AddAsync(user);
|
||||||
|
|
||||||
return new UserResponse()
|
return UserMapper.ToResponse(finalUser);
|
||||||
{
|
|
||||||
Id = finalUser.Id,
|
|
||||||
Email = finalUser.Email,
|
|
||||||
Username = finalUser.Username,
|
|
||||||
Permissions = finalUser.Permissions
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPatch("{id}")]
|
[HttpPatch("{id}")]
|
||||||
[Authorize(Policy = "permissions:admin.users.update")]
|
[Authorize(Policy = "permissions:admin.users.update")]
|
||||||
public async Task<UserResponse> Update([FromRoute] int id, [FromBody] UpdateUserRequest request)
|
public async Task<ActionResult<UserResponse>> UpdateAsync([FromRoute] int id, [FromBody] UpdateUserRequest request)
|
||||||
{
|
{
|
||||||
var user = await UserRepository
|
var user = await UserRepository
|
||||||
.Get()
|
.Get()
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
.FirstOrDefaultAsync(x => x.Id == id);
|
||||||
|
|
||||||
if (user == null)
|
if (user == null)
|
||||||
throw new HttpApiException("No user with that id found", 404);
|
return Problem("No user with that id found", statusCode: 404);
|
||||||
|
|
||||||
// Reformat values
|
// Reformat values
|
||||||
request.Username = request.Username.ToLower().Trim();
|
request.Username = request.Username.ToLower().Trim();
|
||||||
@@ -132,10 +143,10 @@ public class UsersController : Controller
|
|||||||
|
|
||||||
// Check for users with the same values
|
// Check for users with the same values
|
||||||
if (UserRepository.Get().Any(x => x.Username == request.Username && x.Id != user.Id))
|
if (UserRepository.Get().Any(x => x.Username == request.Username && x.Id != user.Id))
|
||||||
throw new HttpApiException("A user with that username already exists", 400);
|
return Problem("Another user with that username already exists", statusCode: 400);
|
||||||
|
|
||||||
if (UserRepository.Get().Any(x => x.Email == request.Email && x.Id != user.Id))
|
if (UserRepository.Get().Any(x => x.Email == request.Email && x.Id != user.Id))
|
||||||
throw new HttpApiException("A user with that email address already exists", 400);
|
return Problem("Another user with that email address already exists", statusCode: 400);
|
||||||
|
|
||||||
// Perform hashing the password if required
|
// Perform hashing the password if required
|
||||||
if (!string.IsNullOrEmpty(request.Password))
|
if (!string.IsNullOrEmpty(request.Password))
|
||||||
@@ -153,38 +164,33 @@ public class UsersController : Controller
|
|||||||
user.Email = request.Email;
|
user.Email = request.Email;
|
||||||
user.Username = request.Username;
|
user.Username = request.Username;
|
||||||
|
|
||||||
await UserRepository.Update(user);
|
await UserRepository.UpdateAsync(user);
|
||||||
|
|
||||||
return new UserResponse()
|
return UserMapper.ToResponse(user);
|
||||||
{
|
|
||||||
Id = user.Id,
|
|
||||||
Email = user.Email,
|
|
||||||
Username = user.Username,
|
|
||||||
Permissions = user.Permissions
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
[Authorize(Policy = "permissions:admin.users.delete")]
|
[Authorize(Policy = "permissions:admin.users.delete")]
|
||||||
public async Task Delete([FromRoute] int id, [FromQuery] bool force = false)
|
public async Task<ActionResult> DeleteAsync([FromRoute] int id, [FromQuery] bool force = false)
|
||||||
{
|
{
|
||||||
var user = await UserRepository
|
var user = await UserRepository
|
||||||
.Get()
|
.Get()
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
.FirstOrDefaultAsync(x => x.Id == id);
|
||||||
|
|
||||||
if (user == null)
|
if (user == null)
|
||||||
throw new HttpApiException("No user with that id found", 404);
|
return Problem("No user with that id found", statusCode: 404);
|
||||||
|
|
||||||
var deletionService = HttpContext.RequestServices.GetRequiredService<UserDeletionService>();
|
var deletionService = HttpContext.RequestServices.GetRequiredService<UserDeletionService>();
|
||||||
|
|
||||||
if (!force)
|
if (!force)
|
||||||
{
|
{
|
||||||
var validationResult = await deletionService.Validate(user);
|
var validationResult = await deletionService.ValidateAsync(user);
|
||||||
|
|
||||||
if (!validationResult.IsAllowed)
|
if (!validationResult.IsAllowed)
|
||||||
throw new HttpApiException($"Unable to delete user", 400, validationResult.Reason);
|
return Problem("Unable to delete user", statusCode: 400, title: validationResult.Reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
await deletionService.Delete(user, force);
|
await deletionService.DeleteAsync(user, force);
|
||||||
|
return NoContent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ public class AuthController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<AuthSchemeResponse[]> GetSchemes()
|
public async Task<AuthSchemeResponse[]> GetSchemesAsync()
|
||||||
{
|
{
|
||||||
var schemes = await SchemeProvider.GetAllSchemesAsync();
|
var schemes = await SchemeProvider.GetAllSchemesAsync();
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ public class AuthController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{identifier:alpha}")]
|
[HttpGet("{identifier:alpha}")]
|
||||||
public async Task StartScheme([FromRoute] string identifier)
|
public async Task StartSchemeAsync([FromRoute] string identifier)
|
||||||
{
|
{
|
||||||
// Validate identifier against our enable list
|
// Validate identifier against our enable list
|
||||||
var allowedSchemes = Configuration.Authentication.EnabledSchemes;
|
var allowedSchemes = Configuration.Authentication.EnabledSchemes;
|
||||||
@@ -91,7 +91,7 @@ public class AuthController : Controller
|
|||||||
|
|
||||||
[Authorize]
|
[Authorize]
|
||||||
[HttpGet("check")]
|
[HttpGet("check")]
|
||||||
public async Task<AuthClaimResponse[]> Check()
|
public async Task<AuthClaimResponse[]> CheckAsync()
|
||||||
{
|
{
|
||||||
var username = User.FindFirstValue(ClaimTypes.Name)!;
|
var username = User.FindFirstValue(ClaimTypes.Name)!;
|
||||||
var id = User.FindFirstValue(ClaimTypes.NameIdentifier)!;
|
var id = User.FindFirstValue(ClaimTypes.NameIdentifier)!;
|
||||||
@@ -113,7 +113,7 @@ public class AuthController : Controller
|
|||||||
foreach (var extension in Extensions)
|
foreach (var extension in Extensions)
|
||||||
{
|
{
|
||||||
claims.AddRange(
|
claims.AddRange(
|
||||||
await extension.GetFrontendClaims(User)
|
await extension.GetFrontendClaimsAsync(User)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ public class AuthController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("logout")]
|
[HttpGet("logout")]
|
||||||
public async Task Logout()
|
public async Task LogoutAsync()
|
||||||
{
|
{
|
||||||
await HttpContext.SignOutAsync();
|
await HttpContext.SignOutAsync();
|
||||||
await Results.Redirect("/").ExecuteAsync(HttpContext);
|
await Results.Redirect("/").ExecuteAsync(HttpContext);
|
||||||
|
|||||||
@@ -18,13 +18,13 @@ public class FrontendController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("frontend.json")]
|
[HttpGet("frontend.json")]
|
||||||
public async Task<FrontendConfiguration> GetConfiguration()
|
public async Task<FrontendConfiguration> GetConfigurationAsync()
|
||||||
=> await FrontendService.GetConfiguration();
|
=> await FrontendService.GetConfigurationAsync();
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<IResult> Index()
|
public async Task<IResult> IndexAsync()
|
||||||
{
|
{
|
||||||
var content = await FrontendService.GenerateIndexHtml();
|
var content = await FrontendService.GenerateIndexHtmlAsync();
|
||||||
|
|
||||||
return Results.Text(content, "text/html", Encoding.UTF8);
|
return Results.Text(content, "text/html", Encoding.UTF8);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,29 +45,29 @@ public class LocalAuthController : Controller
|
|||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[HttpGet("login")]
|
[HttpGet("login")]
|
||||||
public async Task<IResult> Login()
|
public async Task<ActionResult> LoginAsync()
|
||||||
{
|
{
|
||||||
var html = await ComponentHelper.RenderComponent<Login>(ServiceProvider);
|
var html = await ComponentHelper.RenderToHtmlAsync<Login>(ServiceProvider);
|
||||||
|
|
||||||
return Results.Content(html, "text/html");
|
return Content(html, "text/html");
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("register")]
|
[HttpGet("register")]
|
||||||
public async Task<IResult> Register()
|
public async Task<ActionResult> RegisterAsync()
|
||||||
{
|
{
|
||||||
var html = await ComponentHelper.RenderComponent<Register>(ServiceProvider);
|
var html = await ComponentHelper.RenderToHtmlAsync<Register>(ServiceProvider);
|
||||||
|
|
||||||
return Results.Content(html, "text/html");
|
return Content(html, "text/html");
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[HttpPost("login")]
|
[HttpPost("login")]
|
||||||
public async Task<IResult> Login([FromForm] string email, [FromForm] string password)
|
public async Task<ActionResult> LoginAsync([FromForm] string email, [FromForm] string password)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Perform login
|
// Perform login
|
||||||
var user = await InternalLogin(email, password);
|
var user = await InternalLoginAsync(email, password);
|
||||||
|
|
||||||
// Login user
|
// Login user
|
||||||
var options = Options.Get(LocalAuthConstants.AuthenticationScheme);
|
var options = Options.Get(LocalAuthConstants.AuthenticationScheme);
|
||||||
@@ -84,34 +84,34 @@ public class LocalAuthController : Controller
|
|||||||
), new AuthenticationProperties());
|
), new AuthenticationProperties());
|
||||||
|
|
||||||
// Redirect back to wasm app
|
// Redirect back to wasm app
|
||||||
return Results.Redirect("/");
|
return Redirect("/");
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
string errorMessage;
|
string errorMessage;
|
||||||
|
|
||||||
if (e is HttpApiException apiException)
|
if (e is AggregateException aggregateException)
|
||||||
errorMessage = apiException.Title;
|
errorMessage = aggregateException.Message;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
errorMessage = "An internal error occured";
|
errorMessage = "An internal error occured";
|
||||||
Logger.LogError(e, "An unhandled error occured while logging in user");
|
Logger.LogError(e, "An unhandled error occured while logging in user");
|
||||||
}
|
}
|
||||||
|
|
||||||
var html = await ComponentHelper.RenderComponent<Login>(ServiceProvider,
|
var html = await ComponentHelper.RenderToHtmlAsync<Login>(ServiceProvider,
|
||||||
parameters => { parameters["ErrorMessage"] = errorMessage; });
|
parameters => { parameters["ErrorMessage"] = errorMessage; });
|
||||||
|
|
||||||
return Results.Content(html, "text/html");
|
return Content(html, "text/html");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("register")]
|
[HttpPost("register")]
|
||||||
public async Task<IResult> Register([FromForm] string email, [FromForm] string password, [FromForm] string username)
|
public async Task<ActionResult> RegisterAsync([FromForm] string email, [FromForm] string password, [FromForm] string username)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Perform register
|
// Perform register
|
||||||
var user = await InternalRegister(username, email, password);
|
var user = await InternalRegisterAsync(username, email, password);
|
||||||
|
|
||||||
// Login user
|
// Login user
|
||||||
var options = Options.Get(LocalAuthConstants.AuthenticationScheme);
|
var options = Options.Get(LocalAuthConstants.AuthenticationScheme);
|
||||||
@@ -128,37 +128,37 @@ public class LocalAuthController : Controller
|
|||||||
), new AuthenticationProperties());
|
), new AuthenticationProperties());
|
||||||
|
|
||||||
// Redirect back to wasm app
|
// Redirect back to wasm app
|
||||||
return Results.Redirect("/");
|
return Redirect("/");
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
string errorMessage;
|
string errorMessage;
|
||||||
|
|
||||||
if (e is HttpApiException apiException)
|
if (e is AggregateException aggregateException)
|
||||||
errorMessage = apiException.Title;
|
errorMessage = aggregateException.Message;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
errorMessage = "An internal error occured";
|
errorMessage = "An internal error occured";
|
||||||
Logger.LogError(e, "An unhandled error occured while logging in user");
|
Logger.LogError(e, "An unhandled error occured while logging in user");
|
||||||
}
|
}
|
||||||
|
|
||||||
var html = await ComponentHelper.RenderComponent<Register>(ServiceProvider,
|
var html = await ComponentHelper.RenderToHtmlAsync<Register>(ServiceProvider,
|
||||||
parameters => { parameters["ErrorMessage"] = errorMessage; });
|
parameters => { parameters["ErrorMessage"] = errorMessage; });
|
||||||
|
|
||||||
return Results.Content(html, "text/html");
|
return Content(html, "text/html");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<User> InternalRegister(string username, string email, string password)
|
private async Task<User> InternalRegisterAsync(string username, string email, string password)
|
||||||
{
|
{
|
||||||
email = email.ToLower();
|
email = email.ToLower();
|
||||||
username = username.ToLower();
|
username = username.ToLower();
|
||||||
|
|
||||||
if (await UserRepository.Get().AnyAsync(x => x.Username == username))
|
if (await UserRepository.Get().AnyAsync(x => x.Username == username))
|
||||||
throw new HttpApiException("A account with that username already exists", 400);
|
throw new AggregateException("A account with that username already exists");
|
||||||
|
|
||||||
if (await UserRepository.Get().AnyAsync(x => x.Email == email))
|
if (await UserRepository.Get().AnyAsync(x => x.Email == email))
|
||||||
throw new HttpApiException("A account with that email already exists", 400);
|
throw new AggregateException("A account with that email already exists");
|
||||||
|
|
||||||
string[] permissions = [];
|
string[] permissions = [];
|
||||||
|
|
||||||
@@ -180,12 +180,12 @@ public class LocalAuthController : Controller
|
|||||||
Permissions = permissions
|
Permissions = permissions
|
||||||
};
|
};
|
||||||
|
|
||||||
var finalUser = await UserRepository.Add(user);
|
var finalUser = await UserRepository.AddAsync(user);
|
||||||
|
|
||||||
return finalUser;
|
return finalUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<User> InternalLogin(string email, string password)
|
private async Task<User> InternalLoginAsync(string email, string password)
|
||||||
{
|
{
|
||||||
email = email.ToLower();
|
email = email.ToLower();
|
||||||
|
|
||||||
@@ -194,10 +194,10 @@ public class LocalAuthController : Controller
|
|||||||
.FirstOrDefaultAsync(x => x.Email == email);
|
.FirstOrDefaultAsync(x => x.Email == email);
|
||||||
|
|
||||||
if (user == null)
|
if (user == null)
|
||||||
throw new HttpApiException("Invalid combination of email and password", 400);
|
throw new AggregateException("Invalid combination of email and password");
|
||||||
|
|
||||||
if (!HashHelper.Verify(password, user.Password))
|
if (!HashHelper.Verify(password, user.Password))
|
||||||
throw new HttpApiException("Invalid combination of email and password", 400);
|
throw new AggregateException("Invalid combination of email and password");
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public class SwaggerController : Controller
|
|||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Authorize]
|
[Authorize]
|
||||||
public async Task<ActionResult> Get()
|
public async Task<ActionResult> GetAsync()
|
||||||
{
|
{
|
||||||
if (!Configuration.Development.EnableApiDocs)
|
if (!Configuration.Development.EnableApiDocs)
|
||||||
return BadRequest("Api docs are disabled");
|
return BadRequest("Api docs are disabled");
|
||||||
@@ -32,7 +32,7 @@ public class SwaggerController : Controller
|
|||||||
var options = new ApiDocsOptions();
|
var options = new ApiDocsOptions();
|
||||||
var optionsJson = JsonSerializer.Serialize(options);
|
var optionsJson = JsonSerializer.Serialize(options);
|
||||||
|
|
||||||
var html = await ComponentHelper.RenderComponent<SwaggerPage>(
|
var html = await ComponentHelper.RenderToHtmlAsync<SwaggerPage>(
|
||||||
ServiceProvider,
|
ServiceProvider,
|
||||||
parameters =>
|
parameters =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace Moonlight.ApiServer.Http.Hubs;
|
|||||||
public class DiagnoseHub : Hub
|
public class DiagnoseHub : Hub
|
||||||
{
|
{
|
||||||
[HubMethodName("Ping")]
|
[HubMethodName("Ping")]
|
||||||
public async Task Ping()
|
public async Task PingAsync()
|
||||||
{
|
{
|
||||||
await Clients.All.SendAsync("Pong");
|
await Clients.All.SendAsync("Pong");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public class ApplicationMetric : IMetric
|
|||||||
private Gauge<int> CpuUsage;
|
private Gauge<int> CpuUsage;
|
||||||
private Gauge<double> Uptime;
|
private Gauge<double> Uptime;
|
||||||
|
|
||||||
public Task Initialize(Meter meter)
|
public Task InitializeAsync(Meter meter)
|
||||||
{
|
{
|
||||||
MemoryUsage = meter.CreateGauge<long>("moonlight_memory_usage");
|
MemoryUsage = meter.CreateGauge<long>("moonlight_memory_usage");
|
||||||
CpuUsage = meter.CreateGauge<int>("moonlight_cpu_usage");
|
CpuUsage = meter.CreateGauge<int>("moonlight_cpu_usage");
|
||||||
@@ -20,17 +20,17 @@ public class ApplicationMetric : IMetric
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Run(IServiceProvider provider, CancellationToken cancellationToken)
|
public async Task RunAsync(IServiceProvider provider, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var applicationService = provider.GetRequiredService<ApplicationService>();
|
var applicationService = provider.GetRequiredService<ApplicationService>();
|
||||||
|
|
||||||
var memory = await applicationService.GetMemoryUsage();
|
var memory = await applicationService.GetMemoryUsageAsync();
|
||||||
MemoryUsage.Record(memory);
|
MemoryUsage.Record(memory);
|
||||||
|
|
||||||
var uptime = await applicationService.GetUptime();
|
var uptime = await applicationService.GetUptimeAsync();
|
||||||
Uptime.Record(uptime.TotalSeconds);
|
Uptime.Record(uptime.TotalSeconds);
|
||||||
|
|
||||||
var cpu = await applicationService.GetCpuUsage();
|
var cpu = await applicationService.GetCpuUsageAsync();
|
||||||
CpuUsage.Record(cpu);
|
CpuUsage.Record(cpu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,14 +11,14 @@ public class UsersMetric : IMetric
|
|||||||
{
|
{
|
||||||
private Gauge<int> Users;
|
private Gauge<int> Users;
|
||||||
|
|
||||||
public Task Initialize(Meter meter)
|
public Task InitializeAsync(Meter meter)
|
||||||
{
|
{
|
||||||
Users = meter.CreateGauge<int>("moonlight_users");
|
Users = meter.CreateGauge<int>("moonlight_users");
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Run(IServiceProvider provider, CancellationToken cancellationToken)
|
public async Task RunAsync(IServiceProvider provider, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var usersRepo = provider.GetRequiredService<DatabaseRepository<User>>();
|
var usersRepo = provider.GetRequiredService<DatabaseRepository<User>>();
|
||||||
var count = await usersRepo.Get().CountAsync(cancellationToken: cancellationToken);
|
var count = await usersRepo.Get().CountAsync(cancellationToken: cancellationToken);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace Moonlight.ApiServer.Implementations.Startup;
|
|||||||
|
|
||||||
public class CoreStartup : IPluginStartup
|
public class CoreStartup : IPluginStartup
|
||||||
{
|
{
|
||||||
public Task BuildApplication(IServiceProvider serviceProvider, IHostApplicationBuilder builder)
|
public Task BuildApplicationAsync(IServiceProvider serviceProvider, IHostApplicationBuilder builder)
|
||||||
{
|
{
|
||||||
var configuration = serviceProvider.GetRequiredService<AppConfiguration>();
|
var configuration = serviceProvider.GetRequiredService<AppConfiguration>();
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ public class CoreStartup : IPluginStartup
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task ConfigureApplication(IServiceProvider serviceProvider, IApplicationBuilder app)
|
public Task ConfigureApplicationAsync(IServiceProvider serviceProvider, IApplicationBuilder app)
|
||||||
{
|
{
|
||||||
var configuration = serviceProvider.GetRequiredService<AppConfiguration>();
|
var configuration = serviceProvider.GetRequiredService<AppConfiguration>();
|
||||||
|
|
||||||
@@ -156,7 +156,7 @@ public class CoreStartup : IPluginStartup
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task ConfigureEndpoints(IServiceProvider serviceProvider, IEndpointRouteBuilder routeBuilder)
|
public Task ConfigureEndpointsAsync(IServiceProvider serviceProvider, IEndpointRouteBuilder routeBuilder)
|
||||||
{
|
{
|
||||||
var configuration = serviceProvider.GetRequiredService<AppConfiguration>();
|
var configuration = serviceProvider.GetRequiredService<AppConfiguration>();
|
||||||
|
|
||||||
|
|||||||
@@ -12,5 +12,5 @@ public interface IAuthCheckExtension
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="principal">The principal of the current signed-in user</param>
|
/// <param name="principal">The principal of the current signed-in user</param>
|
||||||
/// <returns>An array of claim responses which gets added to the list of claims to send to the frontend</returns>
|
/// <returns>An array of claim responses which gets added to the list of claims to send to the frontend</returns>
|
||||||
public Task<AuthClaimResponse[]> GetFrontendClaims(ClaimsPrincipal principal);
|
public Task<AuthClaimResponse[]> GetFrontendClaimsAsync(ClaimsPrincipal principal);
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,6 @@ namespace Moonlight.ApiServer.Interfaces;
|
|||||||
|
|
||||||
public interface IMetric
|
public interface IMetric
|
||||||
{
|
{
|
||||||
public Task Initialize(Meter meter);
|
public Task InitializeAsync(Meter meter);
|
||||||
public Task Run(IServiceProvider provider, CancellationToken cancellationToken);
|
public Task RunAsync(IServiceProvider provider, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,7 @@ public interface IUserAuthExtension
|
|||||||
/// <param name="user">The current user this method is called for</param>
|
/// <param name="user">The current user this method is called for</param>
|
||||||
/// <param name="principal">The principal after being processed by moonlight itself</param>
|
/// <param name="principal">The principal after being processed by moonlight itself</param>
|
||||||
/// <returns>The result of the synchronisation. Returning false will immediately invalidate the sign-in and no other extensions will be called</returns>
|
/// <returns>The result of the synchronisation. Returning false will immediately invalidate the sign-in and no other extensions will be called</returns>
|
||||||
public Task<bool> Sync(User user, ClaimsPrincipal principal);
|
public Task<bool> SyncAsync(User user, ClaimsPrincipal principal);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// IMPORTANT: Please note that heavy operations should not occur in this method as it will be called for every request
|
/// IMPORTANT: Please note that heavy operations should not occur in this method as it will be called for every request
|
||||||
@@ -21,5 +21,5 @@ public interface IUserAuthExtension
|
|||||||
/// <param name="user">The current user this method is called for</param>
|
/// <param name="user">The current user this method is called for</param>
|
||||||
/// <param name="principal">The principal after being processed by moonlight itself</param>
|
/// <param name="principal">The principal after being processed by moonlight itself</param>
|
||||||
/// <returns>The result of the validation. Returning false will immediately invalidate the users session and no other extensions will be called</returns>
|
/// <returns>The result of the validation. Returning false will immediately invalidate the users session and no other extensions will be called</returns>
|
||||||
public Task<bool> Validate(User user, ClaimsPrincipal principal);
|
public Task<bool> ValidateAsync(User user, ClaimsPrincipal principal);
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,6 @@ namespace Moonlight.ApiServer.Interfaces;
|
|||||||
|
|
||||||
public interface IUserDeleteHandler
|
public interface IUserDeleteHandler
|
||||||
{
|
{
|
||||||
public Task<UserDeleteValidationResult> Validate(User user);
|
public Task<UserDeleteValidationResult> ValidateAsync(User user);
|
||||||
public Task Delete(User user, bool force);
|
public Task DeleteAsync(User user, bool force);
|
||||||
}
|
}
|
||||||
19
Moonlight.ApiServer/Mappers/ApiKeyMapper.cs
Normal file
19
Moonlight.ApiServer/Mappers/ApiKeyMapper.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Moonlight.ApiServer.Database.Entities;
|
||||||
|
using Moonlight.Shared.Http.Requests.Admin.ApiKeys;
|
||||||
|
using Moonlight.Shared.Http.Responses.Admin.ApiKeys;
|
||||||
|
using Riok.Mapperly.Abstractions;
|
||||||
|
|
||||||
|
namespace Moonlight.ApiServer.Mappers;
|
||||||
|
|
||||||
|
[Mapper]
|
||||||
|
public static partial class ApiKeyMapper
|
||||||
|
{
|
||||||
|
// Mappers
|
||||||
|
public static partial ApiKeyResponse ToResponse(ApiKey apiKey);
|
||||||
|
public static partial ApiKey ToApiKey(CreateApiKeyRequest request);
|
||||||
|
public static partial void Merge([MappingTarget] ApiKey apiKey, UpdateApiKeyRequest request);
|
||||||
|
|
||||||
|
// EF Relations
|
||||||
|
|
||||||
|
public static partial IQueryable<ApiKeyResponse> ProjectToResponse(this IQueryable<ApiKey> apiKeys);
|
||||||
|
}
|
||||||
@@ -8,7 +8,12 @@ namespace Moonlight.ApiServer.Mappers;
|
|||||||
[Mapper]
|
[Mapper]
|
||||||
public static partial class ThemeMapper
|
public static partial class ThemeMapper
|
||||||
{
|
{
|
||||||
|
// Mappers
|
||||||
public static partial ThemeResponse ToResponse(Theme theme);
|
public static partial ThemeResponse ToResponse(Theme theme);
|
||||||
public static partial Theme ToTheme(CreateThemeRequest request);
|
public static partial Theme ToTheme(CreateThemeRequest request);
|
||||||
public static partial void Merge([MappingTarget] Theme theme, UpdateThemeRequest request);
|
public static partial void Merge([MappingTarget] Theme theme, UpdateThemeRequest request);
|
||||||
|
|
||||||
|
// EF Relations
|
||||||
|
|
||||||
|
public static partial IQueryable<ThemeResponse> ProjectToResponse(this IQueryable<Theme> themes);
|
||||||
}
|
}
|
||||||
15
Moonlight.ApiServer/Mappers/UserMapper.cs
Normal file
15
Moonlight.ApiServer/Mappers/UserMapper.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using Moonlight.ApiServer.Database.Entities;
|
||||||
|
using Moonlight.Shared.Http.Responses.Admin.Users;
|
||||||
|
using Riok.Mapperly.Abstractions;
|
||||||
|
|
||||||
|
namespace Moonlight.ApiServer.Mappers;
|
||||||
|
|
||||||
|
[Mapper]
|
||||||
|
public static partial class UserMapper
|
||||||
|
{
|
||||||
|
// Mappers
|
||||||
|
public static partial UserResponse ToResponse(User user);
|
||||||
|
|
||||||
|
// EF Relations
|
||||||
|
public static partial IQueryable<UserResponse> ProjectToResponse(this IQueryable<User> users);
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PackageId>Moonlight.ApiServer</PackageId>
|
<PackageId>Moonlight.ApiServer</PackageId>
|
||||||
<Version>2.1.10</Version>
|
<Version>2.1.11</Version>
|
||||||
<Authors>Moonlight Panel</Authors>
|
<Authors>Moonlight Panel</Authors>
|
||||||
<Description>A build of the api server for moonlight development</Description>
|
<Description>A build of the api server for moonlight development</Description>
|
||||||
<PackageProjectUrl>https://github.com/Moonlight-Panel/Moonlight</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/Moonlight-Panel/Moonlight</PackageProjectUrl>
|
||||||
@@ -25,10 +25,12 @@
|
|||||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.20"/>
|
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.20"/>
|
||||||
<PackageReference Include="Hangfire.Core" Version="1.8.20"/>
|
<PackageReference Include="Hangfire.Core" Version="1.8.20"/>
|
||||||
<PackageReference Include="Hangfire.EntityFrameworkCore" Version="0.7.0"/>
|
<PackageReference Include="Hangfire.EntityFrameworkCore" Version="0.7.0"/>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.9" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="9.0.9" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="9.0.9" />
|
||||||
<PackageReference Include="MoonCore" Version="1.9.7" />
|
<PackageReference Include="MoonCore" Version="2.0.1" />
|
||||||
<PackageReference Include="MoonCore.Extended" Version="1.3.7" />
|
<PackageReference Include="MoonCore.Extended" Version="1.4.0" />
|
||||||
<PackageReference Include="MoonCore.PluginFramework.Generator" Version="1.0.2"/>
|
<PackageReference Include="MoonCore.PluginFramework.Generator" Version="1.0.2"/>
|
||||||
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.12.0" />
|
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.12.0" />
|
||||||
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.12.0-beta.1"/>
|
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.12.0-beta.1"/>
|
||||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.12.0"/>
|
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.12.0"/>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace Moonlight.ApiServer.Plugins;
|
|||||||
|
|
||||||
public interface IPluginStartup
|
public interface IPluginStartup
|
||||||
{
|
{
|
||||||
public Task BuildApplication(IServiceProvider serviceProvider, IHostApplicationBuilder builder);
|
public Task BuildApplicationAsync(IServiceProvider serviceProvider, IHostApplicationBuilder builder);
|
||||||
public Task ConfigureApplication(IServiceProvider serviceProvider, IApplicationBuilder app);
|
public Task ConfigureApplicationAsync(IServiceProvider serviceProvider, IApplicationBuilder app);
|
||||||
public Task ConfigureEndpoints(IServiceProvider serviceProvider, IEndpointRouteBuilder routeBuilder);
|
public Task ConfigureEndpointsAsync(IServiceProvider serviceProvider, IEndpointRouteBuilder routeBuilder);
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,7 @@ public class ApiKeyAuthService
|
|||||||
ApiKeyRepository = apiKeyRepository;
|
ApiKeyRepository = apiKeyRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> Validate(ClaimsPrincipal? principal)
|
public async Task<bool> ValidateAsync(ClaimsPrincipal? principal)
|
||||||
{
|
{
|
||||||
// Ignore malformed claims principal
|
// Ignore malformed claims principal
|
||||||
if (principal is not { Identity.IsAuthenticated: true })
|
if (principal is not { Identity.IsAuthenticated: true })
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public class ApplicationService
|
|||||||
Host = host;
|
Host = host;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<string> GetOsName()
|
public Task<string> GetOsNameAsync()
|
||||||
{
|
{
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
{
|
{
|
||||||
@@ -58,7 +58,7 @@ public class ApplicationService
|
|||||||
return Task.FromResult("N/A");
|
return Task.FromResult("N/A");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<long> GetMemoryUsage()
|
public async Task<long> GetMemoryUsageAsync()
|
||||||
{
|
{
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
{
|
{
|
||||||
@@ -87,14 +87,14 @@ public class ApplicationService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<TimeSpan> GetUptime()
|
public Task<TimeSpan> GetUptimeAsync()
|
||||||
{
|
{
|
||||||
var process = Process.GetCurrentProcess();
|
var process = Process.GetCurrentProcess();
|
||||||
var uptime = DateTime.Now - process.StartTime;
|
var uptime = DateTime.Now - process.StartTime;
|
||||||
return Task.FromResult(uptime);
|
return Task.FromResult(uptime);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<int> GetCpuUsage()
|
public Task<int> GetCpuUsageAsync()
|
||||||
{
|
{
|
||||||
var process = Process.GetCurrentProcess();
|
var process = Process.GetCurrentProcess();
|
||||||
var cpuTime = process.TotalProcessorTime;
|
var cpuTime = process.TotalProcessorTime;
|
||||||
@@ -105,7 +105,7 @@ public class ApplicationService
|
|||||||
return Task.FromResult(cpuUsage);
|
return Task.FromResult(cpuUsage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task Shutdown()
|
public Task ShutdownAsync()
|
||||||
{
|
{
|
||||||
Logger.LogCritical("Restart of api server has been requested");
|
Logger.LogCritical("Restart of api server has been requested");
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public class FrontendService
|
|||||||
ThemeRepository = themeRepository;
|
ThemeRepository = themeRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<FrontendConfiguration> GetConfiguration()
|
public Task<FrontendConfiguration> GetConfigurationAsync()
|
||||||
{
|
{
|
||||||
var configuration = new FrontendConfiguration()
|
var configuration = new FrontendConfiguration()
|
||||||
{
|
{
|
||||||
@@ -51,7 +51,7 @@ public class FrontendService
|
|||||||
return Task.FromResult(configuration);
|
return Task.FromResult(configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> GenerateIndexHtml() // TODO: Cache
|
public async Task<string> GenerateIndexHtmlAsync() // TODO: Cache
|
||||||
{
|
{
|
||||||
// Load requested theme
|
// Load requested theme
|
||||||
var theme = await ThemeRepository
|
var theme = await ThemeRepository
|
||||||
@@ -70,7 +70,7 @@ public class FrontendService
|
|||||||
.Distinct()
|
.Distinct()
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
return await ComponentHelper.RenderComponent<FrontendPage>(
|
return await ComponentHelper.RenderToHtmlAsync<FrontendPage>(
|
||||||
ServiceProvider,
|
ServiceProvider,
|
||||||
parameters =>
|
parameters =>
|
||||||
{
|
{
|
||||||
@@ -82,7 +82,7 @@ public class FrontendService
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Stream> GenerateZip() // TODO: Rework to be able to extract everything successfully
|
public async Task<Stream> GenerateZipAsync() // TODO: Rework to be able to extract everything successfully
|
||||||
{
|
{
|
||||||
// We only allow the access to this function when we are actually hosting the frontend
|
// We only allow the access to this function when we are actually hosting the frontend
|
||||||
if (!Configuration.Frontend.EnableHosting)
|
if (!Configuration.Frontend.EnableHosting)
|
||||||
@@ -109,16 +109,16 @@ public class FrontendService
|
|||||||
var zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true);
|
var zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true);
|
||||||
|
|
||||||
// Add wasm application
|
// Add wasm application
|
||||||
await ArchiveFsItem(zipArchive, wasmPath, wasmPath);
|
await ArchiveFsItemAsync(zipArchive, wasmPath, wasmPath);
|
||||||
|
|
||||||
// Add blazor files
|
// Add blazor files
|
||||||
await ArchiveFsItem(zipArchive, blazorPath, blazorPath, "_framework/");
|
await ArchiveFsItemAsync(zipArchive, blazorPath, blazorPath, "_framework/");
|
||||||
|
|
||||||
// Add frontend.json
|
// Add frontend.json
|
||||||
var frontendConfig = await GetConfiguration();
|
var frontendConfig = await GetConfigurationAsync();
|
||||||
frontendConfig.HostEnvironment = "Static";
|
frontendConfig.HostEnvironment = "Static";
|
||||||
var frontendJson = JsonSerializer.Serialize(frontendConfig);
|
var frontendJson = JsonSerializer.Serialize(frontendConfig);
|
||||||
await ArchiveText(zipArchive, "frontend.json", frontendJson);
|
await ArchiveTextAsync(zipArchive, "frontend.json", frontendJson);
|
||||||
|
|
||||||
// Finish zip archive and reset stream so the code calling this function can process it
|
// Finish zip archive and reset stream so the code calling this function can process it
|
||||||
zipArchive.Dispose();
|
zipArchive.Dispose();
|
||||||
@@ -128,7 +128,7 @@ public class FrontendService
|
|||||||
return memoryStream;
|
return memoryStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ArchiveFsItem(ZipArchive archive, string path, string prefixToRemove, string prefixToAdd = "")
|
private async Task ArchiveFsItemAsync(ZipArchive archive, string path, string prefixToRemove, string prefixToAdd = "")
|
||||||
{
|
{
|
||||||
if (File.Exists(path))
|
if (File.Exists(path))
|
||||||
{
|
{
|
||||||
@@ -147,17 +147,17 @@ public class FrontendService
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
foreach (var directoryItem in Directory.EnumerateFileSystemEntries(path))
|
foreach (var directoryItem in Directory.EnumerateFileSystemEntries(path))
|
||||||
await ArchiveFsItem(archive, directoryItem, prefixToRemove, prefixToAdd);
|
await ArchiveFsItemAsync(archive, directoryItem, prefixToRemove, prefixToAdd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ArchiveText(ZipArchive archive, string path, string content)
|
private async Task ArchiveTextAsync(ZipArchive archive, string path, string content)
|
||||||
{
|
{
|
||||||
var data = Encoding.UTF8.GetBytes(content);
|
var data = Encoding.UTF8.GetBytes(content);
|
||||||
await ArchiveBytes(archive, path, data);
|
await ArchiveBytesAsync(archive, path, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ArchiveBytes(ZipArchive archive, string path, byte[] bytes)
|
private async Task ArchiveBytesAsync(ZipArchive archive, string path, byte[] bytes)
|
||||||
{
|
{
|
||||||
var entry = archive.CreateEntry(path);
|
var entry = archive.CreateEntry(path);
|
||||||
await using var dataStream = entry.Open();
|
await using var dataStream = entry.Open();
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public class MetricsBackgroundService : BackgroundService
|
|||||||
Metrics = metrics.ToArray();
|
Metrics = metrics.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Initialize()
|
private async Task InitializeAsync()
|
||||||
{
|
{
|
||||||
Logger.LogDebug(
|
Logger.LogDebug(
|
||||||
"Initializing metrics: {names}",
|
"Initializing metrics: {names}",
|
||||||
@@ -41,12 +41,12 @@ public class MetricsBackgroundService : BackgroundService
|
|||||||
);
|
);
|
||||||
|
|
||||||
foreach (var metric in Metrics)
|
foreach (var metric in Metrics)
|
||||||
await metric.Initialize(Meter);
|
await metric.InitializeAsync(Meter);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
await Initialize();
|
await InitializeAsync();
|
||||||
|
|
||||||
while (!stoppingToken.IsCancellationRequested)
|
while (!stoppingToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
@@ -56,7 +56,7 @@ public class MetricsBackgroundService : BackgroundService
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await metric.Run(scope.ServiceProvider, stoppingToken);
|
await metric.RunAsync(scope.ServiceProvider, stoppingToken);
|
||||||
}
|
}
|
||||||
catch (TaskCanceledException)
|
catch (TaskCanceledException)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public class UserAuthService
|
|||||||
Extensions = extensions;
|
Extensions = extensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> Sync(ClaimsPrincipal? principal)
|
public async Task<bool> SyncAsync(ClaimsPrincipal? principal)
|
||||||
{
|
{
|
||||||
// Ignore malformed claims principal
|
// Ignore malformed claims principal
|
||||||
if (principal is not { Identity.IsAuthenticated: true })
|
if (principal is not { Identity.IsAuthenticated: true })
|
||||||
@@ -80,7 +80,7 @@ public class UserAuthService
|
|||||||
permissions = ["*"];
|
permissions = ["*"];
|
||||||
}
|
}
|
||||||
|
|
||||||
user = await UserRepository.Add(new User()
|
user = await UserRepository.AddAsync(new User()
|
||||||
{
|
{
|
||||||
Email = email,
|
Email = email,
|
||||||
TokenValidTimestamp = DateTimeOffset.UtcNow.AddMinutes(-1),
|
TokenValidTimestamp = DateTimeOffset.UtcNow.AddMinutes(-1),
|
||||||
@@ -94,7 +94,7 @@ public class UserAuthService
|
|||||||
if (user.Username != username)
|
if (user.Username != username)
|
||||||
{
|
{
|
||||||
user.Username = username;
|
user.Username = username;
|
||||||
await UserRepository.Update(user);
|
await UserRepository.UpdateAsync(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enrich claims with required metadata
|
// Enrich claims with required metadata
|
||||||
@@ -107,7 +107,7 @@ public class UserAuthService
|
|||||||
// Call extensions
|
// Call extensions
|
||||||
foreach (var extension in Extensions)
|
foreach (var extension in Extensions)
|
||||||
{
|
{
|
||||||
var result = await extension.Sync(user, principal);
|
var result = await extension.SyncAsync(user, principal);
|
||||||
|
|
||||||
if (!result) // Exit immediately if result is false
|
if (!result) // Exit immediately if result is false
|
||||||
return false;
|
return false;
|
||||||
@@ -116,7 +116,7 @@ public class UserAuthService
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> Validate(ClaimsPrincipal? principal)
|
public async Task<bool> ValidateAsync(ClaimsPrincipal? principal)
|
||||||
{
|
{
|
||||||
// Ignore malformed claims principal
|
// Ignore malformed claims principal
|
||||||
if (principal is not { Identity.IsAuthenticated: true })
|
if (principal is not { Identity.IsAuthenticated: true })
|
||||||
@@ -157,7 +157,7 @@ public class UserAuthService
|
|||||||
// Call extensions
|
// Call extensions
|
||||||
foreach (var extension in Extensions)
|
foreach (var extension in Extensions)
|
||||||
{
|
{
|
||||||
var result = await extension.Validate(user, principal);
|
var result = await extension.ValidateAsync(user, principal);
|
||||||
|
|
||||||
if (!result) // Exit immediately if result is false
|
if (!result) // Exit immediately if result is false
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -19,11 +19,11 @@ public class UserDeletionService
|
|||||||
Handlers = handlers.ToArray();
|
Handlers = handlers.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<UserDeleteValidationResult> Validate(User user)
|
public async Task<UserDeleteValidationResult> ValidateAsync(User user)
|
||||||
{
|
{
|
||||||
foreach (var handler in Handlers)
|
foreach (var handler in Handlers)
|
||||||
{
|
{
|
||||||
var result = await handler.Validate(user);
|
var result = await handler.ValidateAsync(user);
|
||||||
|
|
||||||
if (!result.IsAllowed)
|
if (!result.IsAllowed)
|
||||||
return result;
|
return result;
|
||||||
@@ -32,11 +32,11 @@ public class UserDeletionService
|
|||||||
return UserDeleteValidationResult.Allow();
|
return UserDeleteValidationResult.Allow();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Delete(User user, bool force)
|
public async Task DeleteAsync(User user, bool force)
|
||||||
{
|
{
|
||||||
foreach (var handler in Handlers)
|
foreach (var handler in Handlers)
|
||||||
await Delete(user, force);
|
await handler.DeleteAsync(user, force);
|
||||||
|
|
||||||
await UserRepository.Remove(user);
|
await UserRepository.RemoveAsync(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ namespace Moonlight.ApiServer.Startup;
|
|||||||
|
|
||||||
public partial class Startup
|
public partial class Startup
|
||||||
{
|
{
|
||||||
private Task RegisterAuth()
|
private Task RegisterAuthAsync()
|
||||||
{
|
{
|
||||||
WebApplicationBuilder.Services
|
WebApplicationBuilder.Services
|
||||||
.AddAuthentication(options => { options.DefaultScheme = "MainScheme"; })
|
.AddAuthentication(options => { options.DefaultScheme = "MainScheme"; })
|
||||||
@@ -62,7 +62,7 @@ public partial class Startup
|
|||||||
.RequestServices
|
.RequestServices
|
||||||
.GetRequiredService<ApiKeyAuthService>();
|
.GetRequiredService<ApiKeyAuthService>();
|
||||||
|
|
||||||
var result = await apiKeyAuthService.Validate(context.Principal);
|
var result = await apiKeyAuthService.ValidateAsync(context.Principal);
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
context.Fail("API key has been deleted");
|
context.Fail("API key has been deleted");
|
||||||
@@ -120,7 +120,7 @@ public partial class Startup
|
|||||||
.RequestServices
|
.RequestServices
|
||||||
.GetRequiredService<UserAuthService>();
|
.GetRequiredService<UserAuthService>();
|
||||||
|
|
||||||
var result = await userSyncService.Sync(context.Principal);
|
var result = await userSyncService.SyncAsync(context.Principal);
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
context.Principal = new();
|
context.Principal = new();
|
||||||
@@ -135,7 +135,7 @@ public partial class Startup
|
|||||||
.RequestServices
|
.RequestServices
|
||||||
.GetRequiredService<UserAuthService>();
|
.GetRequiredService<UserAuthService>();
|
||||||
|
|
||||||
var result = await userSyncService.Validate(context.Principal);
|
var result = await userSyncService.ValidateAsync(context.Principal);
|
||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
context.RejectPrincipal();
|
context.RejectPrincipal();
|
||||||
@@ -178,7 +178,7 @@ public partial class Startup
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task UseAuth()
|
private Task UseAuthAsync()
|
||||||
{
|
{
|
||||||
WebApplication.UseAuthentication();
|
WebApplication.UseAuthentication();
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace Moonlight.ApiServer.Startup;
|
|||||||
|
|
||||||
public partial class Startup
|
public partial class Startup
|
||||||
{
|
{
|
||||||
private Task RegisterBase()
|
private Task RegisterBaseAsync()
|
||||||
{
|
{
|
||||||
WebApplicationBuilder.Services.AutoAddServices<Startup>();
|
WebApplicationBuilder.Services.AutoAddServices<Startup>();
|
||||||
WebApplicationBuilder.Services.AddHttpClient();
|
WebApplicationBuilder.Services.AddHttpClient();
|
||||||
@@ -29,7 +29,7 @@ public partial class Startup
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task UseBase()
|
private Task UseBaseAsync()
|
||||||
{
|
{
|
||||||
WebApplication.UseRouting();
|
WebApplication.UseRouting();
|
||||||
WebApplication.UseExceptionHandler();
|
WebApplication.UseExceptionHandler();
|
||||||
@@ -37,7 +37,7 @@ public partial class Startup
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task MapBase()
|
private Task MapBaseAsync()
|
||||||
{
|
{
|
||||||
WebApplication.MapControllers();
|
WebApplication.MapControllers();
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ public partial class Startup
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task ConfigureKestrel()
|
private Task ConfigureKestrelAsync()
|
||||||
{
|
{
|
||||||
WebApplicationBuilder.WebHost.ConfigureKestrel(kestrelOptions =>
|
WebApplicationBuilder.WebHost.ConfigureKestrel(kestrelOptions =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ namespace Moonlight.ApiServer.Startup;
|
|||||||
|
|
||||||
public partial class Startup
|
public partial class Startup
|
||||||
{
|
{
|
||||||
private async Task SetupAppConfiguration()
|
private async Task SetupAppConfigurationAsync()
|
||||||
{
|
{
|
||||||
var configPath = Path.Combine("storage", "config.yml");
|
var configPath = Path.Combine("storage", "config.yml");
|
||||||
|
|
||||||
await YamlDefaultGenerator.Generate<AppConfiguration>(configPath);
|
await YamlDefaultGenerator.GenerateAsync<AppConfiguration>(configPath);
|
||||||
|
|
||||||
// Configure configuration (wow)
|
// Configure configuration (wow)
|
||||||
var configurationBuilder = new ConfigurationBuilder();
|
var configurationBuilder = new ConfigurationBuilder();
|
||||||
@@ -27,7 +27,7 @@ public partial class Startup
|
|||||||
configurationRoot.Bind(Configuration);
|
configurationRoot.Bind(Configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task RegisterAppConfiguration()
|
private Task RegisterAppConfigurationAsync()
|
||||||
{
|
{
|
||||||
WebApplicationBuilder.Services.AddSingleton(Configuration);
|
WebApplicationBuilder.Services.AddSingleton(Configuration);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace Moonlight.ApiServer.Startup;
|
|||||||
|
|
||||||
public partial class Startup
|
public partial class Startup
|
||||||
{
|
{
|
||||||
private Task RegisterDatabase()
|
private Task RegisterDatabaseAsync()
|
||||||
{
|
{
|
||||||
WebApplicationBuilder.Services.AddDatabaseMappings();
|
WebApplicationBuilder.Services.AddDatabaseMappings();
|
||||||
WebApplicationBuilder.Services.AddServiceCollectionAccessor();
|
WebApplicationBuilder.Services.AddServiceCollectionAccessor();
|
||||||
@@ -16,9 +16,9 @@ public partial class Startup
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task PrepareDatabase()
|
private async Task PrepareDatabaseAsync()
|
||||||
{
|
{
|
||||||
await WebApplication.Services.EnsureDatabaseMigrated();
|
await WebApplication.Services.EnsureDatabaseMigratedAsync();
|
||||||
|
|
||||||
WebApplication.Services.GenerateDatabaseMappings();
|
WebApplication.Services.GenerateDatabaseMappings();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ namespace Moonlight.ApiServer.Startup;
|
|||||||
|
|
||||||
public partial class Startup
|
public partial class Startup
|
||||||
{
|
{
|
||||||
private Task RegisterHangfire()
|
private Task RegisterHangfireAsync()
|
||||||
{
|
{
|
||||||
WebApplicationBuilder.Services.AddHangfire((provider, configuration) =>
|
WebApplicationBuilder.Services.AddHangfire((provider, configuration) =>
|
||||||
{
|
{
|
||||||
@@ -38,7 +38,7 @@ public partial class Startup
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task UseHangfire()
|
private Task UseHangfireAsync()
|
||||||
{
|
{
|
||||||
if (WebApplication.Environment.IsDevelopment())
|
if (WebApplication.Environment.IsDevelopment())
|
||||||
WebApplication.UseHangfireDashboard();
|
WebApplication.UseHangfireDashboard();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace Moonlight.ApiServer.Startup;
|
|||||||
|
|
||||||
public partial class Startup
|
public partial class Startup
|
||||||
{
|
{
|
||||||
private Task SetupLogging()
|
private Task SetupLoggingAsync()
|
||||||
{
|
{
|
||||||
var loggerFactory = new LoggerFactory();
|
var loggerFactory = new LoggerFactory();
|
||||||
loggerFactory.AddAnsiConsole();
|
loggerFactory.AddAnsiConsole();
|
||||||
@@ -16,7 +16,7 @@ public partial class Startup
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RegisterLogging()
|
private async Task RegisterLoggingAsync()
|
||||||
{
|
{
|
||||||
// Configure application logging
|
// Configure application logging
|
||||||
WebApplicationBuilder.Logging.ClearProviders();
|
WebApplicationBuilder.Logging.ClearProviders();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace Moonlight.ApiServer.Startup;
|
|||||||
|
|
||||||
public partial class Startup
|
public partial class Startup
|
||||||
{
|
{
|
||||||
private Task PrintVersion()
|
private Task PrintVersionAsync()
|
||||||
{
|
{
|
||||||
// Fancy start console output... yes very fancy :>
|
// Fancy start console output... yes very fancy :>
|
||||||
var rainbow = new Crayon.Rainbow(0.5);
|
var rainbow = new Crayon.Rainbow(0.5);
|
||||||
@@ -25,7 +25,7 @@ public partial class Startup
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task CreateStorage()
|
private Task CreateStorageAsync()
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory("storage");
|
Directory.CreateDirectory("storage");
|
||||||
Directory.CreateDirectory(Path.Combine("storage", "logs"));
|
Directory.CreateDirectory(Path.Combine("storage", "logs"));
|
||||||
@@ -33,7 +33,7 @@ public partial class Startup
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task RegisterCors()
|
private Task RegisterCorsAsync()
|
||||||
{
|
{
|
||||||
var allowedOrigins = Configuration.Kestrel.AllowedOrigins;
|
var allowedOrigins = Configuration.Kestrel.AllowedOrigins;
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ public partial class Startup
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task UseCors()
|
private Task UseCorsAsync()
|
||||||
{
|
{
|
||||||
WebApplication.UseCors();
|
WebApplication.UseCors();
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public partial class Startup
|
|||||||
private IServiceProvider PluginLoadServiceProvider;
|
private IServiceProvider PluginLoadServiceProvider;
|
||||||
private IPluginStartup[] PluginStartups;
|
private IPluginStartup[] PluginStartups;
|
||||||
|
|
||||||
private Task InitializePlugins()
|
private Task InitializePluginsAsync()
|
||||||
{
|
{
|
||||||
// Create service provider for starting up
|
// Create service provider for starting up
|
||||||
var serviceCollection = new ServiceCollection();
|
var serviceCollection = new ServiceCollection();
|
||||||
@@ -28,13 +28,13 @@ public partial class Startup
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HookPluginBuild()
|
private async Task HookPluginBuildAsync()
|
||||||
{
|
{
|
||||||
foreach (var pluginAppStartup in PluginStartups)
|
foreach (var pluginAppStartup in PluginStartups)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await pluginAppStartup.BuildApplication(PluginLoadServiceProvider, WebApplicationBuilder);
|
await pluginAppStartup.BuildApplicationAsync(PluginLoadServiceProvider, WebApplicationBuilder);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -47,13 +47,13 @@ public partial class Startup
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HookPluginConfigure()
|
private async Task HookPluginConfigureAsync()
|
||||||
{
|
{
|
||||||
foreach (var pluginAppStartup in PluginStartups)
|
foreach (var pluginAppStartup in PluginStartups)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await pluginAppStartup.ConfigureApplication(PluginLoadServiceProvider, WebApplication);
|
await pluginAppStartup.ConfigureApplicationAsync(PluginLoadServiceProvider, WebApplication);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -66,13 +66,13 @@ public partial class Startup
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HookPluginEndpoints()
|
private async Task HookPluginEndpointsAsync()
|
||||||
{
|
{
|
||||||
foreach (var pluginEndpointStartup in PluginStartups)
|
foreach (var pluginEndpointStartup in PluginStartups)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await pluginEndpointStartup.ConfigureEndpoints(PluginLoadServiceProvider, WebApplication);
|
await pluginEndpointStartup.ConfigureEndpointsAsync(PluginLoadServiceProvider, WebApplication);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace Moonlight.ApiServer.Startup;
|
|||||||
|
|
||||||
public partial class Startup
|
public partial class Startup
|
||||||
{
|
{
|
||||||
public Task RegisterSignalR()
|
public Task RegisterSignalRAsync()
|
||||||
{
|
{
|
||||||
var signalRBuilder = WebApplicationBuilder.Services.AddSignalR();
|
var signalRBuilder = WebApplicationBuilder.Services.AddSignalR();
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ public partial class Startup
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task MapSignalR()
|
public Task MapSignalRAsync()
|
||||||
{
|
{
|
||||||
WebApplication.MapHub<DiagnoseHub>("/api/admin/system/diagnose/ws");
|
WebApplication.MapHub<DiagnoseHub>("/api/admin/system/diagnose/ws");
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public partial class Startup
|
|||||||
public WebApplication WebApplication { get; private set; }
|
public WebApplication WebApplication { get; private set; }
|
||||||
public WebApplicationBuilder WebApplicationBuilder { get; private set; }
|
public WebApplicationBuilder WebApplicationBuilder { get; private set; }
|
||||||
|
|
||||||
public Task Initialize(string[] args, IPluginStartup[]? plugins = null)
|
public Task InitializeAsync(string[] args, IPluginStartup[]? plugins = null)
|
||||||
{
|
{
|
||||||
Args = args;
|
Args = args;
|
||||||
PluginStartups = plugins ?? [];
|
PluginStartups = plugins ?? [];
|
||||||
@@ -27,43 +27,43 @@ public partial class Startup
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task AddMoonlight(WebApplicationBuilder builder)
|
public async Task AddMoonlightAsync(WebApplicationBuilder builder)
|
||||||
{
|
{
|
||||||
WebApplicationBuilder = builder;
|
WebApplicationBuilder = builder;
|
||||||
|
|
||||||
await PrintVersion();
|
await PrintVersionAsync();
|
||||||
|
|
||||||
await CreateStorage();
|
await CreateStorageAsync();
|
||||||
await SetupAppConfiguration();
|
await SetupAppConfigurationAsync();
|
||||||
await SetupLogging();
|
await SetupLoggingAsync();
|
||||||
await InitializePlugins();
|
await InitializePluginsAsync();
|
||||||
|
|
||||||
await ConfigureKestrel();
|
await ConfigureKestrelAsync();
|
||||||
await RegisterAppConfiguration();
|
await RegisterAppConfigurationAsync();
|
||||||
await RegisterLogging();
|
await RegisterLoggingAsync();
|
||||||
await RegisterBase();
|
await RegisterBaseAsync();
|
||||||
await RegisterDatabase();
|
await RegisterDatabaseAsync();
|
||||||
await RegisterAuth();
|
await RegisterAuthAsync();
|
||||||
await RegisterCors();
|
await RegisterCorsAsync();
|
||||||
await RegisterHangfire();
|
await RegisterHangfireAsync();
|
||||||
await RegisterSignalR();
|
await RegisterSignalRAsync();
|
||||||
await HookPluginBuild();
|
await HookPluginBuildAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task AddMoonlight(WebApplication application)
|
public async Task AddMoonlightAsync(WebApplication application)
|
||||||
{
|
{
|
||||||
WebApplication = application;
|
WebApplication = application;
|
||||||
|
|
||||||
await PrepareDatabase();
|
await PrepareDatabaseAsync();
|
||||||
|
|
||||||
await UseCors();
|
await UseCorsAsync();
|
||||||
await UseBase();
|
await UseBaseAsync();
|
||||||
await UseAuth();
|
await UseAuthAsync();
|
||||||
await UseHangfire();
|
await UseHangfireAsync();
|
||||||
await HookPluginConfigure();
|
await HookPluginConfigureAsync();
|
||||||
|
|
||||||
await MapBase();
|
await MapBaseAsync();
|
||||||
await MapSignalR();
|
await MapSignalRAsync();
|
||||||
await HookPluginEndpoints();
|
await HookPluginEndpointsAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,8 +15,8 @@
|
|||||||
<PackageReference Include="MoonCore.PluginFramework" Version="1.0.8" />
|
<PackageReference Include="MoonCore.PluginFramework" Version="1.0.8" />
|
||||||
<PackageReference Include="MoonCore.PluginFramework.Generator" Version="1.0.2" />
|
<PackageReference Include="MoonCore.PluginFramework.Generator" Version="1.0.2" />
|
||||||
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.9" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.8" PrivateAssets="all" />
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="9.0.9" PrivateAssets="all" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Import Project="Plugins.props" />
|
<Import Project="Plugins.props" />
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ pluginLoader.Initialize();
|
|||||||
|
|
||||||
var startup = new Startup();
|
var startup = new Startup();
|
||||||
|
|
||||||
await startup.Initialize(pluginLoader.Instances);
|
await startup.InitializeAsync(pluginLoader.Instances);
|
||||||
|
|
||||||
var wasmHostBuilder = WebAssemblyHostBuilder.CreateDefault(args);
|
var wasmHostBuilder = WebAssemblyHostBuilder.CreateDefault(args);
|
||||||
|
|
||||||
await startup.AddMoonlight(wasmHostBuilder);
|
await startup.AddMoonlightAsync(wasmHostBuilder);
|
||||||
|
|
||||||
var wasmApp = wasmHostBuilder.Build();
|
var wasmApp = wasmHostBuilder.Build();
|
||||||
|
|
||||||
await startup.AddMoonlight(wasmApp);
|
await startup.AddMoonlightAsync(wasmApp);
|
||||||
|
|
||||||
await wasmApp.RunAsync();
|
await wasmApp.RunAsync();
|
||||||
@@ -100,18 +100,38 @@
|
|||||||
.select {
|
.select {
|
||||||
@apply !border-base-content/20 border-2 ring-0! outline-0! focus:border-primary! focus-within:border-primary! bg-base-200/50;
|
@apply !border-base-content/20 border-2 ring-0! outline-0! focus:border-primary! focus-within:border-primary! bg-base-200/50;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table {
|
.table {
|
||||||
:where(th, td) {
|
:where(th, td) {
|
||||||
@apply py-1.5;
|
@apply py-1.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-item {
|
.dropdown-item {
|
||||||
@apply px-2.5 py-1.5 text-sm;
|
@apply px-2.5 py-1.5 text-sm;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
@apply bg-base-150;
|
@apply bg-base-150;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.advance-select-menu {
|
||||||
|
@apply !border-base-content/20 border-2 ring-0! outline-0! bg-base-200/50 !px-0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.advance-select-option {
|
||||||
|
@apply !rounded-none hover:!bg-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.advance-select-option.selected {
|
||||||
|
@apply !bg-primary !text-primary-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.advance-select-toggle {
|
||||||
|
@apply !border-base-content/20 border-2 ring-0! outline-0! focus:border-primary! focus-within:border-primary! bg-base-200/50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table thead {
|
||||||
|
@apply !normal-case;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@ namespace Moonlight.Client.Implementations;
|
|||||||
|
|
||||||
public class CoreStartup : IPluginStartup
|
public class CoreStartup : IPluginStartup
|
||||||
{
|
{
|
||||||
public Task BuildApplication(IServiceProvider serviceProvider, WebAssemblyHostBuilder builder)
|
public Task BuildApplicationAsync(IServiceProvider serviceProvider, WebAssemblyHostBuilder builder)
|
||||||
{
|
{
|
||||||
builder.Services.AddSingleton<ISidebarItemProvider, DefaultSidebarItemProvider>();
|
builder.Services.AddSingleton<ISidebarItemProvider, DefaultSidebarItemProvider>();
|
||||||
builder.Services.AddSingleton<IOverviewElementProvider, DefaultOverviewElementProvider>();
|
builder.Services.AddSingleton<IOverviewElementProvider, DefaultOverviewElementProvider>();
|
||||||
@@ -15,6 +15,6 @@ public class CoreStartup : IPluginStartup
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task ConfigureApplication(IServiceProvider serviceProvider, WebAssemblyHost app)
|
public Task ConfigureApplicationAsync(IServiceProvider serviceProvider, WebAssemblyHost app)
|
||||||
=> Task.CompletedTask;
|
=> Task.CompletedTask;
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@ public class LogErrorFilter : IGlobalErrorFilter
|
|||||||
Logger = logger;
|
Logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<bool> HandleException(Exception ex)
|
public Task<bool> HandleExceptionAsync(Exception ex)
|
||||||
{
|
{
|
||||||
Logger.LogError(ex, "Global error processed");
|
Logger.LogError(ex, "Global error processed");
|
||||||
return Task.FromResult(false);
|
return Task.FromResult(false);
|
||||||
|
|||||||
@@ -18,21 +18,21 @@ public class SystemFsAccess : IFsAccess, ICombineAccess, IArchiveAccess, IDownlo
|
|||||||
ApiClient = apiClient;
|
ApiClient = apiClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CreateFile(string path)
|
public async Task CreateFileAsync(string path)
|
||||||
{
|
{
|
||||||
await ApiClient.Post(
|
await ApiClient.Post(
|
||||||
$"{BaseApiUrl}/touch?path={path}"
|
$"{BaseApiUrl}/touch?path={path}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CreateDirectory(string path)
|
public async Task CreateDirectoryAsync(string path)
|
||||||
{
|
{
|
||||||
await ApiClient.Post(
|
await ApiClient.Post(
|
||||||
$"{BaseApiUrl}/mkdir?path={path}"
|
$"{BaseApiUrl}/mkdir?path={path}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FsEntry[]> List(string path)
|
public async Task<FsEntry[]> ListAsync(string path)
|
||||||
{
|
{
|
||||||
var entries = await ApiClient.GetJson<FileSystemEntryResponse[]>(
|
var entries = await ApiClient.GetJson<FileSystemEntryResponse[]>(
|
||||||
$"{BaseApiUrl}/list?path={path}"
|
$"{BaseApiUrl}/list?path={path}"
|
||||||
@@ -48,14 +48,14 @@ public class SystemFsAccess : IFsAccess, ICombineAccess, IArchiveAccess, IDownlo
|
|||||||
}).ToArray();
|
}).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Move(string oldPath, string newPath)
|
public async Task MoveAsync(string oldPath, string newPath)
|
||||||
{
|
{
|
||||||
await ApiClient.Post(
|
await ApiClient.Post(
|
||||||
$"{BaseApiUrl}/move?oldPath={oldPath}&newPath={newPath}"
|
$"{BaseApiUrl}/move?oldPath={oldPath}&newPath={newPath}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Read(string path, Func<Stream, Task> onHandleData)
|
public async Task ReadAsync(string path, Func<Stream, Task> onHandleData)
|
||||||
{
|
{
|
||||||
await using var stream = await ApiClient.GetStream(
|
await using var stream = await ApiClient.GetStream(
|
||||||
$"{BaseApiUrl}/download?path={path}"
|
$"{BaseApiUrl}/download?path={path}"
|
||||||
@@ -66,7 +66,7 @@ public class SystemFsAccess : IFsAccess, ICombineAccess, IArchiveAccess, IDownlo
|
|||||||
stream.Close();
|
stream.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Write(string path, Stream dataStream)
|
public async Task WriteAsync(string path, Stream dataStream)
|
||||||
{
|
{
|
||||||
using var multiPartForm = new MultipartFormDataContent();
|
using var multiPartForm = new MultipartFormDataContent();
|
||||||
|
|
||||||
@@ -78,14 +78,14 @@ public class SystemFsAccess : IFsAccess, ICombineAccess, IArchiveAccess, IDownlo
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Delete(string path)
|
public async Task DeleteAsync(string path)
|
||||||
{
|
{
|
||||||
await ApiClient.Delete(
|
await ApiClient.Delete(
|
||||||
$"{BaseApiUrl}/delete?path={path}"
|
$"{BaseApiUrl}/delete?path={path}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Combine(string destination, string[] files)
|
public async Task CombineAsync(string destination, string[] files)
|
||||||
{
|
{
|
||||||
await ApiClient.Post(
|
await ApiClient.Post(
|
||||||
$"{BaseApiUrl}/combine",
|
$"{BaseApiUrl}/combine",
|
||||||
@@ -103,7 +103,7 @@ public class SystemFsAccess : IFsAccess, ICombineAccess, IArchiveAccess, IDownlo
|
|||||||
new("tar.gz", ["tar.gz"], "Tar.gz Archive")
|
new("tar.gz", ["tar.gz"], "Tar.gz Archive")
|
||||||
];
|
];
|
||||||
|
|
||||||
public async Task Archive(
|
public async Task ArchiveAsync(
|
||||||
string destination,
|
string destination,
|
||||||
ArchiveFormat format,
|
ArchiveFormat format,
|
||||||
string root,
|
string root,
|
||||||
@@ -120,7 +120,7 @@ public class SystemFsAccess : IFsAccess, ICombineAccess, IArchiveAccess, IDownlo
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Unarchive(
|
public async Task UnarchiveAsync(
|
||||||
string path,
|
string path,
|
||||||
ArchiveFormat format,
|
ArchiveFormat format,
|
||||||
string destination,
|
string destination,
|
||||||
@@ -137,13 +137,13 @@ public class SystemFsAccess : IFsAccess, ICombineAccess, IArchiveAccess, IDownlo
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> GetFileUrl(string path)
|
public async Task<string> GetFileUrlAsync(string path)
|
||||||
=> await GetDownloadUrl(path);
|
=> await GetDownloadUrlAsync(path);
|
||||||
|
|
||||||
public async Task<string> GetFolderUrl(string path)
|
public async Task<string> GetFolderUrlAsync(string path)
|
||||||
=> await GetDownloadUrl(path);
|
=> await GetDownloadUrlAsync(path);
|
||||||
|
|
||||||
private async Task<string> GetDownloadUrl(string path)
|
private async Task<string> GetDownloadUrlAsync(string path)
|
||||||
{
|
{
|
||||||
var response = await ApiClient.PostJson<DownloadUrlResponse>(
|
var response = await ApiClient.PostJson<DownloadUrlResponse>(
|
||||||
$"{BaseApiUrl}/downloadUrl?path={path}"
|
$"{BaseApiUrl}/downloadUrl?path={path}"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using MoonCore.Blazor.FlyonUi.Exceptions;
|
using MoonCore.Blazor.FlyonUi.Exceptions;
|
||||||
|
using MoonCore.Blazor.FlyonUi.Toasts;
|
||||||
using MoonCore.Exceptions;
|
using MoonCore.Exceptions;
|
||||||
|
|
||||||
namespace Moonlight.Client.Implementations;
|
namespace Moonlight.Client.Implementations;
|
||||||
@@ -7,18 +8,25 @@ namespace Moonlight.Client.Implementations;
|
|||||||
public class UnauthenticatedErrorFilter : IGlobalErrorFilter
|
public class UnauthenticatedErrorFilter : IGlobalErrorFilter
|
||||||
{
|
{
|
||||||
private readonly NavigationManager Navigation;
|
private readonly NavigationManager Navigation;
|
||||||
|
private readonly ToastService ToastService;
|
||||||
|
|
||||||
public UnauthenticatedErrorFilter(NavigationManager navigation)
|
public UnauthenticatedErrorFilter(
|
||||||
|
NavigationManager navigation,
|
||||||
|
ToastService toastService
|
||||||
|
)
|
||||||
{
|
{
|
||||||
Navigation = navigation;
|
Navigation = navigation;
|
||||||
|
ToastService = toastService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<bool> HandleException(Exception ex)
|
public async Task<bool> HandleExceptionAsync(Exception ex)
|
||||||
{
|
{
|
||||||
if (ex is not HttpApiException { Status: 401 })
|
if (ex is not HttpApiException { Status: 401 })
|
||||||
return Task.FromResult(false);
|
return false;
|
||||||
|
|
||||||
|
await ToastService.InfoAsync("Session expired", "Your session has expired. Reloading..");
|
||||||
|
|
||||||
Navigation.NavigateTo("/api/auth/logout", true);
|
Navigation.NavigateTo("/api/auth/logout", true);
|
||||||
return Task.FromResult(true);
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PackageTags>frontend</PackageTags>
|
<PackageTags>frontend</PackageTags>
|
||||||
<PackageId>Moonlight.Client</PackageId>
|
<PackageId>Moonlight.Client</PackageId>
|
||||||
<Version>2.1.10</Version>
|
<Version>2.1.11</Version>
|
||||||
<Authors>Moonlight Panel</Authors>
|
<Authors>Moonlight Panel</Authors>
|
||||||
<Description>A build of the client for moonlight development</Description>
|
<Description>A build of the client for moonlight development</Description>
|
||||||
<PackageProjectUrl>https://github.com/Moonlight-Panel/Moonlight</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/Moonlight-Panel/Moonlight</PackageProjectUrl>
|
||||||
@@ -22,11 +22,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Blazor-ApexCharts" Version="6.0.2" />
|
<PackageReference Include="Blazor-ApexCharts" Version="6.0.2" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.9" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.9" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.9" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.8" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.8" />
|
||||||
<PackageReference Include="MoonCore" Version="1.9.7" />
|
<PackageReference Include="MoonCore" Version="2.0.1" />
|
||||||
<PackageReference Include="MoonCore.Blazor" Version="1.3.1" />
|
<PackageReference Include="MoonCore.Blazor.FlyonUi" Version="1.2.5" />
|
||||||
<PackageReference Include="MoonCore.Blazor.FlyonUi" Version="1.1.9" />
|
|
||||||
<PackageReference Include="System.Security.Claims" Version="4.3.0" />
|
<PackageReference Include="System.Security.Claims" Version="4.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ namespace Moonlight.Client.Plugins;
|
|||||||
|
|
||||||
public interface IPluginStartup
|
public interface IPluginStartup
|
||||||
{
|
{
|
||||||
public Task BuildApplication(IServiceProvider serviceProvider, WebAssemblyHostBuilder builder);
|
public Task BuildApplicationAsync(IServiceProvider serviceProvider, WebAssemblyHostBuilder builder);
|
||||||
public Task ConfigureApplication(IServiceProvider serviceProvider, WebAssemblyHost app);
|
public Task ConfigureApplicationAsync(IServiceProvider serviceProvider, WebAssemblyHost app);
|
||||||
}
|
}
|
||||||
@@ -17,21 +17,14 @@ public class ThemeService
|
|||||||
ApiClient = apiClient;
|
ApiClient = apiClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<PagedData<ThemeResponse>> Get(int page, int pageSize)
|
public async Task<ThemeResponse> GetAsync(int id)
|
||||||
{
|
|
||||||
return await ApiClient.GetJson<PagedData<ThemeResponse>>(
|
|
||||||
$"api/admin/system/customisation/themes?page={page}&pageSize={pageSize}"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<ThemeResponse> Get(int id)
|
|
||||||
{
|
{
|
||||||
return await ApiClient.GetJson<ThemeResponse>(
|
return await ApiClient.GetJson<ThemeResponse>(
|
||||||
$"api/admin/system/customisation/themes/{id}"
|
$"api/admin/system/customisation/themes/{id}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ThemeResponse> Create(CreateThemeRequest request)
|
public async Task<ThemeResponse> CreateAsync(CreateThemeRequest request)
|
||||||
{
|
{
|
||||||
return await ApiClient.PostJson<ThemeResponse>(
|
return await ApiClient.PostJson<ThemeResponse>(
|
||||||
"api/admin/system/customisation/themes",
|
"api/admin/system/customisation/themes",
|
||||||
@@ -39,7 +32,7 @@ public class ThemeService
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ThemeResponse> Update(int id, UpdateThemeRequest request)
|
public async Task<ThemeResponse> UpdateAsync(int id, UpdateThemeRequest request)
|
||||||
{
|
{
|
||||||
return await ApiClient.PatchJson<ThemeResponse>(
|
return await ApiClient.PatchJson<ThemeResponse>(
|
||||||
$"api/admin/system/customisation/themes/{id}",
|
$"api/admin/system/customisation/themes/{id}",
|
||||||
@@ -47,7 +40,7 @@ public class ThemeService
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Delete(int id)
|
public async Task DeleteAsync(int id)
|
||||||
{
|
{
|
||||||
await ApiClient.Delete(
|
await ApiClient.Delete(
|
||||||
$"api/admin/system/customisation/themes/{id}"
|
$"api/admin/system/customisation/themes/{id}"
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ public class WindowService
|
|||||||
JsRuntime = jsRuntime;
|
JsRuntime = jsRuntime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Open(string url, string title, int height, int width)
|
public async Task OpenAsync(string url, string title, int height, int width)
|
||||||
=> await JsRuntime.InvokeVoidAsync("moonlight.window.open", url, title, height, width);
|
=> await JsRuntime.InvokeVoidAsync("moonlight.window.open", url, title, height, width);
|
||||||
|
|
||||||
public async Task Close()
|
public async Task CloseAsync()
|
||||||
=> await JsRuntime.InvokeVoidAsync("moonlight.window.closeCurrent");
|
=> await JsRuntime.InvokeVoidAsync("moonlight.window.closeCurrent");
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ namespace Moonlight.Client.Startup;
|
|||||||
|
|
||||||
public partial class Startup
|
public partial class Startup
|
||||||
{
|
{
|
||||||
private Task RegisterAuthentication()
|
private Task RegisterAuthenticationAsync()
|
||||||
{
|
{
|
||||||
WebAssemblyHostBuilder.Services.AddAuthorizationCore();
|
WebAssemblyHostBuilder.Services.AddAuthorizationCore();
|
||||||
WebAssemblyHostBuilder.Services.AddCascadingAuthenticationState();
|
WebAssemblyHostBuilder.Services.AddCascadingAuthenticationState();
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using MoonCore.Blazor.FlyonUi;
|
using MoonCore.Blazor.FlyonUi;
|
||||||
using MoonCore.Blazor.Services;
|
|
||||||
using MoonCore.Extensions;
|
using MoonCore.Extensions;
|
||||||
using MoonCore.Helpers;
|
using MoonCore.Helpers;
|
||||||
using Moonlight.Client.Services;
|
using Moonlight.Client.Services;
|
||||||
@@ -10,7 +9,7 @@ namespace Moonlight.Client.Startup;
|
|||||||
|
|
||||||
public partial class Startup
|
public partial class Startup
|
||||||
{
|
{
|
||||||
private Task RegisterBase()
|
private Task RegisterBaseAsync()
|
||||||
{
|
{
|
||||||
WebAssemblyHostBuilder.RootComponents.Add<App>("#app");
|
WebAssemblyHostBuilder.RootComponents.Add<App>("#app");
|
||||||
WebAssemblyHostBuilder.RootComponents.Add<HeadOutlet>("head::after");
|
WebAssemblyHostBuilder.RootComponents.Add<HeadOutlet>("head::after");
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace Moonlight.Client.Startup;
|
|||||||
|
|
||||||
public partial class Startup
|
public partial class Startup
|
||||||
{
|
{
|
||||||
private Task SetupLogging()
|
private Task SetupLoggingAsync()
|
||||||
{
|
{
|
||||||
var loggerFactory = new LoggerFactory();
|
var loggerFactory = new LoggerFactory();
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ public partial class Startup
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task RegisterLogging()
|
private Task RegisterLoggingAsync()
|
||||||
{
|
{
|
||||||
WebAssemblyHostBuilder.Logging.ClearProviders();
|
WebAssemblyHostBuilder.Logging.ClearProviders();
|
||||||
WebAssemblyHostBuilder.Logging.AddAnsiConsole();
|
WebAssemblyHostBuilder.Logging.AddAnsiConsole();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace Moonlight.Client.Startup;
|
|||||||
|
|
||||||
public partial class Startup
|
public partial class Startup
|
||||||
{
|
{
|
||||||
private Task PrintVersion()
|
private Task PrintVersionAsync()
|
||||||
{
|
{
|
||||||
// Fancy start console output... yes very fancy :>
|
// Fancy start console output... yes very fancy :>
|
||||||
Console.Write("Running ");
|
Console.Write("Running ");
|
||||||
@@ -27,7 +27,7 @@ public partial class Startup
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LoadConfiguration()
|
private async Task LoadConfigurationAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public partial class Startup
|
|||||||
private IPluginStartup[] PluginStartups;
|
private IPluginStartup[] PluginStartups;
|
||||||
private IServiceProvider PluginLoadServiceProvider;
|
private IServiceProvider PluginLoadServiceProvider;
|
||||||
|
|
||||||
private Task InitializePlugins()
|
private Task InitializePluginsAsync()
|
||||||
{
|
{
|
||||||
// Define minimal service collection
|
// Define minimal service collection
|
||||||
var startupSc = new ServiceCollection();
|
var startupSc = new ServiceCollection();
|
||||||
@@ -38,13 +38,13 @@ public partial class Startup
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HookPluginBuild()
|
private async Task HookPluginBuildAsync()
|
||||||
{
|
{
|
||||||
foreach (var pluginAppStartup in PluginStartups)
|
foreach (var pluginAppStartup in PluginStartups)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await pluginAppStartup.BuildApplication(PluginLoadServiceProvider, WebAssemblyHostBuilder);
|
await pluginAppStartup.BuildApplicationAsync(PluginLoadServiceProvider, WebAssemblyHostBuilder);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -57,13 +57,13 @@ public partial class Startup
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HookPluginConfigure()
|
private async Task HookPluginConfigureAsync()
|
||||||
{
|
{
|
||||||
foreach (var pluginAppStartup in PluginStartups)
|
foreach (var pluginAppStartup in PluginStartups)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await pluginAppStartup.ConfigureApplication(PluginLoadServiceProvider, WebAssemblyHost);
|
await pluginAppStartup.ConfigureApplicationAsync(PluginLoadServiceProvider, WebAssemblyHost);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,33 +16,33 @@ public partial class Startup
|
|||||||
public FrontendConfiguration Configuration { get; private set; }
|
public FrontendConfiguration Configuration { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
public Task Initialize(IPluginStartup[]? plugins = null)
|
public Task InitializeAsync(IPluginStartup[]? plugins = null)
|
||||||
{
|
{
|
||||||
PluginStartups = plugins ?? [];
|
PluginStartups = plugins ?? [];
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task AddMoonlight(WebAssemblyHostBuilder builder)
|
public async Task AddMoonlightAsync(WebAssemblyHostBuilder builder)
|
||||||
{
|
{
|
||||||
WebAssemblyHostBuilder = builder;
|
WebAssemblyHostBuilder = builder;
|
||||||
|
|
||||||
await PrintVersion();
|
await PrintVersionAsync();
|
||||||
|
|
||||||
await SetupLogging();
|
await SetupLoggingAsync();
|
||||||
await LoadConfiguration();
|
await LoadConfigurationAsync();
|
||||||
await InitializePlugins();
|
await InitializePluginsAsync();
|
||||||
|
|
||||||
await RegisterLogging();
|
await RegisterLoggingAsync();
|
||||||
await RegisterBase();
|
await RegisterBaseAsync();
|
||||||
await RegisterAuthentication();
|
await RegisterAuthenticationAsync();
|
||||||
await HookPluginBuild();
|
await HookPluginBuildAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task AddMoonlight(WebAssemblyHost assemblyHost)
|
public async Task AddMoonlightAsync(WebAssemblyHost assemblyHost)
|
||||||
{
|
{
|
||||||
WebAssemblyHost = assemblyHost;
|
WebAssemblyHost = assemblyHost;
|
||||||
|
|
||||||
await HookPluginConfigure();
|
await HookPluginConfigureAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,518 +0,0 @@
|
|||||||
!bg-base-100
|
|
||||||
!border-base-content/40
|
|
||||||
!border-none
|
|
||||||
!flex
|
|
||||||
!font-medium
|
|
||||||
!font-semibold
|
|
||||||
!h-2.5
|
|
||||||
!justify-between
|
|
||||||
!me-1.5
|
|
||||||
!ms-auto
|
|
||||||
!px-2.5
|
|
||||||
!rounded-full
|
|
||||||
!text-sm
|
|
||||||
!w-2.5
|
|
||||||
*:[grid-area:1/1]
|
|
||||||
*:first:rounded-tl-lg
|
|
||||||
*:last:rounded-tr-lg
|
|
||||||
-left-4
|
|
||||||
-ml-4
|
|
||||||
-translate-x-full
|
|
||||||
-translate-y-1/2
|
|
||||||
absolute
|
|
||||||
accordion
|
|
||||||
accordion-bordered
|
|
||||||
accordion-toggle
|
|
||||||
active
|
|
||||||
active-tab:bg-primary
|
|
||||||
active-tab:text-base-content
|
|
||||||
advance-select-menu
|
|
||||||
advance-select-option
|
|
||||||
advance-select-tag
|
|
||||||
advance-select-toggle
|
|
||||||
alert
|
|
||||||
alert-error
|
|
||||||
alert-outline
|
|
||||||
alert-soft
|
|
||||||
align-bottom
|
|
||||||
align-middle
|
|
||||||
animate-bounce
|
|
||||||
animate-ping
|
|
||||||
aria-[current='page']:text-bg-soft-primary
|
|
||||||
avatar
|
|
||||||
avatar-away-bottom
|
|
||||||
avatar-away-top
|
|
||||||
avatar-busy-bottom
|
|
||||||
avatar-busy-top
|
|
||||||
avatar-offline-bottom
|
|
||||||
avatar-offline-top
|
|
||||||
avatar-online-bottom
|
|
||||||
avatar-online-top
|
|
||||||
avatar-placeholder
|
|
||||||
badge
|
|
||||||
badge-error
|
|
||||||
badge-info
|
|
||||||
badge-outline
|
|
||||||
badge-primary
|
|
||||||
badge-soft
|
|
||||||
badge-success
|
|
||||||
bg-background
|
|
||||||
bg-background/60
|
|
||||||
bg-base-100
|
|
||||||
bg-base-150
|
|
||||||
bg-base-200
|
|
||||||
bg-base-200/50
|
|
||||||
bg-base-300
|
|
||||||
bg-base-300/45
|
|
||||||
bg-base-300/50
|
|
||||||
bg-base-300/60
|
|
||||||
bg-error
|
|
||||||
bg-info
|
|
||||||
bg-primary
|
|
||||||
bg-primary/5
|
|
||||||
bg-success
|
|
||||||
bg-transparent
|
|
||||||
bg-warning
|
|
||||||
block
|
|
||||||
blur
|
|
||||||
border
|
|
||||||
border-0
|
|
||||||
border-2
|
|
||||||
border-b
|
|
||||||
border-base-content
|
|
||||||
border-base-content/20
|
|
||||||
border-base-content/25
|
|
||||||
border-base-content/40
|
|
||||||
border-base-content/5
|
|
||||||
border-dashed
|
|
||||||
border-t
|
|
||||||
border-transparent
|
|
||||||
bottom-0
|
|
||||||
bottom-full
|
|
||||||
break-words
|
|
||||||
btn
|
|
||||||
btn-accent
|
|
||||||
btn-active
|
|
||||||
btn-circle
|
|
||||||
btn-disabled
|
|
||||||
btn-error
|
|
||||||
btn-info
|
|
||||||
btn-outline
|
|
||||||
btn-primary
|
|
||||||
btn-secondary
|
|
||||||
btn-sm
|
|
||||||
btn-soft
|
|
||||||
btn-square
|
|
||||||
btn-success
|
|
||||||
btn-text
|
|
||||||
btn-warning
|
|
||||||
card
|
|
||||||
card-alert
|
|
||||||
card-body
|
|
||||||
card-border
|
|
||||||
card-footer
|
|
||||||
card-header
|
|
||||||
card-title
|
|
||||||
carousel
|
|
||||||
carousel-body
|
|
||||||
carousel-next
|
|
||||||
carousel-prev
|
|
||||||
carousel-slide
|
|
||||||
chat
|
|
||||||
chat-avatar
|
|
||||||
chat-bubble
|
|
||||||
chat-footer
|
|
||||||
chat-header
|
|
||||||
chat-receiver
|
|
||||||
chat-sender
|
|
||||||
checkbox
|
|
||||||
checkbox-primary
|
|
||||||
checkbox-xs
|
|
||||||
col-span-1
|
|
||||||
collapse
|
|
||||||
combo-box-selected:block
|
|
||||||
combo-box-selected:dropdown-active
|
|
||||||
complete
|
|
||||||
container
|
|
||||||
contents
|
|
||||||
cursor-default
|
|
||||||
cursor-not-allowed
|
|
||||||
cursor-pointer
|
|
||||||
diff
|
|
||||||
disabled
|
|
||||||
divide-base-150/60
|
|
||||||
divide-y
|
|
||||||
drop-shadow
|
|
||||||
dropdown
|
|
||||||
dropdown-disabled
|
|
||||||
dropdown-item
|
|
||||||
dropdown-menu
|
|
||||||
dropdown-open:opacity-100
|
|
||||||
dropdown-open:rotate-180
|
|
||||||
dropdown-toggle
|
|
||||||
duration-300
|
|
||||||
duration-500
|
|
||||||
ease-in-out
|
|
||||||
ease-linear
|
|
||||||
end-3
|
|
||||||
file-upload-complete:progress-success
|
|
||||||
fill-black
|
|
||||||
filter
|
|
||||||
filter-reset
|
|
||||||
fixed
|
|
||||||
flex
|
|
||||||
flex-1
|
|
||||||
flex-col
|
|
||||||
flex-grow
|
|
||||||
flex-nowrap
|
|
||||||
flex-row
|
|
||||||
flex-shrink-0
|
|
||||||
flex-wrap
|
|
||||||
focus-visible:outline-none
|
|
||||||
focus-within:border-primary
|
|
||||||
focus:border-primary
|
|
||||||
focus:outline-1
|
|
||||||
focus:outline-none
|
|
||||||
focus:outline-primary
|
|
||||||
focus:ring-0
|
|
||||||
font-bold
|
|
||||||
font-inter
|
|
||||||
font-medium
|
|
||||||
font-normal
|
|
||||||
font-semibold
|
|
||||||
gap-0.5
|
|
||||||
gap-1
|
|
||||||
gap-1.5
|
|
||||||
gap-2
|
|
||||||
gap-3
|
|
||||||
gap-4
|
|
||||||
gap-5
|
|
||||||
gap-6
|
|
||||||
gap-x-1
|
|
||||||
gap-x-2
|
|
||||||
gap-x-3
|
|
||||||
gap-y-1
|
|
||||||
gap-y-3
|
|
||||||
grid
|
|
||||||
grid-cols-1
|
|
||||||
grid-flow-col
|
|
||||||
grow
|
|
||||||
grow-0
|
|
||||||
h-12
|
|
||||||
h-2
|
|
||||||
h-32
|
|
||||||
h-64
|
|
||||||
h-8
|
|
||||||
h-auto
|
|
||||||
h-full
|
|
||||||
h-screen
|
|
||||||
helper-text
|
|
||||||
hidden
|
|
||||||
hover:bg-primary/5
|
|
||||||
hover:bg-transparent
|
|
||||||
hover:text-base-content
|
|
||||||
hover:text-base-content/60
|
|
||||||
hover:text-primary
|
|
||||||
image-full
|
|
||||||
inline
|
|
||||||
inline-block
|
|
||||||
inline-flex
|
|
||||||
inline-grid
|
|
||||||
input
|
|
||||||
input-floating
|
|
||||||
input-floating-label
|
|
||||||
input-lg
|
|
||||||
input-md
|
|
||||||
input-sm
|
|
||||||
input-xl
|
|
||||||
inset-0
|
|
||||||
inset-y-0
|
|
||||||
inset-y-2
|
|
||||||
invisible
|
|
||||||
is-invalid
|
|
||||||
is-valid
|
|
||||||
isolate
|
|
||||||
italic
|
|
||||||
items-center
|
|
||||||
items-end
|
|
||||||
items-start
|
|
||||||
join
|
|
||||||
join-item
|
|
||||||
justify-between
|
|
||||||
justify-center
|
|
||||||
justify-end
|
|
||||||
justify-start
|
|
||||||
justify-stretch
|
|
||||||
label-text
|
|
||||||
leading-3
|
|
||||||
leading-3.5
|
|
||||||
leading-6
|
|
||||||
left-0
|
|
||||||
lg:bg-base-100/20
|
|
||||||
lg:flex
|
|
||||||
lg:gap-y-0
|
|
||||||
lg:grid-cols-2
|
|
||||||
lg:hidden
|
|
||||||
lg:justify-end
|
|
||||||
lg:justify-start
|
|
||||||
lg:min-w-0
|
|
||||||
lg:p-10
|
|
||||||
lg:pb-5
|
|
||||||
lg:pl-64
|
|
||||||
lg:pr-3.5
|
|
||||||
lg:pt-5
|
|
||||||
lg:ring-1
|
|
||||||
lg:ring-base-content/10
|
|
||||||
lg:rounded-lg
|
|
||||||
lg:shadow-xs
|
|
||||||
list-disc
|
|
||||||
list-inside
|
|
||||||
loading
|
|
||||||
loading-lg
|
|
||||||
loading-sm
|
|
||||||
loading-spinner
|
|
||||||
loading-xl
|
|
||||||
loading-xs
|
|
||||||
lowercase
|
|
||||||
m-10
|
|
||||||
mask
|
|
||||||
max-h-52
|
|
||||||
max-lg:flex-col
|
|
||||||
max-lg:hidden
|
|
||||||
max-w-7xl
|
|
||||||
max-w-80
|
|
||||||
max-w-full
|
|
||||||
max-w-lg
|
|
||||||
max-w-sm
|
|
||||||
max-w-xl
|
|
||||||
mb-0.5
|
|
||||||
mb-1
|
|
||||||
mb-2
|
|
||||||
mb-3
|
|
||||||
mb-4
|
|
||||||
mb-5
|
|
||||||
md:table-cell
|
|
||||||
md:text-3xl
|
|
||||||
me-1
|
|
||||||
me-1.5
|
|
||||||
me-2
|
|
||||||
me-2.5
|
|
||||||
me-5
|
|
||||||
menu
|
|
||||||
menu-active
|
|
||||||
menu-disabled
|
|
||||||
menu-dropdown
|
|
||||||
menu-dropdown-show
|
|
||||||
menu-focus
|
|
||||||
menu-horizontal
|
|
||||||
menu-title
|
|
||||||
min-h-0
|
|
||||||
min-h-svh
|
|
||||||
min-w-0
|
|
||||||
min-w-28
|
|
||||||
min-w-48
|
|
||||||
min-w-60
|
|
||||||
min-w-[100px]
|
|
||||||
ml-3
|
|
||||||
ml-4
|
|
||||||
modal
|
|
||||||
modal-content
|
|
||||||
modal-dialog
|
|
||||||
modal-middle
|
|
||||||
modal-title
|
|
||||||
mr-4
|
|
||||||
ms-1
|
|
||||||
ms-2
|
|
||||||
ms-3
|
|
||||||
mt-1
|
|
||||||
mt-1.5
|
|
||||||
mt-10
|
|
||||||
mt-12
|
|
||||||
mt-2
|
|
||||||
mt-2.5
|
|
||||||
mt-3
|
|
||||||
mt-4
|
|
||||||
mt-5
|
|
||||||
mt-8
|
|
||||||
mx-1
|
|
||||||
mx-auto
|
|
||||||
my-3
|
|
||||||
my-auto
|
|
||||||
opacity-0
|
|
||||||
opacity-100
|
|
||||||
open
|
|
||||||
origin-top-left
|
|
||||||
outline
|
|
||||||
outline-0
|
|
||||||
overflow-hidden
|
|
||||||
overflow-x-auto
|
|
||||||
overflow-y-auto
|
|
||||||
overlay-open:duration-50
|
|
||||||
overlay-open:opacity-100
|
|
||||||
p-0.5
|
|
||||||
p-1
|
|
||||||
p-2
|
|
||||||
p-3
|
|
||||||
p-4
|
|
||||||
p-5
|
|
||||||
p-6
|
|
||||||
p-8
|
|
||||||
pin-input
|
|
||||||
pin-input-underline
|
|
||||||
placeholder-base-content/60
|
|
||||||
pointer-events-auto
|
|
||||||
pointer-events-none
|
|
||||||
progress
|
|
||||||
progress-bar
|
|
||||||
progress-indeterminate
|
|
||||||
progress-primary
|
|
||||||
pt-0
|
|
||||||
pt-0.5
|
|
||||||
pt-3
|
|
||||||
px-1.5
|
|
||||||
px-2
|
|
||||||
px-2.5
|
|
||||||
px-3
|
|
||||||
px-4
|
|
||||||
px-5
|
|
||||||
py-0.5
|
|
||||||
py-1.5
|
|
||||||
py-2
|
|
||||||
py-2.5
|
|
||||||
py-6
|
|
||||||
radio
|
|
||||||
range
|
|
||||||
relative
|
|
||||||
resize
|
|
||||||
ring-0
|
|
||||||
ring-1
|
|
||||||
ring-white/10
|
|
||||||
rounded-box
|
|
||||||
rounded-field
|
|
||||||
rounded-full
|
|
||||||
rounded-lg
|
|
||||||
rounded-md
|
|
||||||
rounded-t-lg
|
|
||||||
row-active
|
|
||||||
row-hover
|
|
||||||
rtl:!mr-0
|
|
||||||
select
|
|
||||||
select-disabled:opacity-40
|
|
||||||
select-disabled:pointer-events-none
|
|
||||||
select-floating
|
|
||||||
select-floating-label
|
|
||||||
selected
|
|
||||||
selected:select-active
|
|
||||||
shadow-base-300/20
|
|
||||||
shadow-lg
|
|
||||||
shadow-xs
|
|
||||||
shrink-0
|
|
||||||
size-10
|
|
||||||
size-4
|
|
||||||
size-5
|
|
||||||
size-8
|
|
||||||
skeleton
|
|
||||||
skeleton-animated
|
|
||||||
sm:auto-cols-max
|
|
||||||
sm:flex
|
|
||||||
sm:items-center
|
|
||||||
sm:items-end
|
|
||||||
sm:justify-between
|
|
||||||
sm:justify-end
|
|
||||||
sm:max-w-2xl
|
|
||||||
sm:max-w-3xl
|
|
||||||
sm:max-w-4xl
|
|
||||||
sm:max-w-5xl
|
|
||||||
sm:max-w-6xl
|
|
||||||
sm:max-w-7xl
|
|
||||||
sm:max-w-lg
|
|
||||||
sm:max-w-md
|
|
||||||
sm:max-w-xl
|
|
||||||
sm:mb-0
|
|
||||||
sm:mt-5
|
|
||||||
sm:mt-6
|
|
||||||
sm:p-6
|
|
||||||
sm:py-2
|
|
||||||
sm:text-sm/5
|
|
||||||
space-x-1
|
|
||||||
space-y-1
|
|
||||||
space-y-4
|
|
||||||
sr-only
|
|
||||||
static
|
|
||||||
status
|
|
||||||
status-error
|
|
||||||
sticky
|
|
||||||
switch
|
|
||||||
tab
|
|
||||||
tab-active
|
|
||||||
table
|
|
||||||
table-pin-cols
|
|
||||||
table-pin-rows
|
|
||||||
tabs
|
|
||||||
tabs-bordered
|
|
||||||
tabs-lg
|
|
||||||
tabs-lifted
|
|
||||||
tabs-md
|
|
||||||
tabs-sm
|
|
||||||
tabs-xl
|
|
||||||
tabs-xs
|
|
||||||
text-2xl
|
|
||||||
text-4xl
|
|
||||||
text-accent
|
|
||||||
text-base
|
|
||||||
text-base-content
|
|
||||||
text-base-content/40
|
|
||||||
text-base-content/50
|
|
||||||
text-base-content/60
|
|
||||||
text-base-content/70
|
|
||||||
text-base-content/80
|
|
||||||
text-base/6
|
|
||||||
text-center
|
|
||||||
text-error
|
|
||||||
text-error-content
|
|
||||||
text-gray-400
|
|
||||||
text-info
|
|
||||||
text-info-content
|
|
||||||
text-left
|
|
||||||
text-lg
|
|
||||||
text-primary
|
|
||||||
text-primary-content
|
|
||||||
text-sm
|
|
||||||
text-sm/5
|
|
||||||
text-success
|
|
||||||
text-success-content
|
|
||||||
text-warning
|
|
||||||
text-warning-content
|
|
||||||
text-xl
|
|
||||||
text-xs
|
|
||||||
text-xs/5
|
|
||||||
textarea
|
|
||||||
textarea-floating
|
|
||||||
textarea-floating-label
|
|
||||||
theme-controller
|
|
||||||
tooltip
|
|
||||||
tooltip-content
|
|
||||||
top-0
|
|
||||||
top-1/2
|
|
||||||
top-full
|
|
||||||
transform
|
|
||||||
transition
|
|
||||||
transition-all
|
|
||||||
transition-opacity
|
|
||||||
translate-x-0
|
|
||||||
truncate
|
|
||||||
underline
|
|
||||||
uppercase
|
|
||||||
validate
|
|
||||||
w-0
|
|
||||||
w-0.5
|
|
||||||
w-12
|
|
||||||
w-4
|
|
||||||
w-56
|
|
||||||
w-64
|
|
||||||
w-fit
|
|
||||||
w-full
|
|
||||||
whitespace-nowrap
|
|
||||||
z-10
|
|
||||||
z-40
|
|
||||||
z-50
|
|
||||||
@@ -21,6 +21,8 @@
|
|||||||
-ml-4
|
-ml-4
|
||||||
-translate-x-full
|
-translate-x-full
|
||||||
-translate-y-1/2
|
-translate-y-1/2
|
||||||
|
[animation-duration:0.8s]
|
||||||
|
[animation-timing-function:ease]
|
||||||
absolute
|
absolute
|
||||||
accordion
|
accordion
|
||||||
accordion-bordered
|
accordion-bordered
|
||||||
@@ -42,8 +44,10 @@ align-bottom
|
|||||||
align-middle
|
align-middle
|
||||||
animate-bounce
|
animate-bounce
|
||||||
animate-ping
|
animate-ping
|
||||||
|
animate-spin
|
||||||
aria-[current='page']:text-bg-soft-primary
|
aria-[current='page']:text-bg-soft-primary
|
||||||
avatar
|
avatar
|
||||||
|
avatar-placeholder
|
||||||
badge
|
badge
|
||||||
badge-error
|
badge-error
|
||||||
badge-info
|
badge-info
|
||||||
@@ -73,18 +77,33 @@ block
|
|||||||
blur
|
blur
|
||||||
border
|
border
|
||||||
border-0
|
border-0
|
||||||
|
border-1
|
||||||
border-2
|
border-2
|
||||||
|
border-3
|
||||||
border-b
|
border-b
|
||||||
|
border-b-2
|
||||||
|
border-b-base-content/20
|
||||||
|
border-b-primary
|
||||||
border-base-content
|
border-base-content
|
||||||
border-base-content/20
|
border-base-content/20
|
||||||
border-base-content/25
|
border-base-content/25
|
||||||
border-base-content/40
|
border-base-content/40
|
||||||
border-base-content/5
|
border-base-content/5
|
||||||
border-dashed
|
border-dashed
|
||||||
|
border-dotted
|
||||||
|
border-error/30
|
||||||
|
border-info/30
|
||||||
|
border-l-transparent
|
||||||
|
border-r-transparent
|
||||||
|
border-solid
|
||||||
|
border-success/30
|
||||||
border-t
|
border-t
|
||||||
|
border-t-transparent
|
||||||
border-transparent
|
border-transparent
|
||||||
|
border-warning/30
|
||||||
bottom-0
|
bottom-0
|
||||||
bottom-full
|
bottom-full
|
||||||
|
breadcrumbs
|
||||||
break-words
|
break-words
|
||||||
btn
|
btn
|
||||||
btn-accent
|
btn-accent
|
||||||
@@ -93,7 +112,6 @@ btn-circle
|
|||||||
btn-disabled
|
btn-disabled
|
||||||
btn-error
|
btn-error
|
||||||
btn-info
|
btn-info
|
||||||
btn-outline
|
|
||||||
btn-primary
|
btn-primary
|
||||||
btn-secondary
|
btn-secondary
|
||||||
btn-sm
|
btn-sm
|
||||||
@@ -153,6 +171,7 @@ duration-500
|
|||||||
ease-in-out
|
ease-in-out
|
||||||
ease-linear
|
ease-linear
|
||||||
end-3
|
end-3
|
||||||
|
error-message
|
||||||
file-upload-complete:progress-success
|
file-upload-complete:progress-success
|
||||||
fill-base-content
|
fill-base-content
|
||||||
fill-black
|
fill-black
|
||||||
@@ -180,6 +199,8 @@ font-inter
|
|||||||
font-medium
|
font-medium
|
||||||
font-normal
|
font-normal
|
||||||
font-semibold
|
font-semibold
|
||||||
|
footer
|
||||||
|
footer-center
|
||||||
gap-0.5
|
gap-0.5
|
||||||
gap-1
|
gap-1
|
||||||
gap-1.5
|
gap-1.5
|
||||||
@@ -200,7 +221,7 @@ grid-cols-4
|
|||||||
grid-flow-col
|
grid-flow-col
|
||||||
grow
|
grow
|
||||||
grow-0
|
grow-0
|
||||||
h-12
|
h-10
|
||||||
h-2
|
h-2
|
||||||
h-3
|
h-3
|
||||||
h-32
|
h-32
|
||||||
@@ -213,10 +234,13 @@ helper-text
|
|||||||
hidden
|
hidden
|
||||||
hover:bg-primary/5
|
hover:bg-primary/5
|
||||||
hover:bg-transparent
|
hover:bg-transparent
|
||||||
|
hover:cursor-pointer
|
||||||
hover:text-base-content
|
hover:text-base-content
|
||||||
hover:text-base-content/60
|
hover:text-base-content/60
|
||||||
hover:text-primary
|
hover:text-primary
|
||||||
image-full
|
image-full
|
||||||
|
indicator
|
||||||
|
indicator-item
|
||||||
inline
|
inline
|
||||||
inline-block
|
inline-block
|
||||||
inline-flex
|
inline-flex
|
||||||
@@ -245,11 +269,9 @@ justify-between
|
|||||||
justify-center
|
justify-center
|
||||||
justify-end
|
justify-end
|
||||||
justify-start
|
justify-start
|
||||||
justify-stretch
|
|
||||||
label-text
|
label-text
|
||||||
leading-3
|
leading-3
|
||||||
leading-3.5
|
leading-3.5
|
||||||
leading-6
|
|
||||||
leading-none
|
leading-none
|
||||||
left-0
|
left-0
|
||||||
lg:bg-base-100/20
|
lg:bg-base-100/20
|
||||||
@@ -276,7 +298,6 @@ list-disc
|
|||||||
list-inside
|
list-inside
|
||||||
list-none
|
list-none
|
||||||
loading
|
loading
|
||||||
loading-lg
|
|
||||||
loading-sm
|
loading-sm
|
||||||
loading-spinner
|
loading-spinner
|
||||||
loading-xl
|
loading-xl
|
||||||
@@ -287,7 +308,11 @@ mask
|
|||||||
max-h-52
|
max-h-52
|
||||||
max-lg:flex-col
|
max-lg:flex-col
|
||||||
max-lg:hidden
|
max-lg:hidden
|
||||||
|
max-md:flex-wrap
|
||||||
|
max-md:justify-center
|
||||||
|
max-sm:hidden
|
||||||
max-w-7xl
|
max-w-7xl
|
||||||
|
max-w-8
|
||||||
max-w-80
|
max-w-80
|
||||||
max-w-full
|
max-w-full
|
||||||
max-w-lg
|
max-w-lg
|
||||||
@@ -323,6 +348,7 @@ min-w-28
|
|||||||
min-w-48
|
min-w-48
|
||||||
min-w-60
|
min-w-60
|
||||||
min-w-[100px]
|
min-w-[100px]
|
||||||
|
min-w-full
|
||||||
min-w-sm
|
min-w-sm
|
||||||
ml-3
|
ml-3
|
||||||
ml-4
|
ml-4
|
||||||
@@ -330,10 +356,10 @@ modal
|
|||||||
modal-content
|
modal-content
|
||||||
modal-dialog
|
modal-dialog
|
||||||
modal-middle
|
modal-middle
|
||||||
modal-title
|
|
||||||
mr-4
|
mr-4
|
||||||
ms-0.5
|
ms-0.5
|
||||||
ms-1
|
ms-1
|
||||||
|
ms-1.5
|
||||||
ms-2
|
ms-2
|
||||||
ms-3
|
ms-3
|
||||||
ms-auto
|
ms-auto
|
||||||
@@ -351,10 +377,12 @@ mt-8
|
|||||||
mx-1
|
mx-1
|
||||||
mx-auto
|
mx-auto
|
||||||
my-3
|
my-3
|
||||||
|
my-5
|
||||||
my-auto
|
my-auto
|
||||||
object-cover
|
object-cover
|
||||||
opacity-0
|
opacity-0
|
||||||
opacity-100
|
opacity-100
|
||||||
|
opacity-75
|
||||||
open
|
open
|
||||||
origin-top-left
|
origin-top-left
|
||||||
outline
|
outline
|
||||||
@@ -368,11 +396,13 @@ p-0.5
|
|||||||
p-1
|
p-1
|
||||||
p-1.5
|
p-1.5
|
||||||
p-2
|
p-2
|
||||||
|
p-2.5
|
||||||
p-3
|
p-3
|
||||||
p-4
|
p-4
|
||||||
p-5
|
p-5
|
||||||
p-6
|
p-6
|
||||||
p-8
|
p-8
|
||||||
|
pb-1
|
||||||
pin-input
|
pin-input
|
||||||
placeholder-base-content/60
|
placeholder-base-content/60
|
||||||
pointer-events-auto
|
pointer-events-auto
|
||||||
@@ -383,6 +413,7 @@ progress-indeterminate
|
|||||||
progress-primary
|
progress-primary
|
||||||
pt-0
|
pt-0
|
||||||
pt-0.5
|
pt-0.5
|
||||||
|
pt-2
|
||||||
pt-3
|
pt-3
|
||||||
px-1.5
|
px-1.5
|
||||||
px-2
|
px-2
|
||||||
@@ -418,6 +449,7 @@ select-disabled:opacity-40
|
|||||||
select-disabled:pointer-events-none
|
select-disabled:pointer-events-none
|
||||||
select-floating
|
select-floating
|
||||||
select-floating-label
|
select-floating-label
|
||||||
|
select-sm
|
||||||
selected
|
selected
|
||||||
selected:select-active
|
selected:select-active
|
||||||
shadow-base-300/20
|
shadow-base-300/20
|
||||||
@@ -426,6 +458,7 @@ shadow-md
|
|||||||
shadow-xs
|
shadow-xs
|
||||||
shrink-0
|
shrink-0
|
||||||
size-10
|
size-10
|
||||||
|
size-12
|
||||||
size-4
|
size-4
|
||||||
size-5
|
size-5
|
||||||
size-8
|
size-8
|
||||||
@@ -448,18 +481,20 @@ sm:max-w-md
|
|||||||
sm:max-w-xl
|
sm:max-w-xl
|
||||||
sm:mb-0
|
sm:mb-0
|
||||||
sm:mt-5
|
sm:mt-5
|
||||||
sm:mt-6
|
|
||||||
sm:p-6
|
sm:p-6
|
||||||
sm:py-2
|
sm:py-2
|
||||||
sm:text-sm/5
|
sm:text-sm/5
|
||||||
space-x-1
|
space-x-1
|
||||||
|
space-x-2.5
|
||||||
space-y-1
|
space-y-1
|
||||||
space-y-4
|
space-y-4
|
||||||
sr-only
|
sr-only
|
||||||
|
stack
|
||||||
static
|
static
|
||||||
status
|
status
|
||||||
status-error
|
status-error
|
||||||
sticky
|
sticky
|
||||||
|
success-message
|
||||||
switch
|
switch
|
||||||
tab
|
tab
|
||||||
tab-active
|
tab-active
|
||||||
@@ -495,6 +530,7 @@ text-left
|
|||||||
text-lg
|
text-lg
|
||||||
text-primary
|
text-primary
|
||||||
text-primary-content
|
text-primary-content
|
||||||
|
text-right
|
||||||
text-sm
|
text-sm
|
||||||
text-sm/5
|
text-sm/5
|
||||||
text-success
|
text-success
|
||||||
@@ -525,6 +561,7 @@ validate
|
|||||||
w-0
|
w-0
|
||||||
w-0.5
|
w-0.5
|
||||||
w-12
|
w-12
|
||||||
|
w-13
|
||||||
w-4
|
w-4
|
||||||
w-56
|
w-56
|
||||||
w-64
|
w-64
|
||||||
|
|||||||
@@ -9,7 +9,9 @@
|
|||||||
!me-1.5
|
!me-1.5
|
||||||
!ms-auto
|
!ms-auto
|
||||||
!px-2.5
|
!px-2.5
|
||||||
|
!py-0.5
|
||||||
!rounded-full
|
!rounded-full
|
||||||
|
!rounded-xs
|
||||||
!text-sm
|
!text-sm
|
||||||
!w-2.5
|
!w-2.5
|
||||||
*:[grid-area:1/1]
|
*:[grid-area:1/1]
|
||||||
@@ -19,13 +21,17 @@
|
|||||||
-ml-4
|
-ml-4
|
||||||
-translate-x-full
|
-translate-x-full
|
||||||
-translate-y-1/2
|
-translate-y-1/2
|
||||||
|
[animation-duration:0.8s]
|
||||||
|
[animation-timing-function:ease]
|
||||||
absolute
|
absolute
|
||||||
accordion
|
accordion
|
||||||
accordion-bordered
|
accordion-bordered
|
||||||
accordion-toggle
|
accordion-toggle
|
||||||
active
|
active
|
||||||
active-tab:bg-primary
|
active-tab:bg-primary
|
||||||
|
active-tab:hover:text-primary-content
|
||||||
active-tab:text-base-content
|
active-tab:text-base-content
|
||||||
|
active-tab:text-primary-content
|
||||||
advance-select-menu
|
advance-select-menu
|
||||||
advance-select-option
|
advance-select-option
|
||||||
advance-select-tag
|
advance-select-tag
|
||||||
@@ -39,6 +45,7 @@ align-bottom
|
|||||||
align-middle
|
align-middle
|
||||||
animate-bounce
|
animate-bounce
|
||||||
animate-ping
|
animate-ping
|
||||||
|
animate-spin
|
||||||
aria-[current='page']:text-bg-soft-primary
|
aria-[current='page']:text-bg-soft-primary
|
||||||
avatar
|
avatar
|
||||||
avatar-away-bottom
|
avatar-away-bottom
|
||||||
@@ -62,6 +69,7 @@ bg-background/60
|
|||||||
bg-base-100
|
bg-base-100
|
||||||
bg-base-150
|
bg-base-150
|
||||||
bg-base-200
|
bg-base-200
|
||||||
|
bg-base-200!
|
||||||
bg-base-200/50
|
bg-base-200/50
|
||||||
bg-base-300
|
bg-base-300
|
||||||
bg-base-300/45
|
bg-base-300/45
|
||||||
@@ -85,9 +93,14 @@ block
|
|||||||
blur
|
blur
|
||||||
border
|
border
|
||||||
border-0
|
border-0
|
||||||
|
border-1
|
||||||
border-2
|
border-2
|
||||||
|
border-3
|
||||||
border-accent
|
border-accent
|
||||||
border-b
|
border-b
|
||||||
|
border-b-2
|
||||||
|
border-b-base-content/20
|
||||||
|
border-b-primary
|
||||||
border-base-content
|
border-base-content
|
||||||
border-base-content/20
|
border-base-content/20
|
||||||
border-base-content/25
|
border-base-content/25
|
||||||
@@ -96,15 +109,26 @@ border-base-content/5
|
|||||||
border-base-content/60
|
border-base-content/60
|
||||||
border-base-content/70
|
border-base-content/70
|
||||||
border-dashed
|
border-dashed
|
||||||
|
border-dotted
|
||||||
border-e-2
|
border-e-2
|
||||||
|
border-error/30
|
||||||
|
border-info/30
|
||||||
border-l-4
|
border-l-4
|
||||||
|
border-l-transparent
|
||||||
border-primary
|
border-primary
|
||||||
|
border-r-transparent
|
||||||
|
border-solid
|
||||||
border-success
|
border-success
|
||||||
|
border-success/30
|
||||||
border-t
|
border-t
|
||||||
border-t-2
|
border-t-2
|
||||||
|
border-t-transparent
|
||||||
border-transparent
|
border-transparent
|
||||||
|
border-warning/30
|
||||||
bottom-0
|
bottom-0
|
||||||
bottom-full
|
bottom-full
|
||||||
|
breadcrumbs
|
||||||
|
breadcrumbs-separator
|
||||||
break-words
|
break-words
|
||||||
btn
|
btn
|
||||||
btn-accent
|
btn-accent
|
||||||
@@ -164,8 +188,10 @@ diff
|
|||||||
disabled
|
disabled
|
||||||
divide-base-150/60
|
divide-base-150/60
|
||||||
divide-y
|
divide-y
|
||||||
|
divider
|
||||||
drop-shadow
|
drop-shadow
|
||||||
dropdown
|
dropdown
|
||||||
|
dropdown-active
|
||||||
dropdown-disabled
|
dropdown-disabled
|
||||||
dropdown-item
|
dropdown-item
|
||||||
dropdown-menu
|
dropdown-menu
|
||||||
@@ -179,7 +205,9 @@ ease-linear
|
|||||||
end-3
|
end-3
|
||||||
error-message
|
error-message
|
||||||
file-upload-complete:progress-success
|
file-upload-complete:progress-success
|
||||||
|
fill-base-content
|
||||||
fill-black
|
fill-black
|
||||||
|
fill-gray-200
|
||||||
filter
|
filter
|
||||||
filter-reset
|
filter-reset
|
||||||
fixed
|
fixed
|
||||||
@@ -210,6 +238,8 @@ font-inter
|
|||||||
font-medium
|
font-medium
|
||||||
font-normal
|
font-normal
|
||||||
font-semibold
|
font-semibold
|
||||||
|
footer
|
||||||
|
footer-center
|
||||||
from-violet-400
|
from-violet-400
|
||||||
gap-0.5
|
gap-0.5
|
||||||
gap-1
|
gap-1
|
||||||
@@ -226,6 +256,7 @@ gap-x-6
|
|||||||
gap-y-1
|
gap-y-1
|
||||||
gap-y-1.5
|
gap-y-1.5
|
||||||
gap-y-2
|
gap-y-2
|
||||||
|
gap-y-2.5
|
||||||
gap-y-3
|
gap-y-3
|
||||||
gap-y-8
|
gap-y-8
|
||||||
grid
|
grid
|
||||||
@@ -233,10 +264,12 @@ grid-cols-1
|
|||||||
grid-cols-12
|
grid-cols-12
|
||||||
grid-cols-2
|
grid-cols-2
|
||||||
grid-cols-3
|
grid-cols-3
|
||||||
|
grid-cols-4
|
||||||
grid-flow-col
|
grid-flow-col
|
||||||
grow
|
grow
|
||||||
grow-0
|
grow-0
|
||||||
h-0
|
h-0
|
||||||
|
h-10
|
||||||
h-12
|
h-12
|
||||||
h-14
|
h-14
|
||||||
h-2
|
h-2
|
||||||
@@ -253,11 +286,14 @@ hidden
|
|||||||
hover:bg-indigo-500
|
hover:bg-indigo-500
|
||||||
hover:bg-primary/5
|
hover:bg-primary/5
|
||||||
hover:bg-transparent
|
hover:bg-transparent
|
||||||
|
hover:cursor-pointer
|
||||||
hover:text-base-content
|
hover:text-base-content
|
||||||
hover:text-base-content/60
|
hover:text-base-content/60
|
||||||
hover:text-indigo-500
|
hover:text-indigo-500
|
||||||
hover:text-primary
|
hover:text-primary
|
||||||
image-full
|
image-full
|
||||||
|
indicator
|
||||||
|
indicator-item
|
||||||
inline
|
inline
|
||||||
inline-block
|
inline-block
|
||||||
inline-flex
|
inline-flex
|
||||||
@@ -294,6 +330,7 @@ leading-3.5
|
|||||||
leading-6
|
leading-6
|
||||||
leading-9
|
leading-9
|
||||||
leading-[1.1]
|
leading-[1.1]
|
||||||
|
leading-none
|
||||||
left-0
|
left-0
|
||||||
lg:bg-base-100/20
|
lg:bg-base-100/20
|
||||||
lg:flex
|
lg:flex
|
||||||
@@ -320,6 +357,7 @@ link-hover
|
|||||||
link-primary
|
link-primary
|
||||||
list-disc
|
list-disc
|
||||||
list-inside
|
list-inside
|
||||||
|
list-none
|
||||||
loading
|
loading
|
||||||
loading-lg
|
loading-lg
|
||||||
loading-sm
|
loading-sm
|
||||||
@@ -332,7 +370,11 @@ mask
|
|||||||
max-h-52
|
max-h-52
|
||||||
max-lg:flex-col
|
max-lg:flex-col
|
||||||
max-lg:hidden
|
max-lg:hidden
|
||||||
|
max-md:flex-wrap
|
||||||
|
max-md:justify-center
|
||||||
|
max-sm:hidden
|
||||||
max-w-7xl
|
max-w-7xl
|
||||||
|
max-w-8
|
||||||
max-w-80
|
max-w-80
|
||||||
max-w-full
|
max-w-full
|
||||||
max-w-lg
|
max-w-lg
|
||||||
@@ -376,6 +418,7 @@ min-w-28
|
|||||||
min-w-48
|
min-w-48
|
||||||
min-w-60
|
min-w-60
|
||||||
min-w-[100px]
|
min-w-[100px]
|
||||||
|
min-w-full
|
||||||
min-w-sm
|
min-w-sm
|
||||||
mix-blend-exclusion
|
mix-blend-exclusion
|
||||||
ml-3
|
ml-3
|
||||||
@@ -387,10 +430,13 @@ modal-middle
|
|||||||
modal-title
|
modal-title
|
||||||
mr-2
|
mr-2
|
||||||
mr-4
|
mr-4
|
||||||
|
ms-0.5
|
||||||
ms-1
|
ms-1
|
||||||
|
ms-1.5
|
||||||
ms-2
|
ms-2
|
||||||
ms-2.5
|
ms-2.5
|
||||||
ms-3
|
ms-3
|
||||||
|
ms-auto
|
||||||
mt-1
|
mt-1
|
||||||
mt-1.5
|
mt-1.5
|
||||||
mt-10
|
mt-10
|
||||||
@@ -398,6 +444,7 @@ mt-12
|
|||||||
mt-2
|
mt-2
|
||||||
mt-2.5
|
mt-2.5
|
||||||
mt-3
|
mt-3
|
||||||
|
mt-3.5
|
||||||
mt-4
|
mt-4
|
||||||
mt-5
|
mt-5
|
||||||
mt-6
|
mt-6
|
||||||
@@ -407,9 +454,12 @@ mx-auto
|
|||||||
my-2.5
|
my-2.5
|
||||||
my-3
|
my-3
|
||||||
my-5
|
my-5
|
||||||
|
my-8
|
||||||
my-auto
|
my-auto
|
||||||
|
object-cover
|
||||||
opacity-0
|
opacity-0
|
||||||
opacity-100
|
opacity-100
|
||||||
|
opacity-75
|
||||||
open
|
open
|
||||||
origin-top-left
|
origin-top-left
|
||||||
outline
|
outline
|
||||||
@@ -430,6 +480,7 @@ p-4
|
|||||||
p-5
|
p-5
|
||||||
p-6
|
p-6
|
||||||
p-8
|
p-8
|
||||||
|
pb-1
|
||||||
pe-1.5
|
pe-1.5
|
||||||
pin-input
|
pin-input
|
||||||
pin-input-underline
|
pin-input-underline
|
||||||
@@ -444,6 +495,7 @@ progress-primary
|
|||||||
pt-0
|
pt-0
|
||||||
pt-0.5
|
pt-0.5
|
||||||
pt-1.5
|
pt-1.5
|
||||||
|
pt-2
|
||||||
pt-3
|
pt-3
|
||||||
px-1.5
|
px-1.5
|
||||||
px-2
|
px-2
|
||||||
@@ -459,10 +511,12 @@ py-12
|
|||||||
py-2
|
py-2
|
||||||
py-2.5
|
py-2.5
|
||||||
py-6
|
py-6
|
||||||
|
radial-progress
|
||||||
radio
|
radio
|
||||||
range
|
range
|
||||||
relative
|
relative
|
||||||
resize
|
resize
|
||||||
|
ring
|
||||||
ring-0
|
ring-0
|
||||||
ring-1
|
ring-1
|
||||||
ring-gray-700
|
ring-gray-700
|
||||||
@@ -543,10 +597,12 @@ sm:text-sm/5
|
|||||||
sm:w-1/2
|
sm:w-1/2
|
||||||
sm:w-full
|
sm:w-full
|
||||||
space-x-1
|
space-x-1
|
||||||
|
space-x-2.5
|
||||||
space-y-1
|
space-y-1
|
||||||
space-y-4
|
space-y-4
|
||||||
space-y-6
|
space-y-6
|
||||||
sr-only
|
sr-only
|
||||||
|
stack
|
||||||
stat
|
stat
|
||||||
stat-actions
|
stat-actions
|
||||||
stat-desc
|
stat-desc
|
||||||
@@ -602,6 +658,7 @@ text-left
|
|||||||
text-lg
|
text-lg
|
||||||
text-primary
|
text-primary
|
||||||
text-primary-content
|
text-primary-content
|
||||||
|
text-right
|
||||||
text-slate-100
|
text-slate-100
|
||||||
text-sm
|
text-sm
|
||||||
text-sm/5
|
text-sm/5
|
||||||
@@ -645,6 +702,7 @@ via-sky-400
|
|||||||
w-0
|
w-0
|
||||||
w-0.5
|
w-0.5
|
||||||
w-12
|
w-12
|
||||||
|
w-13
|
||||||
w-32
|
w-32
|
||||||
w-4
|
w-4
|
||||||
w-56
|
w-56
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<label for="@Id" class="btn btn-square border-0 ring-0 outline-0" style="background-color: @Value">
|
<label for="@Id" class="btn btn-square border-0 ring-0 outline-0" style="background-color: @Value">
|
||||||
<i class="text-lg text-base-content @Icon"></i>
|
<i class="text-lg text-base-content @Icon"></i>
|
||||||
</label>
|
</label>
|
||||||
<input value="@Value" @oninput="Update" id="@Id" type="color" class="h-0 w-0 opacity-0"/>
|
<input value="@Value" @oninput="UpdateAsync" id="@Id" type="color" class="h-0 w-0 opacity-0"/>
|
||||||
|
|
||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
Id = $"color-selector-{GetHashCode()}";
|
Id = $"color-selector-{GetHashCode()}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Update(ChangeEventArgs args)
|
private async Task UpdateAsync(ChangeEventArgs args)
|
||||||
{
|
{
|
||||||
Value = args.Value?.ToString() ?? "#FFFFFF";
|
Value = args.Value?.ToString() ?? "#FFFFFF";
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
for all SignalR Hubs to be synced.
|
for all SignalR Hubs to be synced.
|
||||||
</p>
|
</p>
|
||||||
<div class="mt-5">
|
<div class="mt-5">
|
||||||
<LazyLoader Load="Load">
|
<LazyLoader Load="LoadAsync">
|
||||||
<WButton OnClick="OnClick" CssClasses="btn btn-primary">
|
<WButton OnClick="OnClick" CssClasses="btn btn-primary">
|
||||||
Send broadcast
|
Send broadcast
|
||||||
</WButton>
|
</WButton>
|
||||||
@@ -30,9 +30,9 @@
|
|||||||
{
|
{
|
||||||
private HubConnection? Connection;
|
private HubConnection? Connection;
|
||||||
|
|
||||||
private async Task Load(LazyLoader lazyLoader)
|
private async Task LoadAsync(LazyLoader lazyLoader)
|
||||||
{
|
{
|
||||||
await lazyLoader.UpdateText("Connecting to SignalR endpoint");
|
await lazyLoader.UpdateTextAsync("Connecting to SignalR endpoint");
|
||||||
|
|
||||||
Connection = new HubConnectionBuilder()
|
Connection = new HubConnectionBuilder()
|
||||||
.WithUrl(Navigation.ToAbsoluteUri("/api/admin/system/diagnose/ws"))
|
.WithUrl(Navigation.ToAbsoluteUri("/api/admin/system/diagnose/ws"))
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
|
|
||||||
Connection.On(
|
Connection.On(
|
||||||
"Pong",
|
"Pong",
|
||||||
async () => await ToastService.Success("Received broadcast")
|
async () => await ToastService.SuccessAsync("Received broadcast")
|
||||||
);
|
);
|
||||||
|
|
||||||
await Connection.StartAsync();
|
await Connection.StartAsync();
|
||||||
|
|||||||
@@ -137,7 +137,7 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<input @onclick="_ => UpdateRadiusBox(possibleValue)" type="radio" name="radius-box" class="radio hidden"/>
|
<input @onclick="_ => UpdateRadiusBoxAsync(possibleValue)" type="radio" name="radius-box" class="radio hidden"/>
|
||||||
}
|
}
|
||||||
<span class="label-text w-full">
|
<span class="label-text w-full">
|
||||||
<div class="pe-1.5 pt-1.5" aria-hidden="true">
|
<div class="pe-1.5 pt-1.5" aria-hidden="true">
|
||||||
@@ -164,7 +164,7 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<input @onclick="_ => UpdateRadiusField(possibleValue)" type="radio" name="radius-field" class="radio hidden"/>
|
<input @onclick="_ => UpdateRadiusFieldAsync(possibleValue)" type="radio" name="radius-field" class="radio hidden"/>
|
||||||
}
|
}
|
||||||
<span class="label-text w-full">
|
<span class="label-text w-full">
|
||||||
<div class="pe-1.5 pt-1.5" aria-hidden="true">
|
<div class="pe-1.5 pt-1.5" aria-hidden="true">
|
||||||
@@ -191,7 +191,7 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<input @onclick="_ => UpdateRadiusSelector(possibleValue)" type="radio" name="radius-selector" class="radio hidden"/>
|
<input @onclick="_ => UpdateRadiusSelectorAsync(possibleValue)" type="radio" name="radius-selector" class="radio hidden"/>
|
||||||
}
|
}
|
||||||
<span class="label-text w-full">
|
<span class="label-text w-full">
|
||||||
<div class="pe-1.5 pt-1.5" aria-hidden="true">
|
<div class="pe-1.5 pt-1.5" aria-hidden="true">
|
||||||
@@ -302,19 +302,19 @@
|
|||||||
set => Theme.Noise = value ? 1 : 0;
|
set => Theme.Noise = value ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateRadiusBox(float value)
|
private async Task UpdateRadiusBoxAsync(float value)
|
||||||
{
|
{
|
||||||
Theme.RadiusBox = value;
|
Theme.RadiusBox = value;
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateRadiusField(float value)
|
private async Task UpdateRadiusFieldAsync(float value)
|
||||||
{
|
{
|
||||||
Theme.RadiusField = value;
|
Theme.RadiusField = value;
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateRadiusSelector(float value)
|
private async Task UpdateRadiusSelectorAsync(float value)
|
||||||
{
|
{
|
||||||
Theme.RadiusSelector = value;
|
Theme.RadiusSelector = value;
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
public event Func<Task> OnStateChanged;
|
public event Func<Task> OnStateChanged;
|
||||||
public bool ShowMobileNavigation { get; private set; } = false;
|
public bool ShowMobileNavigation { get; private set; } = false;
|
||||||
|
|
||||||
public async Task ToggleMobileNavigation()
|
public async Task ToggleMobileNavigationAsync()
|
||||||
{
|
{
|
||||||
ShowMobileNavigation = !ShowMobileNavigation;
|
ShowMobileNavigation = !ShowMobileNavigation;
|
||||||
await OnStateChanged();
|
await OnStateChanged();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<header class="flex items-center px-4 lg:hidden border-b border-base-content/5">
|
<header class="flex items-center px-4 lg:hidden border-b border-base-content/5">
|
||||||
<div class="py-2.5">
|
<div class="py-2.5">
|
||||||
<span class="relative">
|
<span class="relative">
|
||||||
<button @onclick="Layout.ToggleMobileNavigation" aria-label="Open navigation"
|
<button @onclick="Layout.ToggleMobileNavigationAsync" aria-label="Open navigation"
|
||||||
class="relative flex min-w-0 items-center gap-3 rounded-lg p-2 text-left text-base/6 sm:text-sm/5 text-base-content"
|
class="relative flex min-w-0 items-center gap-3 rounded-lg p-2 text-left text-base/6 sm:text-sm/5 text-base-content"
|
||||||
type="button">
|
type="button">
|
||||||
<i class="icon-menu text-xl"></i>
|
<i class="icon-menu text-xl"></i>
|
||||||
|
|||||||
@@ -95,7 +95,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a href="#" @onclick:preventDefault @onclick="Logout" class="flex items-center">
|
<a href="#" @onclick:preventDefault @onclick="LogoutAsync" class="flex items-center">
|
||||||
<i class="icon-log-out text-lg"></i>
|
<i class="icon-log-out text-lg"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -121,7 +121,7 @@
|
|||||||
<div class="truncate">Moonlight v2.1</div>
|
<div class="truncate">Moonlight v2.1</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button @onclick="Layout.ToggleMobileNavigation" aria-label="Close navigation" type="button"
|
<button @onclick="Layout.ToggleMobileNavigationAsync" aria-label="Close navigation" type="button"
|
||||||
class="relative flex min-w-0 items-center gap-3 rounded-lg p-2 text-left text-base/6 text-base-content">
|
class="relative flex min-w-0 items-center gap-3 rounded-lg p-2 text-left text-base/6 text-base-content">
|
||||||
<i class="icon-x text-lg"></i>
|
<i class="icon-x text-lg"></i>
|
||||||
</button>
|
</button>
|
||||||
@@ -180,7 +180,7 @@
|
|||||||
<div class="flex flex-col gap-0.5">
|
<div class="flex flex-col gap-0.5">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<a class="flex w-full items-center gap-3 rounded-lg px-2 py-2.5 text-left text-base/6 sm:py-2 sm:text-sm/5 text-base-content"
|
<a class="flex w-full items-center gap-3 rounded-lg px-2 py-2.5 text-left text-base/6 sm:py-2 sm:text-sm/5 text-base-content"
|
||||||
href="#" @onclick:preventDefault @onclick="Logout">
|
href="#" @onclick:preventDefault @onclick="LogoutAsync">
|
||||||
<i class="icon-log-out"></i>
|
<i class="icon-log-out"></i>
|
||||||
<span class="truncate">Logout</span>
|
<span class="truncate">Logout</span>
|
||||||
</a>
|
</a>
|
||||||
@@ -252,13 +252,13 @@
|
|||||||
if (!Layout.ShowMobileNavigation)
|
if (!Layout.ShowMobileNavigation)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await Layout.ToggleMobileNavigation();
|
await Layout.ToggleMobileNavigationAsync();
|
||||||
};
|
};
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task Logout()
|
private Task LogoutAsync()
|
||||||
{
|
{
|
||||||
Navigation.NavigateTo("/api/auth/logout", true);
|
Navigation.NavigateTo("/api/auth/logout", true);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<div class="flex h-screen justify-center items-center">
|
<div class="flex h-screen justify-center items-center">
|
||||||
<div class="sm:min-w-md">
|
<div class="sm:min-w-md">
|
||||||
<div class="bg-base-100 shadow-base-300/20 z-1 w-full space-y-6 rounded-xl p-6 shadow-md lg:p-8">
|
<div class="bg-base-100 shadow-base-300/20 z-1 w-full space-y-6 rounded-xl p-6 shadow-md lg:p-8">
|
||||||
<LazyLoader EnableDefaultSpacing="false" Load="Load">
|
<LazyLoader EnableDefaultSpacing="false" Load="LoadAsync">
|
||||||
@if (ShowSelection)
|
@if (ShowSelection)
|
||||||
{
|
{
|
||||||
<div class="flex justify-center items-center gap-3">
|
<div class="flex justify-center items-center gap-3">
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
if (config == null) // Ignore all schemes which have no ui configured
|
if (config == null) // Ignore all schemes which have no ui configured
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
<button @onclick="() => Start(scheme)" class="btn btn-text w-full"
|
<button @onclick="() => StartAsync(scheme)" class="btn btn-text w-full"
|
||||||
style="background-color: @(config.Color)">
|
style="background-color: @(config.Color)">
|
||||||
<img src="@config.IconUrl"
|
<img src="@config.IconUrl"
|
||||||
alt="scheme icon"
|
alt="scheme icon"
|
||||||
@@ -71,7 +71,7 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Load(LazyLoader arg)
|
private async Task LoadAsync(LazyLoader arg)
|
||||||
{
|
{
|
||||||
AuthSchemes = await ApiClient.GetJson<AuthSchemeResponse[]>(
|
AuthSchemes = await ApiClient.GetJson<AuthSchemeResponse[]>(
|
||||||
"api/auth"
|
"api/auth"
|
||||||
@@ -82,12 +82,12 @@
|
|||||||
// showing the selection screen
|
// showing the selection screen
|
||||||
|
|
||||||
if (AuthSchemes.Length == 1)
|
if (AuthSchemes.Length == 1)
|
||||||
await Start(AuthSchemes[0]);
|
await StartAsync(AuthSchemes[0]);
|
||||||
else
|
else
|
||||||
ShowSelection = true;
|
ShowSelection = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task Start(AuthSchemeResponse scheme)
|
private Task StartAsync(AuthSchemeResponse scheme)
|
||||||
{
|
{
|
||||||
Navigation.NavigateTo($"/api/auth/{scheme.Identifier}", true);
|
Navigation.NavigateTo($"/api/auth/{scheme.Identifier}", true);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<i class="icon-chevron-left"></i>
|
<i class="icon-chevron-left"></i>
|
||||||
Back
|
Back
|
||||||
</a>
|
</a>
|
||||||
<WButton OnClick="_ => Form.Submit()" CssClasses="btn btn-primary">
|
<WButton OnClick="Form.SubmitAsync" CssClasses="btn btn-primary">
|
||||||
<i class="icon-check"></i>
|
<i class="icon-check"></i>
|
||||||
Create
|
Create
|
||||||
</WButton>
|
</WButton>
|
||||||
@@ -66,17 +66,17 @@
|
|||||||
|
|
||||||
var response = await ApiClient.PostJson<CreateApiKeyResponse>("api/admin/apikeys", Request);
|
var response = await ApiClient.PostJson<CreateApiKeyResponse>("api/admin/apikeys", Request);
|
||||||
|
|
||||||
await DownloadService.Download(
|
await DownloadService.DownloadAsync(
|
||||||
$"moonlight-key-{response.Id}.txt",
|
$"moonlight-key-{response.Id}.txt",
|
||||||
response.Secret
|
response.Secret
|
||||||
);
|
);
|
||||||
|
|
||||||
await AlertService.Success(
|
await AlertService.SuccessAsync(
|
||||||
"API Key successfully created",
|
"API Key successfully created",
|
||||||
"The API Key has been downloaded. Dont lose it, you cant view it anymore"
|
"The API Key has been downloaded. Dont lose it, you cant view it anymore"
|
||||||
);
|
);
|
||||||
|
|
||||||
await ToastService.Success("Successfully created api key");
|
await ToastService.SuccessAsync("Successfully created api key");
|
||||||
Navigation.NavigateTo("/admin/api");
|
Navigation.NavigateTo("/admin/api");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,9 @@
|
|||||||
@using MoonCore.Helpers
|
@using MoonCore.Helpers
|
||||||
@using MoonCore.Models
|
@using MoonCore.Models
|
||||||
@using Moonlight.Shared.Http.Responses.Admin.ApiKeys
|
@using Moonlight.Shared.Http.Responses.Admin.ApiKeys
|
||||||
@using MoonCore.Blazor.FlyonUi.DataTables
|
@using MoonCore.Blazor.FlyonUi.Grid
|
||||||
|
@using MoonCore.Blazor.FlyonUi.Grid.Columns
|
||||||
|
@using MoonCore.Blazor.FlyonUi.Grid.ToolbarItems
|
||||||
|
|
||||||
@inject HttpApiClient ApiClient
|
@inject HttpApiClient ApiClient
|
||||||
@inject AlertService AlertService
|
@inject AlertService AlertService
|
||||||
@@ -47,58 +49,82 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-5 flex justify-end">
|
<DataGrid @ref="Grid"
|
||||||
<a href="/admin/api/create" class="btn btn-primary">
|
TGridItem="ApiKeyResponse"
|
||||||
Create
|
ItemsProvider="ItemsProviderAsync"
|
||||||
</a>
|
EnableFiltering="true"
|
||||||
</div>
|
EnablePagination="true">
|
||||||
|
<PropertyColumn Field="x => x.Id" Sortable="true" />
|
||||||
|
<PropertyColumn Field="x => x.Description" />
|
||||||
|
<TemplateColumn Sortable="true" Title="Expires At">
|
||||||
|
<td>
|
||||||
|
@Formatter.FormatDate(context.ExpiresAt.UtcDateTime)
|
||||||
|
</td>
|
||||||
|
</TemplateColumn>
|
||||||
|
<TemplateColumn Sortable="true" Title="Created At">
|
||||||
|
<td>
|
||||||
|
@Formatter.FormatDate(context.CreatedAt.UtcDateTime)
|
||||||
|
</td>
|
||||||
|
</TemplateColumn>
|
||||||
|
<TemplateColumn>
|
||||||
|
<td>
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<a href="/admin/api/@(context.Id)" class="text-primary mr-2 sm:mr-3">
|
||||||
|
<i class="icon-pencil text-base"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
<DataTable @ref="Table" TItem="ApiKeyResponse">
|
<a href="#" @onclick="() => DeleteAsync(context)" @onclick:preventDefault
|
||||||
<Configuration>
|
class="text-error">
|
||||||
<Pagination TItem="ApiKeyResponse" ItemSource="LoadData" />
|
<i class="icon-trash text-base"></i>
|
||||||
|
</a>
|
||||||
<DataTableColumn TItem="ApiKeyResponse" Field="@(x => x.Id)" Name="Id"/>
|
</div>
|
||||||
<DataTableColumn TItem="ApiKeyResponse" Field="@(x => x.Description)" Name="Description"/>
|
</td>
|
||||||
<DataTableColumn TItem="ApiKeyResponse" Field="@(x => x.ExpiresAt)" Name="Expires at">
|
</TemplateColumn>
|
||||||
<ColumnTemplate>
|
|
||||||
@(Formatter.FormatDate(context.ExpiresAt.UtcDateTime))
|
<TemplateToolbarItem>
|
||||||
</ColumnTemplate>
|
<a href="/admin/api/create" class="btn btn-primary ms-1.5">
|
||||||
</DataTableColumn>
|
Create
|
||||||
<DataTableColumn TItem="ApiKeyResponse">
|
</a>
|
||||||
<ColumnTemplate>
|
</TemplateToolbarItem>
|
||||||
<div class="flex justify-end">
|
</DataGrid>
|
||||||
<a href="/admin/api/@(context.Id)" class="text-primary mr-2 sm:mr-3">
|
|
||||||
<i class="icon-pencil text-base"></i>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="#" @onclick="() => Delete(context)" @onclick:preventDefault
|
|
||||||
class="text-error">
|
|
||||||
<i class="icon-trash text-base"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</ColumnTemplate>
|
|
||||||
</DataTableColumn>
|
|
||||||
</Configuration>
|
|
||||||
</DataTable>
|
|
||||||
|
|
||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
private DataTable<ApiKeyResponse> Table;
|
private DataGrid<ApiKeyResponse> Grid;
|
||||||
|
|
||||||
private async Task<IPagedData<ApiKeyResponse>> LoadData(PaginationOptions options)
|
private async Task<DataGridItemResult<ApiKeyResponse>> ItemsProviderAsync(DataGridItemRequest request)
|
||||||
=> await ApiClient.GetJson<PagedData<ApiKeyResponse>>($"api/admin/apikeys?page={options.Page}&pageSize={options.PerPage}");
|
|
||||||
|
|
||||||
private async Task Delete(ApiKeyResponse apiKeyResponse)
|
|
||||||
{
|
{
|
||||||
await AlertService.ConfirmDanger(
|
var query = $"?startIndex={request.StartIndex}&count={request.Count}";
|
||||||
"API Key deletion",
|
|
||||||
|
if (!string.IsNullOrEmpty(request.SortColumn))
|
||||||
|
{
|
||||||
|
var dir = request.SortDirection == SortState.Descending ? "desc" : "asc";
|
||||||
|
query += $"&orderBy={request.SortColumn}&orderByDir={dir}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(request.Filter))
|
||||||
|
query += $"&filter={request.Filter}";
|
||||||
|
|
||||||
|
var data = await ApiClient.GetJson<CountedData<ApiKeyResponse>>($"api/admin/apikeys{query}");
|
||||||
|
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
Items = data.Items,
|
||||||
|
TotalCount = data.TotalCount
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DeleteAsync(ApiKeyResponse apiKeyResponse)
|
||||||
|
{
|
||||||
|
await AlertService.ConfirmDangerAsync(
|
||||||
|
"API Key Deletion",
|
||||||
$"Do you really want to delete the api key '{apiKeyResponse.Description}'",
|
$"Do you really want to delete the api key '{apiKeyResponse.Description}'",
|
||||||
async () =>
|
async () =>
|
||||||
{
|
{
|
||||||
await ApiClient.Delete($"api/admin/apikeys/{apiKeyResponse.Id}");
|
await ApiClient.Delete($"api/admin/apikeys/{apiKeyResponse.Id}");
|
||||||
await ToastService.Success("Successfully deleted api key");
|
await ToastService.SuccessAsync("Successfully deleted api key");
|
||||||
|
|
||||||
await Table.Refresh();
|
await Grid.RefreshAsync();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,13 @@
|
|||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
@inject ToastService ToastService
|
@inject ToastService ToastService
|
||||||
|
|
||||||
<LazyLoader Load="Load">
|
<LazyLoader Load="LoadAsync">
|
||||||
<PageHeader Title="Update API Key">
|
<PageHeader Title="Update API Key">
|
||||||
<a href="/admin/api" class="btn btn-secondary">
|
<a href="/admin/api" class="btn btn-secondary">
|
||||||
<i class="icon-chevron-left"></i>
|
<i class="icon-chevron-left"></i>
|
||||||
Back
|
Back
|
||||||
</a>
|
</a>
|
||||||
<WButton OnClick="_ => Form.Submit()" CssClasses="btn btn-primary">
|
<WButton OnClick="Form.SubmitAsync" CssClasses="btn btn-primary">
|
||||||
<i class="icon-check"></i>
|
<i class="icon-check"></i>
|
||||||
Update
|
Update
|
||||||
</WButton>
|
</WButton>
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
private HandleForm Form;
|
private HandleForm Form;
|
||||||
private UpdateApiKeyRequest Request;
|
private UpdateApiKeyRequest Request;
|
||||||
|
|
||||||
private async Task Load(LazyLoader _)
|
private async Task LoadAsync(LazyLoader _)
|
||||||
{
|
{
|
||||||
var detail = await ApiClient.GetJson<ApiKeyResponse>($"api/admin/apikeys/{Id}");
|
var detail = await ApiClient.GetJson<ApiKeyResponse>($"api/admin/apikeys/{Id}");
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
{
|
{
|
||||||
await ApiClient.Patch($"api/admin/apikeys/{Id}", Request);
|
await ApiClient.Patch($"api/admin/apikeys/{Id}", Request);
|
||||||
|
|
||||||
await ToastService.Success("Successfully updated api key");
|
await ToastService.SuccessAsync("Successfully updated api key");
|
||||||
Navigation.NavigateTo("/admin/api");
|
Navigation.NavigateTo("/admin/api");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,17 +26,17 @@
|
|||||||
includes your installed theme and plugins. For more information, have a look at <a class="text-primary" href="https://help.moonlightpanel.xyz">our docs</a>
|
includes your installed theme and plugins. For more information, have a look at <a class="text-primary" href="https://help.moonlightpanel.xyz">our docs</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<WButton OnClick="GenerateFrontend" CssClasses="btn btn-primary mt-5">Generate frontend.zip</WButton>
|
<WButton OnClick="GenerateFrontendAsync" CssClasses="btn btn-primary mt-5">Generate frontend.zip</WButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
private async Task GenerateFrontend(WButton _)
|
private async Task GenerateFrontendAsync(WButton _)
|
||||||
{
|
{
|
||||||
var stream = await ApiClient.GetStream("api/admin/system/advanced/frontend");
|
var stream = await ApiClient.GetStream("api/admin/system/advanced/frontend");
|
||||||
|
|
||||||
await DownloadService.Download("frontend.zip", stream);
|
await DownloadService.DownloadAsync("frontend.zip", stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
@using System.Text.Json
|
@using System.Text.Json
|
||||||
@using Microsoft.AspNetCore.Authorization
|
@using Microsoft.AspNetCore.Authorization
|
||||||
@using MoonCore.Blazor.FlyonUi.DataTables
|
@using MoonCore.Blazor.FlyonUi.Grid
|
||||||
|
@using MoonCore.Blazor.FlyonUi.Grid.Columns
|
||||||
|
@using MoonCore.Blazor.FlyonUi.Grid.ToolbarItems
|
||||||
@using MoonCore.Blazor.FlyonUi.Helpers
|
@using MoonCore.Blazor.FlyonUi.Helpers
|
||||||
@using MoonCore.Helpers
|
@using MoonCore.Helpers
|
||||||
@using MoonCore.Models
|
@using MoonCore.Models
|
||||||
@@ -16,6 +18,7 @@
|
|||||||
@inject ThemeService ThemeService
|
@inject ThemeService ThemeService
|
||||||
@inject AlertService AlertService
|
@inject AlertService AlertService
|
||||||
@inject ToastService ToastService
|
@inject ToastService ToastService
|
||||||
|
@inject HttpApiClient ApiClient
|
||||||
@inject DownloadService DownloadService
|
@inject DownloadService DownloadService
|
||||||
@inject ILogger<Index> Logger
|
@inject ILogger<Index> Logger
|
||||||
|
|
||||||
@@ -25,72 +28,72 @@
|
|||||||
Themes
|
Themes
|
||||||
</PageSeparator>
|
</PageSeparator>
|
||||||
|
|
||||||
<div class="mt-5 flex justify-end">
|
<div class="my-8">
|
||||||
<div>
|
<DataGrid TGridItem="ThemeResponse"
|
||||||
<label for="import-theme" class="btn btn-info me-1">
|
ItemsProvider="ItemsProviderAsync"
|
||||||
<i class="icon-file-up"></i>
|
EnableFiltering="true"
|
||||||
Import
|
EnablePagination="true">
|
||||||
</label>
|
|
||||||
<InputFile OnChange="Import" id="import-theme" class="hidden" multiple />
|
<PropertyColumn Field="x => x.Id" Sortable="true" />
|
||||||
<a href="/admin/system/customisation/themes/create" class="btn btn-primary">Create</a>
|
<TemplateColumn Title="Name" Sortable="true">
|
||||||
</div>
|
<td>
|
||||||
</div>
|
<div class="flex items-center">
|
||||||
|
@context.Name
|
||||||
|
|
||||||
<div class="my-2.5">
|
@if (context.IsEnabled)
|
||||||
<DataTable @ref="Table" TItem="ThemeResponse">
|
{
|
||||||
<Configuration>
|
<i class="icon-check text-success ms-2"></i>
|
||||||
<DataTableColumn TItem="ThemeResponse" Field="@(x => x.Id)" Name="Id"/>
|
}
|
||||||
<DataTableColumn TItem="ThemeResponse" Field="@(x => x.Name)" Name="Name">
|
</div>
|
||||||
<ColumnTemplate>
|
</td>
|
||||||
<div class="flex items-center">
|
</TemplateColumn>
|
||||||
@context.Name
|
<PropertyColumn Field="x => x.Version" Sortable="true" />
|
||||||
|
<PropertyColumn Field="x => x.Author" />
|
||||||
@if (context.IsEnabled)
|
|
||||||
{
|
<TemplateColumn>
|
||||||
<i class="icon-check text-success ms-2"></i>
|
<td>
|
||||||
}
|
<div class="flex justify-end">
|
||||||
</div>
|
@if (!string.IsNullOrEmpty(context.DonateUrl))
|
||||||
</ColumnTemplate>
|
{
|
||||||
</DataTableColumn>
|
<a href="@context.DonateUrl" target="_blank" class="flex items-center mr-2 sm:mr-3">
|
||||||
<DataTableColumn TItem="ThemeResponse" Field="@(x => x.Author)" Name="Author"/>
|
<i class="text-accent icon-heart me-1"></i>
|
||||||
<DataTableColumn TItem="ThemeResponse" Field="@(x => x.Version)" Name="Version"/>
|
<span class="text-accent">Donate</span>
|
||||||
<DataTableColumn TItem="ThemeResponse">
|
|
||||||
<ColumnTemplate>
|
|
||||||
<div class="flex justify-end">
|
|
||||||
@if (!string.IsNullOrEmpty(context.DonateUrl))
|
|
||||||
{
|
|
||||||
<a href="@context.DonateUrl" target="_blank" class="flex items-center mr-2 sm:mr-3">
|
|
||||||
<i class="text-accent icon-heart me-1"></i>
|
|
||||||
<span class="text-accent">Donate</span>
|
|
||||||
</a>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (!string.IsNullOrEmpty(context.UpdateUrl))
|
|
||||||
{
|
|
||||||
<a href="#" class="flex items-center mr-2 sm:mr-3">
|
|
||||||
<i class="text-info icon-refresh-cw me-1"></i>
|
|
||||||
<span class="text-info">Update</span>
|
|
||||||
</a>
|
|
||||||
}
|
|
||||||
|
|
||||||
<a @onclick="() => Export(context)" @onclick:preventDefault href="#" class="flex items-center mr-2 sm:mr-3">
|
|
||||||
<i class="text-success icon-download me-1"></i>
|
|
||||||
<span class="text-success">Export</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="/admin/system/customisation/themes/@(context.Id)" class="mr-2 sm:mr-3">
|
|
||||||
<i class="icon-pencil text-primary"></i>
|
|
||||||
</a>
|
</a>
|
||||||
|
}
|
||||||
|
|
||||||
<a href="#" @onclick="() => Delete(context)" @onclick:preventDefault>
|
@if (!string.IsNullOrEmpty(context.UpdateUrl))
|
||||||
<i class="icon-trash text-error"></i>
|
{
|
||||||
|
<a href="#" class="flex items-center mr-2 sm:mr-3">
|
||||||
|
<i class="text-info icon-refresh-cw me-1"></i>
|
||||||
|
<span class="text-info">Update</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
}
|
||||||
</ColumnTemplate>
|
|
||||||
</DataTableColumn>
|
<a @onclick="() => ExportAsync(context)" @onclick:preventDefault href="#" class="flex items-center mr-2 sm:mr-3">
|
||||||
<Pagination TItem="ThemeResponse" PageSize="10" ItemSource="LoadItems"/>
|
<i class="text-success icon-download me-1"></i>
|
||||||
</Configuration>
|
<span class="text-success">Export</span>
|
||||||
</DataTable>
|
</a>
|
||||||
|
|
||||||
|
<a href="/admin/system/customisation/themes/@(context.Id)" class="mr-2 sm:mr-3">
|
||||||
|
<i class="icon-pencil text-primary"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="#" @onclick="() => DeleteAsync(context)" @onclick:preventDefault>
|
||||||
|
<i class="icon-trash text-error"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</TemplateColumn>
|
||||||
|
|
||||||
|
<TemplateToolbarItem>
|
||||||
|
<label for="import-theme" class="btn btn-info me-1 ms-1.5">
|
||||||
|
<i class="icon-file-up"></i>
|
||||||
|
Import
|
||||||
|
</label>
|
||||||
|
<InputFile OnChange="ImportAsync" id="import-theme" class="hidden" multiple />
|
||||||
|
<a href="/admin/system/customisation/themes/create" class="btn btn-primary">Create</a>
|
||||||
|
</TemplateToolbarItem>
|
||||||
|
</DataGrid>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PageSeparator Icon="icon-images">
|
<PageSeparator Icon="icon-images">
|
||||||
@@ -100,12 +103,31 @@
|
|||||||
|
|
||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
private DataTable<ThemeResponse> Table;
|
private DataGrid<ThemeResponse> Grid;
|
||||||
|
|
||||||
private async Task<IPagedData<ThemeResponse>> LoadItems(PaginationOptions options)
|
private async Task<DataGridItemResult<ThemeResponse>> ItemsProviderAsync(DataGridItemRequest request)
|
||||||
=> await ThemeService.Get(options.Page, options.PerPage);
|
{
|
||||||
|
var query = $"?startIndex={request.StartIndex}&count={request.Count}";
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(request.SortColumn))
|
||||||
|
{
|
||||||
|
var dir = request.SortDirection == SortState.Descending ? "desc" : "asc";
|
||||||
|
query += $"&orderBy={request.SortColumn}&orderByDir={dir}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(request.Filter))
|
||||||
|
query += $"&filter={request.Filter}";
|
||||||
|
|
||||||
|
var data = await ApiClient.GetJson<CountedData<ThemeResponse>>($"api/admin/system/customisation/themes{query}");
|
||||||
|
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
Items = data.Items,
|
||||||
|
TotalCount = data.TotalCount
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private async Task Import(InputFileChangeEventArgs eventArgs)
|
private async Task ImportAsync(InputFileChangeEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if(eventArgs.FileCount < 1)
|
if(eventArgs.FileCount < 1)
|
||||||
return;
|
return;
|
||||||
@@ -120,13 +142,13 @@
|
|||||||
{
|
{
|
||||||
if (!file.Name.EndsWith(".json"))
|
if (!file.Name.EndsWith(".json"))
|
||||||
{
|
{
|
||||||
await ToastService.Error($"Unable to import {file.Name}", "Only .json files are supported");
|
await ToastService.ErrorAsync($"Unable to import {file.Name}", "Only .json files are supported");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.Size > maxFileSize)
|
if (file.Size > maxFileSize)
|
||||||
{
|
{
|
||||||
await ToastService.Error($"Unable to import {file.Name}", "Exceeded the maximum file limit of 1MB");
|
await ToastService.ErrorAsync($"Unable to import {file.Name}", "Exceeded the maximum file limit of 1MB");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,11 +159,11 @@
|
|||||||
|
|
||||||
if (themeTransfer == null)
|
if (themeTransfer == null)
|
||||||
{
|
{
|
||||||
await ToastService.Error($"Unable to import {file.Name}", "Failed to deserialize the content");
|
await ToastService.ErrorAsync($"Unable to import {file.Name}", "Failed to deserialize the content");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var theme = await ThemeService.Create(new CreateThemeRequest()
|
var theme = await ThemeService.CreateAsync(new CreateThemeRequest()
|
||||||
{
|
{
|
||||||
Name = themeTransfer.Name,
|
Name = themeTransfer.Name,
|
||||||
Author = themeTransfer.Author,
|
Author = themeTransfer.Author,
|
||||||
@@ -151,9 +173,9 @@
|
|||||||
Version = themeTransfer.Version
|
Version = themeTransfer.Version
|
||||||
});
|
});
|
||||||
|
|
||||||
await ToastService.Success("Successfully imported theme", theme.Name);
|
await ToastService.SuccessAsync("Successfully imported theme", theme.Name);
|
||||||
|
|
||||||
await Table.Refresh();
|
await Grid.RefreshAsync();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -162,7 +184,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Export(ThemeResponse theme)
|
private async Task ExportAsync(ThemeResponse theme)
|
||||||
{
|
{
|
||||||
var transfer = new ThemeTransferModel()
|
var transfer = new ThemeTransferModel()
|
||||||
{
|
{
|
||||||
@@ -181,20 +203,20 @@
|
|||||||
|
|
||||||
var fileName = $"{transfer.Name.Replace(" ", string.Empty).Trim()}.json";
|
var fileName = $"{transfer.Name.Replace(" ", string.Empty).Trim()}.json";
|
||||||
|
|
||||||
await DownloadService.Download(fileName, json);
|
await DownloadService.DownloadAsync(fileName, json);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Delete(ThemeResponse response)
|
private async Task DeleteAsync(ThemeResponse response)
|
||||||
{
|
{
|
||||||
await AlertService.ConfirmDanger(
|
await AlertService.ConfirmDangerAsync(
|
||||||
"Theme deletion",
|
"Theme deletion",
|
||||||
$"Do you really want to delete the theme: {response.Name}",
|
$"Do you really want to delete the theme: {response.Name}",
|
||||||
async () =>
|
async () =>
|
||||||
{
|
{
|
||||||
await ThemeService.Delete(response.Id);
|
await ThemeService.DeleteAsync(response.Id);
|
||||||
|
|
||||||
await ToastService.Success("Successfully deleted theme");
|
await ToastService.SuccessAsync("Successfully deleted theme");
|
||||||
await Table.Refresh();
|
await Grid.RefreshAsync();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<i class="icon-chevron-left"></i>
|
<i class="icon-chevron-left"></i>
|
||||||
Back
|
Back
|
||||||
</a>
|
</a>
|
||||||
<WButton OnClick="_ => Form.Submit()" CssClasses="btn btn-primary">
|
<WButton OnClick="Form.SubmitAsync" CssClasses="btn btn-primary">
|
||||||
<i class="icon-check"></i>
|
<i class="icon-check"></i>
|
||||||
Create
|
Create
|
||||||
</WButton>
|
</WButton>
|
||||||
@@ -58,8 +58,8 @@
|
|||||||
|
|
||||||
private async Task OnValidSubmit()
|
private async Task OnValidSubmit()
|
||||||
{
|
{
|
||||||
await ThemeService.Create(Request);
|
await ThemeService.CreateAsync(Request);
|
||||||
await ToastService.Success("Successfully created theme");
|
await ToastService.SuccessAsync("Successfully created theme");
|
||||||
|
|
||||||
NavigationManager.NavigateTo("/admin/system/customisation");
|
NavigationManager.NavigateTo("/admin/system/customisation");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,13 @@
|
|||||||
@inject ToastService ToastService
|
@inject ToastService ToastService
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
|
|
||||||
<LazyLoader Load="Load">
|
<LazyLoader Load="LoadAsync">
|
||||||
<PageHeader Title="@($"Update {Request.Name}")">
|
<PageHeader Title="@($"Update {Request.Name}")">
|
||||||
<a href="/admin/system/customisation" class="btn btn-secondary">
|
<a href="/admin/system/customisation" class="btn btn-secondary">
|
||||||
<i class="icon-chevron-left"></i>
|
<i class="icon-chevron-left"></i>
|
||||||
Back
|
Back
|
||||||
</a>
|
</a>
|
||||||
<WButton OnClick="_ => Form.Submit()" CssClasses="btn btn-primary">
|
<WButton OnClick="Form.SubmitAsync" CssClasses="btn btn-primary">
|
||||||
<i class="icon-check"></i>
|
<i class="icon-check"></i>
|
||||||
Update
|
Update
|
||||||
</WButton>
|
</WButton>
|
||||||
@@ -80,9 +80,9 @@
|
|||||||
|
|
||||||
private HandleForm Form;
|
private HandleForm Form;
|
||||||
|
|
||||||
private async Task Load(LazyLoader _)
|
private async Task LoadAsync(LazyLoader _)
|
||||||
{
|
{
|
||||||
Response = await ThemeService.Get(Id);
|
Response = await ThemeService.GetAsync(Id);
|
||||||
|
|
||||||
Request = new()
|
Request = new()
|
||||||
{
|
{
|
||||||
@@ -98,9 +98,9 @@
|
|||||||
|
|
||||||
private async Task OnValidSubmit()
|
private async Task OnValidSubmit()
|
||||||
{
|
{
|
||||||
await ThemeService.Update(Id, Request);
|
await ThemeService.UpdateAsync(Id, Request);
|
||||||
|
|
||||||
await ToastService.Success("Successfully updated theme");
|
await ToastService.SuccessAsync("Successfully updated theme");
|
||||||
Navigation.NavigateTo("/admin/system/customisation");
|
Navigation.NavigateTo("/admin/system/customisation");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,11 +28,11 @@
|
|||||||
If you only want to export specific parts of the diagnose report, click on "Advanced" and select the desired providers
|
If you only want to export specific parts of the diagnose report, click on "Advanced" and select the desired providers
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<WButton OnClick="GenerateDiagnose" CssClasses="btn btn-primary my-5">Generate report</WButton>
|
<WButton OnClick="GenerateDiagnoseAsync" CssClasses="btn btn-primary my-5">Generate report</WButton>
|
||||||
|
|
||||||
<div class="text-sm">
|
<div class="text-sm">
|
||||||
<a class="text-primary cursor-pointer flex items-center" @onclick:preventDefault
|
<a class="text-primary cursor-pointer flex items-center" @onclick:preventDefault
|
||||||
@onclick="ToggleDropDown">
|
@onclick="ToggleDropDownAsync">
|
||||||
<span class="me-1.5">Advanced</span>
|
<span class="me-1.5">Advanced</span>
|
||||||
@if (DropdownOpen)
|
@if (DropdownOpen)
|
||||||
{
|
{
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="@(DropdownOpen ? "" : "hidden")">
|
<div class="@(DropdownOpen ? "" : "hidden")">
|
||||||
<LazyLoader Load="Load">
|
<LazyLoader Load="LoadAsync">
|
||||||
<div class="mb-2 py-2 border-b border-base-content/70 flex items-center gap-3">
|
<div class="mb-2 py-2 border-b border-base-content/70 flex items-center gap-3">
|
||||||
<input id="selectall_checkbox" @bind="SelectAll" type="checkbox" class="checkbox checkbox-primary checkbox-xs">
|
<input id="selectall_checkbox" @bind="SelectAll" type="checkbox" class="checkbox checkbox-primary checkbox-xs">
|
||||||
<label for="selectall_checkbox">Select all</label>
|
<label for="selectall_checkbox">Select all</label>
|
||||||
@@ -86,7 +86,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Load(LazyLoader arg)
|
private async Task LoadAsync(LazyLoader arg)
|
||||||
{
|
{
|
||||||
var providers = await ApiClient.GetJson<DiagnoseProvideResponse[]>(
|
var providers = await ApiClient.GetJson<DiagnoseProvideResponse[]>(
|
||||||
"api/admin/system/diagnose/providers"
|
"api/admin/system/diagnose/providers"
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
.ToDictionary(x => x, _ => true);
|
.ToDictionary(x => x, _ => true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task GenerateDiagnose(WButton _)
|
private async Task GenerateDiagnoseAsync(WButton button)
|
||||||
{
|
{
|
||||||
var request = new GenerateDiagnoseRequest();
|
var request = new GenerateDiagnoseRequest();
|
||||||
|
|
||||||
@@ -111,11 +111,11 @@
|
|||||||
|
|
||||||
var stream = await ApiClient.PostStream("api/admin/system/diagnose", request);
|
var stream = await ApiClient.PostStream("api/admin/system/diagnose", request);
|
||||||
|
|
||||||
await DownloadService.Download("diagnose.zip", stream);
|
await DownloadService.DownloadAsync("diagnose.zip", stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async Task ToggleDropDown()
|
private async Task ToggleDropDownAsync()
|
||||||
{
|
{
|
||||||
DropdownOpen = !DropdownOpen;
|
DropdownOpen = !DropdownOpen;
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
</HelperMessage>
|
</HelperMessage>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<LazyLoader Load="Load">
|
<LazyLoader Load="LoadAsync">
|
||||||
<div class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-4 gap-5">
|
<div class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-4 gap-5">
|
||||||
<StatCard Title="Servers" Text="@Stats.Servers.ToString()" Icon="icon-server"/>
|
<StatCard Title="Servers" Text="@Stats.Servers.ToString()" Icon="icon-server"/>
|
||||||
<StatCard Title="Recurring" Text="@Stats.Recurring.ToString()" Icon="icon-calendar-sync"/>
|
<StatCard Title="Recurring" Text="@Stats.Recurring.ToString()" Icon="icon-calendar-sync"/>
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
{
|
{
|
||||||
private HangfireStatsResponse Stats;
|
private HangfireStatsResponse Stats;
|
||||||
|
|
||||||
private async Task Load(LazyLoader _)
|
private async Task LoadAsync(LazyLoader _)
|
||||||
{
|
{
|
||||||
Stats = await ApiClient.GetJson<HangfireStatsResponse>(
|
Stats = await ApiClient.GetJson<HangfireStatsResponse>(
|
||||||
"api/admin/system/hangfire/stats"
|
"api/admin/system/hangfire/stats"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
<NavTabs Index="0" Names="UiConstants.AdminNavNames" Links="UiConstants.AdminNavLinks" />
|
<NavTabs Index="0" Names="UiConstants.AdminNavNames" Links="UiConstants.AdminNavLinks" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<LazyLoader Load="LoadOverview">
|
<LazyLoader Load="LoadOverviewAsync">
|
||||||
<div class="gap-5 grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-4">
|
<div class="gap-5 grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-4">
|
||||||
<StatCard Title="CPU Usage" Text="@(OverviewData.CpuUsage + "%")" Icon="icon-cpu"/>
|
<StatCard Title="CPU Usage" Text="@(OverviewData.CpuUsage + "%")" Icon="icon-cpu"/>
|
||||||
<StatCard Title="Memory Usage" Text="@(Formatter.FormatSize(OverviewData.MemoryUsage))" Icon="icon-memory-stick"/>
|
<StatCard Title="Memory Usage" Text="@(Formatter.FormatSize(OverviewData.MemoryUsage))" Icon="icon-memory-stick"/>
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div class="card card-body">
|
<div class="card card-body">
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<WButton OnClick="Restart" CssClasses="btn btn-error w-full">
|
<WButton OnClick="RestartAsync" CssClasses="btn btn-error w-full">
|
||||||
<i class="icon-repeat me-2"></i>
|
<i class="icon-repeat me-2"></i>
|
||||||
Restart/Shutdown
|
Restart/Shutdown
|
||||||
</WButton>
|
</WButton>
|
||||||
@@ -41,12 +41,12 @@
|
|||||||
{
|
{
|
||||||
private SystemOverviewResponse OverviewData;
|
private SystemOverviewResponse OverviewData;
|
||||||
|
|
||||||
private async Task LoadOverview(LazyLoader arg)
|
private async Task LoadOverviewAsync(LazyLoader arg)
|
||||||
{
|
{
|
||||||
OverviewData = await ApiClient.GetJson<SystemOverviewResponse>("api/admin/system");
|
OverviewData = await ApiClient.GetJson<SystemOverviewResponse>("api/admin/system");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Restart(WButton _)
|
private async Task RestartAsync(WButton _)
|
||||||
{
|
{
|
||||||
await ApiClient.Post("api/admin/system/shutdown");
|
await ApiClient.Post("api/admin/system/shutdown");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<i class="icon-chevron-left"></i>
|
<i class="icon-chevron-left"></i>
|
||||||
Back
|
Back
|
||||||
</a>
|
</a>
|
||||||
<WButton OnClick="_ => Form.Submit()" CssClasses="btn btn-primary">
|
<WButton OnClick="Form.SubmitAsync" CssClasses="btn btn-primary">
|
||||||
<i class="icon-check"></i>
|
<i class="icon-check"></i>
|
||||||
Create
|
Create
|
||||||
</WButton>
|
</WButton>
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
|
|
||||||
await ApiClient.Post("api/admin/users", Request);
|
await ApiClient.Post("api/admin/users", Request);
|
||||||
|
|
||||||
await ToastService.Success("Successfully created user");
|
await ToastService.SuccessAsync("Successfully created user");
|
||||||
Navigation.NavigateTo("/admin/users");
|
Navigation.NavigateTo("/admin/users");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,13 +3,14 @@
|
|||||||
@using MoonCore.Helpers
|
@using MoonCore.Helpers
|
||||||
@using MoonCore.Models
|
@using MoonCore.Models
|
||||||
@using Moonlight.Shared.Http.Responses.Admin.Users
|
@using Moonlight.Shared.Http.Responses.Admin.Users
|
||||||
@using MoonCore.Blazor.FlyonUi.DataTables
|
@using MoonCore.Blazor.FlyonUi.Grid
|
||||||
|
@using MoonCore.Blazor.FlyonUi.Grid.Columns
|
||||||
|
|
||||||
@inject HttpApiClient ApiClient
|
@inject HttpApiClient ApiClient
|
||||||
@inject AlertService AlertService
|
@inject AlertService AlertService
|
||||||
@inject ToastService ToastService
|
@inject ToastService ToastService
|
||||||
|
|
||||||
<div class="mb-5">
|
<div class="mb-8">
|
||||||
<PageHeader Title="Users">
|
<PageHeader Title="Users">
|
||||||
<a href="/admin/users/create" class="btn btn-primary">
|
<a href="/admin/users/create" class="btn btn-primary">
|
||||||
Create
|
Create
|
||||||
@@ -17,47 +18,67 @@
|
|||||||
</PageHeader>
|
</PageHeader>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DataTable @ref="Table" TItem="UserResponse">
|
<DataGrid @ref="Grid"
|
||||||
<Configuration>
|
TGridItem="UserResponse"
|
||||||
<Pagination TItem="UserResponse" ItemSource="LoadData" />
|
ItemsProvider="ItemsProviderAsync"
|
||||||
|
EnableFiltering="true"
|
||||||
<DataTableColumn TItem="UserResponse" Field="@(x => x.Id)" Name="Id"/>
|
EnablePagination="true">
|
||||||
<DataTableColumn TItem="UserResponse" Field="@(x => x.Username)" Name="Username"/>
|
<PropertyColumn Field="x => x.Id" Sortable="true" />
|
||||||
<DataTableColumn TItem="UserResponse" Field="@(x => x.Email)" Name="Email"/>
|
<PropertyColumn Field="x => x.Username" Sortable="true" />
|
||||||
<DataTableColumn TItem="UserResponse">
|
<PropertyColumn Field="x => x.Email" Sortable="true" />
|
||||||
<ColumnTemplate>
|
|
||||||
<div class="flex justify-end">
|
<TemplateColumn>
|
||||||
<a href="/admin/users/@(context.Id)" class="mr-2 sm:mr-3">
|
<td>
|
||||||
<i class="icon-pencil text-primary"></i>
|
<div class="flex justify-end">
|
||||||
</a>
|
<a href="/admin/users/@(context.Id)" class="mr-2 sm:mr-3">
|
||||||
|
<i class="icon-pencil text-primary"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
<a href="#" @onclick="() => Delete(context)" @onclick:preventDefault>
|
<a href="#" @onclick="() => DeleteAsync(context)" @onclick:preventDefault>
|
||||||
<i class="icon-trash text-error"></i>
|
<i class="icon-trash text-error"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</ColumnTemplate>
|
</td>
|
||||||
</DataTableColumn>
|
</TemplateColumn>
|
||||||
</Configuration>
|
</DataGrid>
|
||||||
</DataTable>
|
|
||||||
|
|
||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
private DataTable<UserResponse> Table;
|
private DataGrid<UserResponse> Grid;
|
||||||
|
|
||||||
private async Task<IPagedData<UserResponse>> LoadData(PaginationOptions options)
|
private async Task<DataGridItemResult<UserResponse>> ItemsProviderAsync(DataGridItemRequest request)
|
||||||
=> await ApiClient.GetJson<PagedData<UserResponse>>($"api/admin/users?page={options.Page}&pageSize={options.PerPage}");
|
|
||||||
|
|
||||||
private async Task Delete(UserResponse response)
|
|
||||||
{
|
{
|
||||||
await AlertService.ConfirmDanger(
|
var query = $"?startIndex={request.StartIndex}&count={request.Count}";
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(request.SortColumn))
|
||||||
|
{
|
||||||
|
var dir = request.SortDirection == SortState.Descending ? "desc" : "asc";
|
||||||
|
query += $"&orderBy={request.SortColumn}&orderByDir={dir}";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(request.Filter))
|
||||||
|
query += $"&filter={request.Filter}";
|
||||||
|
|
||||||
|
var data = await ApiClient.GetJson<CountedData<UserResponse>>($"api/admin/users{query}");
|
||||||
|
|
||||||
|
return new()
|
||||||
|
{
|
||||||
|
Items = data.Items,
|
||||||
|
TotalCount = data.TotalCount
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DeleteAsync(UserResponse response)
|
||||||
|
{
|
||||||
|
await AlertService.ConfirmDangerAsync(
|
||||||
"User deletion",
|
"User deletion",
|
||||||
$"Do you really want to delete the user '{response.Username}'",
|
$"Do you really want to delete the user '{response.Username}'",
|
||||||
async () =>
|
async () =>
|
||||||
{
|
{
|
||||||
await ApiClient.Delete($"api/admin/users/{response.Id}");
|
await ApiClient.Delete($"api/admin/users/{response.Id}");
|
||||||
await ToastService.Success("Successfully deleted user");
|
await ToastService.SuccessAsync("Successfully deleted user");
|
||||||
|
|
||||||
await Table.Refresh();
|
await Grid.RefreshAsync();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,13 +8,13 @@
|
|||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
@inject ToastService ToastService
|
@inject ToastService ToastService
|
||||||
|
|
||||||
<LazyLoader Load="Load">
|
<LazyLoader Load="LoadAsync">
|
||||||
<PageHeader Title="Update User">
|
<PageHeader Title="Update User">
|
||||||
<a href="/admin/users" class="btn btn-secondary">
|
<a href="/admin/users" class="btn btn-secondary">
|
||||||
<i class="icon-chevron-left"></i>
|
<i class="icon-chevron-left"></i>
|
||||||
Back
|
Back
|
||||||
</a>
|
</a>
|
||||||
<WButton OnClick="_ => Form.Submit()" CssClasses="btn btn-primary">
|
<WButton OnClick="Form.SubmitAsync" CssClasses="btn btn-primary">
|
||||||
<i class="icon-check"></i>
|
<i class="icon-check"></i>
|
||||||
Update
|
Update
|
||||||
</WButton>
|
</WButton>
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
|
|
||||||
private List<string> Permissions = [];
|
private List<string> Permissions = [];
|
||||||
|
|
||||||
private async Task Load(LazyLoader _)
|
private async Task LoadAsync(LazyLoader _)
|
||||||
{
|
{
|
||||||
var detail = await ApiClient.GetJson<UserResponse>($"api/admin/users/{Id}");
|
var detail = await ApiClient.GetJson<UserResponse>($"api/admin/users/{Id}");
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
|
|
||||||
await ApiClient.Patch($"api/admin/users/{Id}", Request);
|
await ApiClient.Patch($"api/admin/users/{Id}", Request);
|
||||||
|
|
||||||
await ToastService.Success("Successfully updated user");
|
await ToastService.SuccessAsync("Successfully updated user");
|
||||||
Navigation.NavigateTo("/admin/users");
|
Navigation.NavigateTo("/admin/users");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,4 +6,5 @@ public class ApiKeyResponse
|
|||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public string[] Permissions { get; set; } = [];
|
public string[] Permissions { get; set; } = [];
|
||||||
public DateTimeOffset ExpiresAt { get; set; }
|
public DateTimeOffset ExpiresAt { get; set; }
|
||||||
|
public DateTimeOffset CreatedAt { get; set; }
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user