Merge pull request #372 from Moonlight-Panel/v2_ServerFeature
V2 server feature
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
using Moonlight.Core.Actions.Dummy.Layouts;
|
using Moonlight.Core.Actions.Dummy.Layouts;
|
||||||
using Moonlight.Core.Actions.Dummy.Pages;
|
using Moonlight.Core.Actions.Dummy.Pages;
|
||||||
using Moonlight.Core.Helpers;
|
|
||||||
using Moonlight.Features.ServiceManagement.Models.Abstractions;
|
using Moonlight.Features.ServiceManagement.Models.Abstractions;
|
||||||
|
|
||||||
namespace Moonlight.Core.Actions.Dummy;
|
namespace Moonlight.Core.Actions.Dummy;
|
||||||
@@ -11,14 +10,14 @@ public class DummyServiceDefinition : ServiceDefinition
|
|||||||
public override Type ConfigType => typeof(DummyConfig);
|
public override Type ConfigType => typeof(DummyConfig);
|
||||||
public override async Task BuildUserView(ServiceViewContext context)
|
public override async Task BuildUserView(ServiceViewContext context)
|
||||||
{
|
{
|
||||||
context.Layout = ComponentHelper.FromType<DummyUser>();
|
context.Layout = typeof(DummyUser);
|
||||||
|
|
||||||
await context.AddPage<DummyPage>("Demo", "/demo");
|
await context.AddPage<DummyPage>("Demo", "/demo");
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task BuildAdminView(ServiceViewContext context)
|
public override Task BuildAdminView(ServiceViewContext context)
|
||||||
{
|
{
|
||||||
context.Layout = ComponentHelper.FromType<DummyAdmin>();
|
context.Layout = typeof(DummyAdmin);
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using Moonlight.Core.Helpers;
|
using MoonCore.Helpers;
|
||||||
using Moonlight.Features.Advertisement.Configuration;
|
using Moonlight.Features.Advertisement.Configuration;
|
||||||
|
using Moonlight.Features.FileManager.Configuration;
|
||||||
|
using Moonlight.Features.Servers.Configuration;
|
||||||
using Moonlight.Features.StoreSystem.Configuration;
|
using Moonlight.Features.StoreSystem.Configuration;
|
||||||
using Moonlight.Features.Theming.Configuration;
|
using Moonlight.Features.Theming.Configuration;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@@ -22,6 +24,19 @@ public class ConfigV1
|
|||||||
[JsonProperty("Theme")] public ThemeData Theme { get; set; } = new();
|
[JsonProperty("Theme")] public ThemeData Theme { get; set; } = new();
|
||||||
[JsonProperty("Advertisement")] public AdvertisementData Advertisement { get; set; } = new();
|
[JsonProperty("Advertisement")] public AdvertisementData Advertisement { get; set; } = new();
|
||||||
|
|
||||||
|
[JsonProperty("FileManager")] public FileManagerData FileManager { get; set; } = new();
|
||||||
|
|
||||||
|
[JsonProperty("WebServer")] public WebServerData WebServer { get; set; } = new();
|
||||||
|
|
||||||
|
[JsonProperty("Servers")] public ServersData Servers { get; set; } = new();
|
||||||
|
|
||||||
|
public class WebServerData
|
||||||
|
{
|
||||||
|
[JsonProperty("HttpUploadLimit")]
|
||||||
|
[Description("This sets the kestrel upload limit in megabytes. Changing this will need an restart")]
|
||||||
|
public int HttpUploadLimit { get; set; } = 100 * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
public class SecurityData
|
public class SecurityData
|
||||||
{
|
{
|
||||||
[JsonProperty("Token")]
|
[JsonProperty("Token")]
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using MoonCore.Services;
|
||||||
|
using Moonlight.Core.Configuration;
|
||||||
using Moonlight.Core.Database.Entities;
|
using Moonlight.Core.Database.Entities;
|
||||||
using Moonlight.Core.Services;
|
using Moonlight.Core.Services;
|
||||||
using Moonlight.Features.Community.Entities;
|
using Moonlight.Features.Community.Entities;
|
||||||
|
using Moonlight.Features.Servers.Entities;
|
||||||
using Moonlight.Features.ServiceManagement.Entities;
|
using Moonlight.Features.ServiceManagement.Entities;
|
||||||
using Moonlight.Features.StoreSystem.Entities;
|
using Moonlight.Features.StoreSystem.Entities;
|
||||||
using Moonlight.Features.Theming.Entities;
|
using Moonlight.Features.Theming.Entities;
|
||||||
@@ -11,7 +14,7 @@ namespace Moonlight.Core.Database;
|
|||||||
|
|
||||||
public class DataContext : DbContext
|
public class DataContext : DbContext
|
||||||
{
|
{
|
||||||
private readonly ConfigService ConfigService;
|
private readonly ConfigService<ConfigV1> ConfigService;
|
||||||
|
|
||||||
public DbSet<User> Users { get; set; }
|
public DbSet<User> Users { get; set; }
|
||||||
|
|
||||||
@@ -40,7 +43,17 @@ public class DataContext : DbContext
|
|||||||
// Themes
|
// Themes
|
||||||
public DbSet<Theme> Themes { get; set; }
|
public DbSet<Theme> Themes { get; set; }
|
||||||
|
|
||||||
public DataContext(ConfigService configService)
|
// Servers
|
||||||
|
public DbSet<Server> Servers { get; set; }
|
||||||
|
public DbSet<ServerAllocation> ServerAllocations { get; set; }
|
||||||
|
public DbSet<ServerImage> ServerImages { get; set; }
|
||||||
|
public DbSet<ServerNode> ServerNodes { get; set; }
|
||||||
|
public DbSet<ServerVariable> ServerVariables { get; set; }
|
||||||
|
public DbSet<ServerDockerImage> ServerDockerImages { get; set; }
|
||||||
|
public DbSet<ServerImageVariable> ServerImageVariables { get; set; }
|
||||||
|
public DbSet<ServerSchedule> ServerSchedules { get; set; }
|
||||||
|
|
||||||
|
public DataContext(ConfigService<ConfigV1> configService)
|
||||||
{
|
{
|
||||||
ConfigService = configService;
|
ConfigService = configService;
|
||||||
}
|
}
|
||||||
|
|||||||
1026
Moonlight/Core/Database/Migrations/20240127110558_AddedServerModels.Designer.cs
generated
Normal file
1026
Moonlight/Core/Database/Migrations/20240127110558_AddedServerModels.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,266 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Moonlight.Core.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddedServerModels : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ServerImages",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
Name = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
AllocationsNeeded = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
StartupCommand = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
StopCommand = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
OnlineDetection = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
ParseConfigurations = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
InstallDockerImage = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
InstallShell = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
InstallScript = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Author = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
DonateUrl = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
UpdateUrl = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
DefaultDockerImageIndex = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ServerImages", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ServerImageVariables",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
Key = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
DefaultValue = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
DisplayName = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Description = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
AllowUserToEdit = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||||
|
AllowUserToView = table.Column<bool>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ServerImageVariables", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ServerNodes",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
Name = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Fqdn = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
UseSsl = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||||
|
Token = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
HttpPort = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
FtpPort = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ServerNodes", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ServerDockerImages",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
Name = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
DisplayName = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
AutoPull = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||||
|
ServerImageId = table.Column<int>(type: "INTEGER", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ServerDockerImages", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ServerDockerImages_ServerImages_ServerImageId",
|
||||||
|
column: x => x.ServerImageId,
|
||||||
|
principalTable: "ServerImages",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ServerAllocations",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
IpAddress = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Port = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
ServerId = table.Column<int>(type: "INTEGER", nullable: true),
|
||||||
|
ServerNodeId = table.Column<int>(type: "INTEGER", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ServerAllocations", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ServerAllocations_ServerNodes_ServerNodeId",
|
||||||
|
column: x => x.ServerNodeId,
|
||||||
|
principalTable: "ServerNodes",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Servers",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
ServiceId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
Cpu = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
Memory = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
Disk = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
ImageId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
DockerImageIndex = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
OverrideStartupCommand = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
NodeId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
MainAllocationId = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Servers", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Servers_ServerAllocations_MainAllocationId",
|
||||||
|
column: x => x.MainAllocationId,
|
||||||
|
principalTable: "ServerAllocations",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Servers_ServerImages_ImageId",
|
||||||
|
column: x => x.ImageId,
|
||||||
|
principalTable: "ServerImages",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Servers_ServerNodes_NodeId",
|
||||||
|
column: x => x.NodeId,
|
||||||
|
principalTable: "ServerNodes",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Servers_Services_ServiceId",
|
||||||
|
column: x => x.ServiceId,
|
||||||
|
principalTable: "Services",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ServerVariables",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
Key = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Value = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
ServerId = table.Column<int>(type: "INTEGER", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ServerVariables", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ServerVariables_Servers_ServerId",
|
||||||
|
column: x => x.ServerId,
|
||||||
|
principalTable: "Servers",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ServerAllocations_ServerId",
|
||||||
|
table: "ServerAllocations",
|
||||||
|
column: "ServerId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ServerAllocations_ServerNodeId",
|
||||||
|
table: "ServerAllocations",
|
||||||
|
column: "ServerNodeId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ServerDockerImages_ServerImageId",
|
||||||
|
table: "ServerDockerImages",
|
||||||
|
column: "ServerImageId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Servers_ImageId",
|
||||||
|
table: "Servers",
|
||||||
|
column: "ImageId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Servers_MainAllocationId",
|
||||||
|
table: "Servers",
|
||||||
|
column: "MainAllocationId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Servers_NodeId",
|
||||||
|
table: "Servers",
|
||||||
|
column: "NodeId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Servers_ServiceId",
|
||||||
|
table: "Servers",
|
||||||
|
column: "ServiceId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ServerVariables_ServerId",
|
||||||
|
table: "ServerVariables",
|
||||||
|
column: "ServerId");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_ServerAllocations_Servers_ServerId",
|
||||||
|
table: "ServerAllocations",
|
||||||
|
column: "ServerId",
|
||||||
|
principalTable: "Servers",
|
||||||
|
principalColumn: "Id");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_ServerAllocations_ServerNodes_ServerNodeId",
|
||||||
|
table: "ServerAllocations");
|
||||||
|
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_Servers_ServerNodes_NodeId",
|
||||||
|
table: "Servers");
|
||||||
|
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_ServerAllocations_Servers_ServerId",
|
||||||
|
table: "ServerAllocations");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ServerDockerImages");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ServerImageVariables");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ServerVariables");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ServerNodes");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Servers");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ServerAllocations");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ServerImages");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1040
Moonlight/Core/Database/Migrations/20240127164420_FixedMissingPropertyInServerImage.Designer.cs
generated
Normal file
1040
Moonlight/Core/Database/Migrations/20240127164420_FixedMissingPropertyInServerImage.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,48 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Moonlight.Core.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class FixedMissingPropertyInServerImage : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "ServerImageId",
|
||||||
|
table: "ServerImageVariables",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ServerImageVariables_ServerImageId",
|
||||||
|
table: "ServerImageVariables",
|
||||||
|
column: "ServerImageId");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_ServerImageVariables_ServerImages_ServerImageId",
|
||||||
|
table: "ServerImageVariables",
|
||||||
|
column: "ServerImageId",
|
||||||
|
principalTable: "ServerImages",
|
||||||
|
principalColumn: "Id");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_ServerImageVariables_ServerImages_ServerImageId",
|
||||||
|
table: "ServerImageVariables");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_ServerImageVariables_ServerImageId",
|
||||||
|
table: "ServerImageVariables");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ServerImageId",
|
||||||
|
table: "ServerImageVariables");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1043
Moonlight/Core/Database/Migrations/20240129160704_AddedAllowUserToChangeDockerImage.Designer.cs
generated
Normal file
1043
Moonlight/Core/Database/Migrations/20240129160704_AddedAllowUserToChangeDockerImage.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Moonlight.Core.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddedAllowUserToChangeDockerImage : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "AllowUserToChangeDockerImage",
|
||||||
|
table: "ServerImages",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "AllowUserToChangeDockerImage",
|
||||||
|
table: "ServerImages");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1089
Moonlight/Core/Database/Migrations/20240214091019_AddedServerSchedules.Designer.cs
generated
Normal file
1089
Moonlight/Core/Database/Migrations/20240214091019_AddedServerSchedules.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,51 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Moonlight.Core.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddedServerSchedules : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ServerSchedules",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
Name = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Cron = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
ActionType = table.Column<int>(type: "INTEGER", nullable: false),
|
||||||
|
ActionData = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
LastRunAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||||
|
WasLastRunAutomatic = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||||
|
ServerId = table.Column<int>(type: "INTEGER", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ServerSchedules", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ServerSchedules_Servers_ServerId",
|
||||||
|
column: x => x.ServerId,
|
||||||
|
principalTable: "Servers",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ServerSchedules_ServerId",
|
||||||
|
table: "ServerSchedules",
|
||||||
|
column: "ServerId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ServerSchedules");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,6 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
using Moonlight.Core.Database;
|
using Moonlight.Core.Database;
|
||||||
using Moonlight.Core.Database;
|
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
@@ -18,7 +17,52 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder.HasAnnotation("ProductVersion", "7.0.2");
|
modelBuilder.HasAnnotation("ProductVersion", "7.0.2");
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Community.Post", b =>
|
modelBuilder.Entity("Moonlight.Core.Database.Entities.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Avatar")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<double>("Balance")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Flags")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Password")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Permissions")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("TokenValidTimestamp")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("TotpKey")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Username")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Users");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.Community.Entities.Post", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -51,7 +95,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
b.ToTable("Posts");
|
b.ToTable("Posts");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Community.PostComment", b =>
|
modelBuilder.Entity("Moonlight.Features.Community.Entities.PostComment", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -82,7 +126,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
b.ToTable("PostComments");
|
b.ToTable("PostComments");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Community.PostLike", b =>
|
modelBuilder.Entity("Moonlight.Features.Community.Entities.PostLike", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -106,7 +150,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
b.ToTable("PostLikes");
|
b.ToTable("PostLikes");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Community.WordFilter", b =>
|
modelBuilder.Entity("Moonlight.Features.Community.Entities.WordFilter", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -121,7 +165,358 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
b.ToTable("WordFilters");
|
b.ToTable("WordFilters");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.Category", b =>
|
modelBuilder.Entity("Moonlight.Features.Servers.Entities.Server", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Cpu")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Disk")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("DockerImageIndex")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("ImageId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("MainAllocationId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Memory")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("NodeId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("OverrideStartupCommand")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("ServiceId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ImageId");
|
||||||
|
|
||||||
|
b.HasIndex("MainAllocationId");
|
||||||
|
|
||||||
|
b.HasIndex("NodeId");
|
||||||
|
|
||||||
|
b.HasIndex("ServiceId");
|
||||||
|
|
||||||
|
b.ToTable("Servers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerAllocation", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("Port")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("ServerId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("ServerNodeId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ServerId");
|
||||||
|
|
||||||
|
b.HasIndex("ServerNodeId");
|
||||||
|
|
||||||
|
b.ToTable("ServerAllocations");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerDockerImage", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("AutoPull")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("ServerImageId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ServerImageId");
|
||||||
|
|
||||||
|
b.ToTable("ServerDockerImages");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerImage", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("AllocationsNeeded")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("AllowUserToChangeDockerImage")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Author")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("DefaultDockerImageIndex")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("DonateUrl")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("InstallDockerImage")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("InstallScript")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("InstallShell")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("OnlineDetection")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("ParseConfigurations")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("StartupCommand")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("StopCommand")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("UpdateUrl")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ServerImages");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerImageVariable", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("AllowUserToEdit")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("AllowUserToView")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("DefaultValue")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("ServerImageId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ServerImageId");
|
||||||
|
|
||||||
|
b.ToTable("ServerImageVariables");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerNode", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Fqdn")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("FtpPort")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("HttpPort")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Token")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("UseSsl")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ServerNodes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerSchedule", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ActionData")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("ActionType")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Cron")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastRunAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("ServerId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("WasLastRunAutomatic")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ServerId");
|
||||||
|
|
||||||
|
b.ToTable("ServerSchedules");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerVariable", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int?>("ServerId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ServerId");
|
||||||
|
|
||||||
|
b.ToTable("ServerVariables");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.ServiceManagement.Entities.Service", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("ConfigJsonOverride")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Nickname")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("OwnerId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("ProductId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("RenewAt")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<bool>("Suspended")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("OwnerId");
|
||||||
|
|
||||||
|
b.HasIndex("ProductId");
|
||||||
|
|
||||||
|
b.ToTable("Services");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.ServiceManagement.Entities.ServiceShare", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("ServiceId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ServiceId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("ServiceShares");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.StoreSystem.Entities.Category", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -144,7 +539,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
b.ToTable("Categories");
|
b.ToTable("Categories");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.Coupon", b =>
|
modelBuilder.Entity("Moonlight.Features.StoreSystem.Entities.Coupon", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -165,7 +560,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
b.ToTable("Coupons");
|
b.ToTable("Coupons");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.CouponUse", b =>
|
modelBuilder.Entity("Moonlight.Features.StoreSystem.Entities.CouponUse", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -186,7 +581,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
b.ToTable("CouponUses");
|
b.ToTable("CouponUses");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.GiftCode", b =>
|
modelBuilder.Entity("Moonlight.Features.StoreSystem.Entities.GiftCode", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -207,7 +602,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
b.ToTable("GiftCodes");
|
b.ToTable("GiftCodes");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.GiftCodeUse", b =>
|
modelBuilder.Entity("Moonlight.Features.StoreSystem.Entities.GiftCodeUse", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -228,7 +623,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
b.ToTable("GiftCodeUses");
|
b.ToTable("GiftCodeUses");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.Product", b =>
|
modelBuilder.Entity("Moonlight.Features.StoreSystem.Entities.Product", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -278,64 +673,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
b.ToTable("Products");
|
b.ToTable("Products");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.Service", b =>
|
modelBuilder.Entity("Moonlight.Features.StoreSystem.Entities.Transaction", b =>
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<string>("ConfigJsonOverride")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<string>("Nickname")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<int>("OwnerId")
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<int>("ProductId")
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<DateTime>("RenewAt")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<bool>("Suspended")
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("OwnerId");
|
|
||||||
|
|
||||||
b.HasIndex("ProductId");
|
|
||||||
|
|
||||||
b.ToTable("Services");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.ServiceShare", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<int?>("ServiceId")
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<int>("UserId")
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("ServiceId");
|
|
||||||
|
|
||||||
b.HasIndex("UserId");
|
|
||||||
|
|
||||||
b.ToTable("ServiceShares");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.Transaction", b =>
|
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -361,7 +699,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
b.ToTable("Transaction");
|
b.ToTable("Transaction");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Theme", b =>
|
modelBuilder.Entity("Moonlight.Features.Theming.Entities.Theme", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -393,7 +731,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
b.ToTable("Themes");
|
b.ToTable("Themes");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Tickets.Ticket", b =>
|
modelBuilder.Entity("Moonlight.Features.Ticketing.Entities.Ticket", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -435,7 +773,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
b.ToTable("Tickets");
|
b.ToTable("Tickets");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Tickets.TicketMessage", b =>
|
modelBuilder.Entity("Moonlight.Features.Ticketing.Entities.TicketMessage", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -469,52 +807,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
b.ToTable("TicketMessages");
|
b.ToTable("TicketMessages");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.User", b =>
|
modelBuilder.Entity("Moonlight.Features.Community.Entities.Post", b =>
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<string>("Avatar")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<double>("Balance")
|
|
||||||
.HasColumnType("REAL");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<string>("Flags")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<string>("Password")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<int>("Permissions")
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<DateTime>("TokenValidTimestamp")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<string>("TotpKey")
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<string>("Username")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Users");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Community.Post", b =>
|
|
||||||
{
|
{
|
||||||
b.HasOne("Moonlight.Core.Database.Entities.User", "Author")
|
b.HasOne("Moonlight.Core.Database.Entities.User", "Author")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
@@ -525,7 +818,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
b.Navigation("Author");
|
b.Navigation("Author");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Community.PostComment", b =>
|
modelBuilder.Entity("Moonlight.Features.Community.Entities.PostComment", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Moonlight.Core.Database.Entities.User", "Author")
|
b.HasOne("Moonlight.Core.Database.Entities.User", "Author")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
@@ -533,16 +826,16 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
b.HasOne("Moonlight.Core.Database.Entities.Community.Post", null)
|
b.HasOne("Moonlight.Features.Community.Entities.Post", null)
|
||||||
.WithMany("Comments")
|
.WithMany("Comments")
|
||||||
.HasForeignKey("PostId");
|
.HasForeignKey("PostId");
|
||||||
|
|
||||||
b.Navigation("Author");
|
b.Navigation("Author");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Community.PostLike", b =>
|
modelBuilder.Entity("Moonlight.Features.Community.Entities.PostLike", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Moonlight.Core.Database.Entities.Community.Post", null)
|
b.HasOne("Moonlight.Features.Community.Entities.Post", null)
|
||||||
.WithMany("Likes")
|
.WithMany("Likes")
|
||||||
.HasForeignKey("PostId");
|
.HasForeignKey("PostId");
|
||||||
|
|
||||||
@@ -555,48 +848,81 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
b.Navigation("User");
|
b.Navigation("User");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.CouponUse", b =>
|
modelBuilder.Entity("Moonlight.Features.Servers.Entities.Server", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Moonlight.Core.Database.Entities.Store.Coupon", "Coupon")
|
b.HasOne("Moonlight.Features.Servers.Entities.ServerImage", "Image")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("CouponId")
|
.HasForeignKey("ImageId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
b.HasOne("Moonlight.Core.Database.Entities.User", null)
|
b.HasOne("Moonlight.Features.Servers.Entities.ServerAllocation", "MainAllocation")
|
||||||
.WithMany("CouponUses")
|
|
||||||
.HasForeignKey("UserId");
|
|
||||||
|
|
||||||
b.Navigation("Coupon");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.GiftCodeUse", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Moonlight.Core.Database.Entities.Store.GiftCode", "GiftCode")
|
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("GiftCodeId")
|
.HasForeignKey("MainAllocationId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
b.HasOne("Moonlight.Core.Database.Entities.User", null)
|
b.HasOne("Moonlight.Features.Servers.Entities.ServerNode", "Node")
|
||||||
.WithMany("GiftCodeUses")
|
|
||||||
.HasForeignKey("UserId");
|
|
||||||
|
|
||||||
b.Navigation("GiftCode");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.Product", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Moonlight.Core.Database.Entities.Store.Category", "Category")
|
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("CategoryId")
|
.HasForeignKey("NodeId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
b.Navigation("Category");
|
b.HasOne("Moonlight.Features.ServiceManagement.Entities.Service", "Service")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ServiceId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Image");
|
||||||
|
|
||||||
|
b.Navigation("MainAllocation");
|
||||||
|
|
||||||
|
b.Navigation("Node");
|
||||||
|
|
||||||
|
b.Navigation("Service");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.Service", b =>
|
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerAllocation", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Moonlight.Features.Servers.Entities.Server", null)
|
||||||
|
.WithMany("Allocations")
|
||||||
|
.HasForeignKey("ServerId");
|
||||||
|
|
||||||
|
b.HasOne("Moonlight.Features.Servers.Entities.ServerNode", null)
|
||||||
|
.WithMany("Allocations")
|
||||||
|
.HasForeignKey("ServerNodeId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerDockerImage", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Moonlight.Features.Servers.Entities.ServerImage", null)
|
||||||
|
.WithMany("DockerImages")
|
||||||
|
.HasForeignKey("ServerImageId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerImageVariable", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Moonlight.Features.Servers.Entities.ServerImage", null)
|
||||||
|
.WithMany("Variables")
|
||||||
|
.HasForeignKey("ServerImageId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerSchedule", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Moonlight.Features.Servers.Entities.Server", null)
|
||||||
|
.WithMany("Schedules")
|
||||||
|
.HasForeignKey("ServerId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerVariable", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Moonlight.Features.Servers.Entities.Server", null)
|
||||||
|
.WithMany("Variables")
|
||||||
|
.HasForeignKey("ServerId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.ServiceManagement.Entities.Service", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Moonlight.Core.Database.Entities.User", "Owner")
|
b.HasOne("Moonlight.Core.Database.Entities.User", "Owner")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
@@ -604,7 +930,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
b.HasOne("Moonlight.Core.Database.Entities.Store.Product", "Product")
|
b.HasOne("Moonlight.Features.StoreSystem.Entities.Product", "Product")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("ProductId")
|
.HasForeignKey("ProductId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -615,9 +941,9 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
b.Navigation("Product");
|
b.Navigation("Product");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.ServiceShare", b =>
|
modelBuilder.Entity("Moonlight.Features.ServiceManagement.Entities.ServiceShare", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Moonlight.Core.Database.Entities.Store.Service", null)
|
b.HasOne("Moonlight.Features.ServiceManagement.Entities.Service", null)
|
||||||
.WithMany("Shares")
|
.WithMany("Shares")
|
||||||
.HasForeignKey("ServiceId");
|
.HasForeignKey("ServiceId");
|
||||||
|
|
||||||
@@ -630,14 +956,55 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
b.Navigation("User");
|
b.Navigation("User");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.Transaction", b =>
|
modelBuilder.Entity("Moonlight.Features.StoreSystem.Entities.CouponUse", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Moonlight.Features.StoreSystem.Entities.Coupon", "Coupon")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CouponId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Moonlight.Core.Database.Entities.User", null)
|
||||||
|
.WithMany("CouponUses")
|
||||||
|
.HasForeignKey("UserId");
|
||||||
|
|
||||||
|
b.Navigation("Coupon");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.StoreSystem.Entities.GiftCodeUse", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Moonlight.Features.StoreSystem.Entities.GiftCode", "GiftCode")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("GiftCodeId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Moonlight.Core.Database.Entities.User", null)
|
||||||
|
.WithMany("GiftCodeUses")
|
||||||
|
.HasForeignKey("UserId");
|
||||||
|
|
||||||
|
b.Navigation("GiftCode");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.StoreSystem.Entities.Product", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Moonlight.Features.StoreSystem.Entities.Category", "Category")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("CategoryId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Category");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.StoreSystem.Entities.Transaction", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Moonlight.Core.Database.Entities.User", null)
|
b.HasOne("Moonlight.Core.Database.Entities.User", null)
|
||||||
.WithMany("Transactions")
|
.WithMany("Transactions")
|
||||||
.HasForeignKey("UserId");
|
.HasForeignKey("UserId");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Tickets.Ticket", b =>
|
modelBuilder.Entity("Moonlight.Features.Ticketing.Entities.Ticket", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Moonlight.Core.Database.Entities.User", "Creator")
|
b.HasOne("Moonlight.Core.Database.Entities.User", "Creator")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
@@ -645,7 +1012,7 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
b.HasOne("Moonlight.Core.Database.Entities.Store.Service", "Service")
|
b.HasOne("Moonlight.Features.ServiceManagement.Entities.Service", "Service")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("ServiceId");
|
.HasForeignKey("ServiceId");
|
||||||
|
|
||||||
@@ -654,36 +1021,19 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
b.Navigation("Service");
|
b.Navigation("Service");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Tickets.TicketMessage", b =>
|
modelBuilder.Entity("Moonlight.Features.Ticketing.Entities.TicketMessage", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Moonlight.Core.Database.Entities.User", "Sender")
|
b.HasOne("Moonlight.Core.Database.Entities.User", "Sender")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("SenderId");
|
.HasForeignKey("SenderId");
|
||||||
|
|
||||||
b.HasOne("Moonlight.Core.Database.Entities.Tickets.Ticket", null)
|
b.HasOne("Moonlight.Features.Ticketing.Entities.Ticket", null)
|
||||||
.WithMany("Messages")
|
.WithMany("Messages")
|
||||||
.HasForeignKey("TicketId");
|
.HasForeignKey("TicketId");
|
||||||
|
|
||||||
b.Navigation("Sender");
|
b.Navigation("Sender");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Community.Post", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Comments");
|
|
||||||
|
|
||||||
b.Navigation("Likes");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Store.Service", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Shares");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.Tickets.Ticket", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Messages");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Core.Database.Entities.User", b =>
|
modelBuilder.Entity("Moonlight.Core.Database.Entities.User", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("CouponUses");
|
b.Navigation("CouponUses");
|
||||||
@@ -692,6 +1042,44 @@ namespace Moonlight.Core.Database.Migrations
|
|||||||
|
|
||||||
b.Navigation("Transactions");
|
b.Navigation("Transactions");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.Community.Entities.Post", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Comments");
|
||||||
|
|
||||||
|
b.Navigation("Likes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.Servers.Entities.Server", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Allocations");
|
||||||
|
|
||||||
|
b.Navigation("Schedules");
|
||||||
|
|
||||||
|
b.Navigation("Variables");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerImage", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("DockerImages");
|
||||||
|
|
||||||
|
b.Navigation("Variables");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.Servers.Entities.ServerNode", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Allocations");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.ServiceManagement.Entities.Service", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Shares");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.Features.Ticketing.Entities.Ticket", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Messages");
|
||||||
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
namespace Moonlight.Core.Exceptions;
|
|
||||||
|
|
||||||
public class DisplayException : Exception
|
|
||||||
{
|
|
||||||
public DisplayException()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public DisplayException(string message) : base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public DisplayException(string message, Exception inner) : base(message, inner)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace Moonlight.Core.Extensions.Attributes;
|
|
||||||
|
|
||||||
public class SelectorAttribute : Attribute
|
|
||||||
{
|
|
||||||
public string SelectorProp { get; set; } = "";
|
|
||||||
public string DisplayProp { get; set; } = "";
|
|
||||||
public bool UseDropdown { get; set; } = false;
|
|
||||||
}
|
|
||||||
22
Moonlight/Core/Extensions/ConfigServiceExtensions.cs
Normal file
22
Moonlight/Core/Extensions/ConfigServiceExtensions.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using MoonCore.Services;
|
||||||
|
using Moonlight.Core.Configuration;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Moonlight.Core.Extensions;
|
||||||
|
|
||||||
|
public static class ConfigServiceExtensions
|
||||||
|
{
|
||||||
|
public static string GetDiagnosticJson(this ConfigService<ConfigV1> configService)
|
||||||
|
{
|
||||||
|
var jsonUnsafe = JsonConvert.SerializeObject(configService.Get());
|
||||||
|
var configUnsafe = JsonConvert.DeserializeObject<ConfigV1>(jsonUnsafe)!;
|
||||||
|
|
||||||
|
// Remote sensitive data
|
||||||
|
configUnsafe.Database.Password =
|
||||||
|
string.IsNullOrEmpty(configUnsafe.Database.Password) ? "IS EMPTY" : "IS NOT EMPTY";
|
||||||
|
configUnsafe.Security.Token = string.IsNullOrEmpty(configUnsafe.Security.Token) ? "IS EMPTY" : "IS NOT EMPTY";
|
||||||
|
configUnsafe.MailServer.Password =string.IsNullOrEmpty(configUnsafe.MailServer.Password) ? "IS EMPTY" : "IS NOT EMPTY";
|
||||||
|
|
||||||
|
return JsonConvert.SerializeObject(configUnsafe, Formatting.Indented);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Moonlight.Core.Extensions;
|
|
||||||
|
|
||||||
public static class ConfigurationBuilderExtensions
|
|
||||||
{
|
|
||||||
public static IConfigurationBuilder AddJsonString(this IConfigurationBuilder configurationBuilder, string json)
|
|
||||||
{
|
|
||||||
var bytes = Encoding.UTF8.GetBytes(json);
|
|
||||||
var stream = new MemoryStream(bytes);
|
|
||||||
return configurationBuilder.AddJsonStream(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Components;
|
|
||||||
|
|
||||||
namespace Moonlight.Core.Helpers;
|
|
||||||
|
|
||||||
public static class ComponentHelper
|
|
||||||
{
|
|
||||||
public static RenderFragment FromType(Type type, Action<Dictionary<string, object>>? buildAttributes = null) => builder =>
|
|
||||||
{
|
|
||||||
builder.OpenComponent(0, type);
|
|
||||||
|
|
||||||
if (buildAttributes != null)
|
|
||||||
{
|
|
||||||
Dictionary<string, object> parameters = new();
|
|
||||||
buildAttributes.Invoke(parameters);
|
|
||||||
builder.AddMultipleAttributes(1, parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.CloseComponent();
|
|
||||||
};
|
|
||||||
|
|
||||||
public static RenderFragment FromType<T>(Action<Dictionary<string, object>>? buildAttributes = null) where T : ComponentBase =>
|
|
||||||
FromType(typeof(T), buildAttributes);
|
|
||||||
}
|
|
||||||
@@ -1,280 +0,0 @@
|
|||||||
using System.Text;
|
|
||||||
using Microsoft.AspNetCore.Components;
|
|
||||||
|
|
||||||
namespace Moonlight.Core.Helpers;
|
|
||||||
|
|
||||||
public static class Formatter
|
|
||||||
{
|
|
||||||
public static string GenerateString(int length)
|
|
||||||
{
|
|
||||||
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
||||||
var stringBuilder = new StringBuilder();
|
|
||||||
var random = new Random();
|
|
||||||
|
|
||||||
for (int i = 0; i < length; i++)
|
|
||||||
{
|
|
||||||
stringBuilder.Append(chars[random.Next(chars.Length)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return stringBuilder.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string IntToStringWithLeadingZeros(int number, int n)
|
|
||||||
{
|
|
||||||
string result = number.ToString();
|
|
||||||
int length = result.Length;
|
|
||||||
|
|
||||||
for (int i = length; i < n; i++)
|
|
||||||
{
|
|
||||||
result = "0" + result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string CapitalizeFirstCharacter(string input)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(input))
|
|
||||||
{
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
char firstChar = char.ToUpper(input[0]);
|
|
||||||
string restOfString = input.Substring(1);
|
|
||||||
|
|
||||||
return firstChar + restOfString;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string CutInHalf(string input)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(input))
|
|
||||||
return input;
|
|
||||||
|
|
||||||
int length = input.Length;
|
|
||||||
int halfLength = length / 2;
|
|
||||||
|
|
||||||
return input.Substring(0, halfLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool EndsInOneOf(string suffix, IEnumerable<string> strings)
|
|
||||||
{
|
|
||||||
foreach (string str in strings)
|
|
||||||
{
|
|
||||||
if (suffix.EndsWith(str))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ContainsOneOf(string textToSearch, IEnumerable<string> strings, out string foundText)
|
|
||||||
{
|
|
||||||
foreach (string str in strings)
|
|
||||||
{
|
|
||||||
if (textToSearch.Contains(str))
|
|
||||||
{
|
|
||||||
foundText = str;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foundText = "";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ContainsOneOf(string textToSearch, IEnumerable<string> strings)
|
|
||||||
{
|
|
||||||
return ContainsOneOf(textToSearch, strings, out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string FormatSize(long bytes)
|
|
||||||
{
|
|
||||||
var i = Math.Abs(bytes) / 1024D;
|
|
||||||
if (i < 1)
|
|
||||||
{
|
|
||||||
return bytes + " B";
|
|
||||||
}
|
|
||||||
else if (i / 1024D < 1)
|
|
||||||
{
|
|
||||||
return i.Round(2) + " KB";
|
|
||||||
}
|
|
||||||
else if (i / (1024D * 1024D) < 1)
|
|
||||||
{
|
|
||||||
return (i / 1024D).Round(2) + " MB";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return (i / (1024D * 1024D)).Round(2) + " GB";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double Round(this double d, int decimals)
|
|
||||||
{
|
|
||||||
return Math.Round(d, decimals);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string ReplaceEnd(string input, string substringToReplace, string newSubstring)
|
|
||||||
{
|
|
||||||
int lastIndexOfSubstring = input.LastIndexOf(substringToReplace);
|
|
||||||
if (lastIndexOfSubstring >= 0)
|
|
||||||
{
|
|
||||||
input = input.Remove(lastIndexOfSubstring, substringToReplace.Length)
|
|
||||||
.Insert(lastIndexOfSubstring, newSubstring);
|
|
||||||
}
|
|
||||||
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string ConvertCamelCaseToSpaces(string input)
|
|
||||||
{
|
|
||||||
StringBuilder output = new StringBuilder();
|
|
||||||
|
|
||||||
foreach (char c in input)
|
|
||||||
{
|
|
||||||
if (char.IsUpper(c))
|
|
||||||
{
|
|
||||||
output.Append(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
output.Append(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
return output.ToString().Trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string FormatUptime(double uptime)
|
|
||||||
{
|
|
||||||
TimeSpan t = TimeSpan.FromMilliseconds(uptime);
|
|
||||||
|
|
||||||
return FormatUptime(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string FormatUptime(TimeSpan t)
|
|
||||||
{
|
|
||||||
if (t.Days > 0)
|
|
||||||
{
|
|
||||||
return $"{t.Days}d {t.Hours}h {t.Minutes}m {t.Seconds}s";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return $"{t.Hours}h {t.Minutes}m {t.Seconds}s";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string FormatDate(DateTime e)
|
|
||||||
{
|
|
||||||
string i2s(int i)
|
|
||||||
{
|
|
||||||
if (i.ToString().Length < 2)
|
|
||||||
return "0" + i;
|
|
||||||
return i.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $"{i2s(e.Day)}.{i2s(e.Month)}.{e.Year} {i2s(e.Hour)}:{i2s(e.Minute)}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string FormatDateOnly(DateTime e)
|
|
||||||
{
|
|
||||||
string i2s(int i)
|
|
||||||
{
|
|
||||||
if (i.ToString().Length < 2)
|
|
||||||
return "0" + i;
|
|
||||||
return i.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $"{i2s(e.Day)}.{i2s(e.Month)}.{e.Year}";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string FormatSize(double bytes)
|
|
||||||
{
|
|
||||||
var i = Math.Abs(bytes) / 1024D;
|
|
||||||
if (i < 1)
|
|
||||||
{
|
|
||||||
return bytes + " B";
|
|
||||||
}
|
|
||||||
else if (i / 1024D < 1)
|
|
||||||
{
|
|
||||||
return i.Round(2) + " KB";
|
|
||||||
}
|
|
||||||
else if (i / (1024D * 1024D) < 1)
|
|
||||||
{
|
|
||||||
return (i / 1024D).Round(2) + " MB";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return (i / (1024D * 1024D)).Round(2) + " GB";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RenderFragment FormatLineBreaks(string content)
|
|
||||||
{
|
|
||||||
return builder =>
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
var arr = content.Split("\n");
|
|
||||||
|
|
||||||
foreach (var line in arr)
|
|
||||||
{
|
|
||||||
builder.AddContent(i, line);
|
|
||||||
if (i++ != arr.Length - 1)
|
|
||||||
{
|
|
||||||
builder.AddMarkupContent(i, "<br/>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string FormatAgoFromDateTime(DateTime dt)
|
|
||||||
{
|
|
||||||
TimeSpan timeSince = DateTime.UtcNow.Subtract(dt);
|
|
||||||
|
|
||||||
if (timeSince.TotalMilliseconds < 1)
|
|
||||||
return "just now";
|
|
||||||
|
|
||||||
if (timeSince.TotalMinutes < 1)
|
|
||||||
return "less than a minute ago";
|
|
||||||
|
|
||||||
if (timeSince.TotalMinutes < 2)
|
|
||||||
return "1 minute ago";
|
|
||||||
|
|
||||||
if (timeSince.TotalMinutes < 60)
|
|
||||||
return Math.Round(timeSince.TotalMinutes) + " minutes ago";
|
|
||||||
|
|
||||||
if (timeSince.TotalHours < 2)
|
|
||||||
return "1 hour ago";
|
|
||||||
|
|
||||||
if (timeSince.TotalHours < 24)
|
|
||||||
return Math.Round(timeSince.TotalHours) + " hours ago";
|
|
||||||
|
|
||||||
if (timeSince.TotalDays < 2)
|
|
||||||
return "1 day ago";
|
|
||||||
|
|
||||||
return Math.Round(timeSince.TotalDays) + " days ago";
|
|
||||||
}
|
|
||||||
|
|
||||||
// This will replace every placeholder with the respective value if specified in the model
|
|
||||||
// For example:
|
|
||||||
// A instance of the user model has been passed in the 'models' parameter of the function.
|
|
||||||
// So the placeholder {{User.Email}} will be replaced by the value of the Email property of the model
|
|
||||||
public static string ProcessTemplating(string text, params object[] models)
|
|
||||||
{
|
|
||||||
foreach (var model in models)
|
|
||||||
{
|
|
||||||
foreach (var property in model.GetType().GetProperties())
|
|
||||||
{
|
|
||||||
var value = property.GetValue(model);
|
|
||||||
|
|
||||||
if(value == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var placeholder = "{{" + $"{model.GetType().Name}.{property.Name}" + "}}";
|
|
||||||
|
|
||||||
text = text.Replace(placeholder, value.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
using System.Security.Cryptography;
|
|
||||||
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
|
|
||||||
|
|
||||||
namespace Moonlight.Core.Helpers;
|
|
||||||
|
|
||||||
// Src: https://codereview.stackexchange.com/questions/176697/net-core-mvc-future-proof-hashing-of-passwords
|
|
||||||
public static class HashHelper
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The default number of Iterations
|
|
||||||
/// </summary>
|
|
||||||
private const int DefaultIterations = 10000;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Provides Information about a specific Hash Version
|
|
||||||
/// </summary>
|
|
||||||
private class HashVersion
|
|
||||||
{
|
|
||||||
public short Version { get; set; }
|
|
||||||
public int SaltSize { get; set; }
|
|
||||||
public int HashSize { get; set; }
|
|
||||||
public KeyDerivationPrf KeyDerivation { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Holds all possible Hash Versions
|
|
||||||
/// </summary>
|
|
||||||
private static readonly Dictionary<short, HashVersion> _versions = new Dictionary<short, HashVersion>
|
|
||||||
{
|
|
||||||
{
|
|
||||||
1, new HashVersion
|
|
||||||
{
|
|
||||||
Version = 1,
|
|
||||||
KeyDerivation = KeyDerivationPrf.HMACSHA512,
|
|
||||||
HashSize = 256 / 8,
|
|
||||||
SaltSize = 128 / 8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The default Hash Version, which should be used, if a new Hash is Created
|
|
||||||
/// </summary>
|
|
||||||
private static HashVersion DefaultVersion => _versions[1];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if a given hash uses the latest version
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="data">The hash</param>
|
|
||||||
/// <returns>Is the hash of the latest version?</returns>
|
|
||||||
public static bool IsLatestHashVersion(byte[] data)
|
|
||||||
{
|
|
||||||
var version = BitConverter.ToInt16(data, 0);
|
|
||||||
return version == DefaultVersion.Version;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if a given hash uses the latest version
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="data">The hash</param>
|
|
||||||
/// <returns>Is the hash of the latest version?</returns>
|
|
||||||
public static bool IsLatestHashVersion(string data)
|
|
||||||
{
|
|
||||||
var dataBytes = Convert.FromBase64String(data);
|
|
||||||
return IsLatestHashVersion(dataBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a random byte array
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="length">The length of the byte array</param>
|
|
||||||
/// <returns>The random byte array</returns>
|
|
||||||
public static byte[] GetRandomBytes(int length)
|
|
||||||
{
|
|
||||||
var data = new byte[length];
|
|
||||||
using (var randomNumberGenerator = RandomNumberGenerator.Create())
|
|
||||||
{
|
|
||||||
randomNumberGenerator.GetBytes(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a Hash of a clear text
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="clearText">the clear text</param>
|
|
||||||
/// <param name="iterations">the number of iteration the hash alogrythm should run</param>
|
|
||||||
/// <returns>the Hash</returns>
|
|
||||||
public static byte[] Hash(string clearText, int iterations = DefaultIterations)
|
|
||||||
{
|
|
||||||
//get current version
|
|
||||||
var currentVersion = DefaultVersion;
|
|
||||||
|
|
||||||
//get the byte arrays of the hash and meta information
|
|
||||||
var saltBytes = GetRandomBytes(currentVersion.SaltSize);
|
|
||||||
var versionBytes = BitConverter.GetBytes(currentVersion.Version);
|
|
||||||
var iterationBytes = BitConverter.GetBytes(iterations);
|
|
||||||
var hashBytes = KeyDerivation.Pbkdf2(clearText, saltBytes, currentVersion.KeyDerivation, iterations,
|
|
||||||
currentVersion.HashSize);
|
|
||||||
|
|
||||||
//calculate the indexes for the combined hash
|
|
||||||
var indexVersion = 0;
|
|
||||||
var indexIteration = indexVersion + 2;
|
|
||||||
var indexSalt = indexIteration + 4;
|
|
||||||
var indexHash = indexSalt + currentVersion.SaltSize;
|
|
||||||
|
|
||||||
//combine all data to one result hash
|
|
||||||
var resultBytes = new byte[2 + 4 + currentVersion.SaltSize + currentVersion.HashSize];
|
|
||||||
Array.Copy(versionBytes, 0, resultBytes, indexVersion, 2);
|
|
||||||
Array.Copy(iterationBytes, 0, resultBytes, indexIteration, 4);
|
|
||||||
Array.Copy(saltBytes, 0, resultBytes, indexSalt, currentVersion.SaltSize);
|
|
||||||
Array.Copy(hashBytes, 0, resultBytes, indexHash, currentVersion.HashSize);
|
|
||||||
return resultBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a Hash of a clear text and convert it to a Base64 String representation
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="clearText">the clear text</param>
|
|
||||||
/// <param name="iterations">the number of iteration the hash alogrythm should run</param>
|
|
||||||
/// <returns>the Hash</returns>
|
|
||||||
public static string HashToString(string clearText, int iterations = DefaultIterations)
|
|
||||||
{
|
|
||||||
var data = Hash(clearText, iterations);
|
|
||||||
return Convert.ToBase64String(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Verifies a given clear Text against a hash
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="clearText">The clear text</param>
|
|
||||||
/// <param name="data">The hash</param>
|
|
||||||
/// <returns>Is the hash equal to the clear text?</returns>
|
|
||||||
public static bool Verify(string clearText, byte[] data)
|
|
||||||
{
|
|
||||||
//Get the current version and number of iterations
|
|
||||||
var currentVersion = _versions[BitConverter.ToInt16(data, 0)];
|
|
||||||
var iteration = BitConverter.ToInt32(data, 2);
|
|
||||||
|
|
||||||
//Create the byte arrays for the salt and hash
|
|
||||||
var saltBytes = new byte[currentVersion.SaltSize];
|
|
||||||
var hashBytes = new byte[currentVersion.HashSize];
|
|
||||||
|
|
||||||
//Calculate the indexes of the salt and the hash
|
|
||||||
var indexSalt = 2 + 4; // Int16 (Version) and Int32 (Iteration)
|
|
||||||
var indexHash = indexSalt + currentVersion.SaltSize;
|
|
||||||
|
|
||||||
//Fill the byte arrays with salt and hash
|
|
||||||
Array.Copy(data, indexSalt, saltBytes, 0, currentVersion.SaltSize);
|
|
||||||
Array.Copy(data, indexHash, hashBytes, 0, currentVersion.HashSize);
|
|
||||||
|
|
||||||
//Hash the current clearText with the parameters given via the data
|
|
||||||
var verificationHashBytes = KeyDerivation.Pbkdf2(clearText, saltBytes, currentVersion.KeyDerivation, iteration,
|
|
||||||
currentVersion.HashSize);
|
|
||||||
|
|
||||||
//Check if generated hashes are equal
|
|
||||||
return hashBytes.SequenceEqual(verificationHashBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Verifies a given clear Text against a hash
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="clearText">The clear text</param>
|
|
||||||
/// <param name="data">The hash</param>
|
|
||||||
/// <returns>Is the hash equal to the clear text?</returns>
|
|
||||||
public static bool Verify(string clearText, string data)
|
|
||||||
{
|
|
||||||
var dataBytes = Convert.FromBase64String(data);
|
|
||||||
return Verify(clearText, dataBytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
namespace Moonlight.Core.Helpers.LogMigrator;
|
|
||||||
|
|
||||||
public class LogMigrateProvider : ILoggerProvider
|
|
||||||
{
|
|
||||||
public void Dispose() {}
|
|
||||||
|
|
||||||
public ILogger CreateLogger(string categoryName)
|
|
||||||
{
|
|
||||||
return new MigrateLogger();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
namespace Moonlight.Core.Helpers.LogMigrator;
|
|
||||||
|
|
||||||
public class MigrateLogger : ILogger
|
|
||||||
{
|
|
||||||
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;
|
|
||||||
|
|
||||||
public bool IsEnabled(LogLevel logLevel)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
|
|
||||||
{
|
|
||||||
switch (logLevel)
|
|
||||||
{
|
|
||||||
case LogLevel.Critical:
|
|
||||||
Logger.Fatal(formatter(state, exception));
|
|
||||||
|
|
||||||
if(exception != null)
|
|
||||||
Logger.Fatal(exception);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case LogLevel.Warning:
|
|
||||||
Logger.Warn(formatter(state, exception));
|
|
||||||
|
|
||||||
if(exception != null)
|
|
||||||
Logger.Warn(exception);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case LogLevel.Debug:
|
|
||||||
Logger.Debug(formatter(state, exception));
|
|
||||||
|
|
||||||
if(exception != null)
|
|
||||||
Logger.Debug(exception);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case LogLevel.Error:
|
|
||||||
Logger.Error(formatter(state, exception));
|
|
||||||
|
|
||||||
if(exception != null)
|
|
||||||
Logger.Error(exception);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case LogLevel.Information:
|
|
||||||
Logger.Info(formatter(state, exception));
|
|
||||||
|
|
||||||
if(exception != null)
|
|
||||||
Logger.Info(exception);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using System.Reflection;
|
|
||||||
using Serilog;
|
|
||||||
|
|
||||||
namespace Moonlight.Core.Helpers;
|
|
||||||
|
|
||||||
public class Logger
|
|
||||||
{
|
|
||||||
#region String logger
|
|
||||||
public static void Verbose(string message, string channel = "default")
|
|
||||||
{
|
|
||||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
|
||||||
.Verbose("{Message}", message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Info(string message, string channel = "default")
|
|
||||||
{
|
|
||||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
|
||||||
.Information("{Message}", message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Debug(string message, string channel = "default")
|
|
||||||
{
|
|
||||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
|
||||||
.Debug("{Message}", message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Error(string message, string channel = "default")
|
|
||||||
{
|
|
||||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
|
||||||
.Error("{Message}", message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Warn(string message, string channel = "default")
|
|
||||||
{
|
|
||||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
|
||||||
.Warning("{Message}", message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Fatal(string message, string channel = "default")
|
|
||||||
{
|
|
||||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
|
||||||
.Fatal("{Message}", message);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Exception method calls
|
|
||||||
|
|
||||||
public static void Verbose(Exception exception, string channel = "default")
|
|
||||||
{
|
|
||||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
|
||||||
.Verbose(exception, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Info(Exception exception, string channel = "default")
|
|
||||||
{
|
|
||||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
|
||||||
.Information(exception, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Debug(Exception exception, string channel = "default")
|
|
||||||
{
|
|
||||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
|
||||||
.Debug(exception, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Error(Exception exception, string channel = "default")
|
|
||||||
{
|
|
||||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
|
||||||
.Error(exception, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Warn(Exception exception, string channel = "default")
|
|
||||||
{
|
|
||||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
|
||||||
.Warning(exception, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Fatal(Exception exception, string channel = "default")
|
|
||||||
{
|
|
||||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
|
||||||
.Fatal(exception, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private static string GetNameOfCallingClass(int skipFrames = 4)
|
|
||||||
{
|
|
||||||
string fullName;
|
|
||||||
Type declaringType;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
MethodBase method = new StackFrame(skipFrames, false).GetMethod();
|
|
||||||
declaringType = method.DeclaringType;
|
|
||||||
if (declaringType == null)
|
|
||||||
{
|
|
||||||
return method.Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
skipFrames++;
|
|
||||||
if (declaringType.Name.Contains("<"))
|
|
||||||
fullName = declaringType.ReflectedType.Name;
|
|
||||||
else
|
|
||||||
fullName = declaringType.Name;
|
|
||||||
} while (declaringType.Module.Name.Equals("mscorlib.dll", StringComparison.OrdinalIgnoreCase) |
|
|
||||||
fullName.Contains("Logger"));
|
|
||||||
|
|
||||||
return fullName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
namespace Moonlight.Core.Helpers;
|
|
||||||
|
|
||||||
public static class PathBuilder
|
|
||||||
{
|
|
||||||
public static string Dir(params string[] parts)
|
|
||||||
{
|
|
||||||
var res = "";
|
|
||||||
|
|
||||||
foreach (var part in parts)
|
|
||||||
{
|
|
||||||
res += part + Path.DirectorySeparatorChar;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.Replace(
|
|
||||||
$"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}",
|
|
||||||
$"{Path.DirectorySeparatorChar}"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string File(params string[] parts)
|
|
||||||
{
|
|
||||||
var res = "";
|
|
||||||
|
|
||||||
foreach (var part in parts)
|
|
||||||
{
|
|
||||||
res += part + (part == parts.Last() ? "" : Path.DirectorySeparatorChar);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.Replace(
|
|
||||||
$"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}",
|
|
||||||
$"{Path.DirectorySeparatorChar}"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Moonlight.Core.Helpers;
|
|
||||||
|
|
||||||
public class PropBinder<T>
|
|
||||||
{
|
|
||||||
private PropertyInfo PropertyInfo;
|
|
||||||
private object DataObject;
|
|
||||||
|
|
||||||
public PropBinder(PropertyInfo propertyInfo, object dataObject)
|
|
||||||
{
|
|
||||||
PropertyInfo = propertyInfo;
|
|
||||||
DataObject = dataObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string StringValue
|
|
||||||
{
|
|
||||||
get => (string)PropertyInfo.GetValue(DataObject)!;
|
|
||||||
set => PropertyInfo.SetValue(DataObject, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int IntValue
|
|
||||||
{
|
|
||||||
get => (int)PropertyInfo.GetValue(DataObject)!;
|
|
||||||
set => PropertyInfo.SetValue(DataObject, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long LongValue
|
|
||||||
{
|
|
||||||
get => (long)PropertyInfo.GetValue(DataObject)!;
|
|
||||||
set => PropertyInfo.SetValue(DataObject, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool BoolValue
|
|
||||||
{
|
|
||||||
get => (bool)PropertyInfo.GetValue(DataObject)!;
|
|
||||||
set => PropertyInfo.SetValue(DataObject, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DateTime DateTimeValue
|
|
||||||
{
|
|
||||||
get => (DateTime)PropertyInfo.GetValue(DataObject)!;
|
|
||||||
set => PropertyInfo.SetValue(DataObject, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Class
|
|
||||||
{
|
|
||||||
get => (T)PropertyInfo.GetValue(DataObject)!;
|
|
||||||
set => PropertyInfo.SetValue(DataObject, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double DoubleValue
|
|
||||||
{
|
|
||||||
get => (double)PropertyInfo.GetValue(DataObject)!;
|
|
||||||
set => PropertyInfo.SetValue(DataObject, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Moonlight.Core.Helpers;
|
using MoonCore.Helpers;
|
||||||
using Moonlight.Features.Theming.Services;
|
using Moonlight.Features.Theming.Services;
|
||||||
|
|
||||||
namespace Moonlight.Core.Http.Controllers.Api;
|
namespace Moonlight.Core.Http.Controllers.Api;
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using MoonCore.Abstractions;
|
||||||
using Moonlight.Core.Database.Entities;
|
using Moonlight.Core.Database.Entities;
|
||||||
using Moonlight.Core.Models.Enums;
|
using Moonlight.Core.Models.Enums;
|
||||||
using Moonlight.Core.Repositories;
|
|
||||||
using Moonlight.Core.Services;
|
using Moonlight.Core.Services;
|
||||||
using Moonlight.Core.Services.Utils;
|
using Moonlight.Core.Services.Utils;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Moonlight.Core.Helpers;
|
using MoonCore.Helpers;
|
||||||
using Moonlight.Core.Models.Enums;
|
using Moonlight.Core.Models.Enums;
|
||||||
using Moonlight.Core.Services;
|
using Moonlight.Core.Services;
|
||||||
using Moonlight.Core.Services.Utils;
|
using Moonlight.Core.Services.Utils;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Moonlight.Core.Helpers;
|
using MoonCore.Helpers;
|
||||||
using Moonlight.Core.Services;
|
using Moonlight.Core.Services;
|
||||||
|
|
||||||
namespace Moonlight.Core.Http.Controllers.Api;
|
namespace Moonlight.Core.Http.Controllers.Api;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ public enum Permission
|
|||||||
AdminTickets = 1004,
|
AdminTickets = 1004,
|
||||||
AdminCommunity = 1030,
|
AdminCommunity = 1030,
|
||||||
AdminServices = 1050,
|
AdminServices = 1050,
|
||||||
|
AdminServers = 1060,
|
||||||
AdminStore = 1900,
|
AdminStore = 1900,
|
||||||
AdminViewExceptions = 1999,
|
AdminViewExceptions = 1999,
|
||||||
AdminRoot = 2000
|
AdminRoot = 2000
|
||||||
|
|||||||
@@ -1,38 +1,41 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using MoonCore.Abstractions;
|
||||||
|
using MoonCore.Attributes;
|
||||||
using Moonlight.Core.Database;
|
using Moonlight.Core.Database;
|
||||||
|
|
||||||
namespace Moonlight.Core.Repositories;
|
namespace Moonlight.Core.Repositories;
|
||||||
|
|
||||||
public class Repository<TEntity> where TEntity : class
|
[Scoped]
|
||||||
|
public class GenericRepository<TEntity> : Repository<TEntity> where TEntity : class
|
||||||
{
|
{
|
||||||
private readonly DataContext DataContext;
|
private readonly DataContext DataContext;
|
||||||
private readonly DbSet<TEntity> DbSet;
|
private readonly DbSet<TEntity> DbSet;
|
||||||
|
|
||||||
public Repository(DataContext dbContext)
|
public GenericRepository(DataContext dbContext)
|
||||||
{
|
{
|
||||||
DataContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
|
DataContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
|
||||||
DbSet = DataContext.Set<TEntity>();
|
DbSet = DataContext.Set<TEntity>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DbSet<TEntity> Get()
|
public override DbSet<TEntity> Get()
|
||||||
{
|
{
|
||||||
return DbSet;
|
return DbSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TEntity Add(TEntity entity)
|
public override TEntity Add(TEntity entity)
|
||||||
{
|
{
|
||||||
var x = DbSet.Add(entity);
|
var x = DbSet.Add(entity);
|
||||||
DataContext.SaveChanges();
|
DataContext.SaveChanges();
|
||||||
return x.Entity;
|
return x.Entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(TEntity entity)
|
public override void Update(TEntity entity)
|
||||||
{
|
{
|
||||||
DbSet.Update(entity);
|
DbSet.Update(entity);
|
||||||
DataContext.SaveChanges();
|
DataContext.SaveChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Delete(TEntity entity)
|
public override void Delete(TEntity entity)
|
||||||
{
|
{
|
||||||
DbSet.Remove(entity);
|
DbSet.Remove(entity);
|
||||||
DataContext.SaveChanges();
|
DataContext.SaveChanges();
|
||||||
@@ -2,20 +2,26 @@
|
|||||||
using Moonlight.Core.Event;
|
using Moonlight.Core.Event;
|
||||||
using Moonlight.Core.Event.Args;
|
using Moonlight.Core.Event.Args;
|
||||||
using Moonlight.Features.ServiceManagement.Entities;
|
using Moonlight.Features.ServiceManagement.Entities;
|
||||||
|
using BackgroundService = MoonCore.Abstractions.BackgroundService;
|
||||||
|
|
||||||
namespace Moonlight.Core.Services.Background;
|
namespace Moonlight.Core.Services.Background;
|
||||||
|
|
||||||
public class AutoMailSendService // This service is responsible for sending mails automatically
|
public class AutoMailSendService : BackgroundService // This service is responsible for sending mails automatically
|
||||||
{
|
{
|
||||||
private readonly MailService MailService;
|
private readonly MailService MailService;
|
||||||
|
|
||||||
public AutoMailSendService(MailService mailService)
|
public AutoMailSendService(MailService mailService)
|
||||||
{
|
{
|
||||||
MailService = mailService;
|
MailService = mailService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task Run()
|
||||||
|
{
|
||||||
Events.OnUserRegistered += OnUserRegistered;
|
Events.OnUserRegistered += OnUserRegistered;
|
||||||
Events.OnServiceOrdered += OnServiceOrdered;
|
Events.OnServiceOrdered += OnServiceOrdered;
|
||||||
Events.OnTransactionCreated += OnTransactionCreated;
|
Events.OnTransactionCreated += OnTransactionCreated;
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnTransactionCreated(object? sender, TransactionCreatedEventArgs eventArgs)
|
private async void OnTransactionCreated(object? sender, TransactionCreatedEventArgs eventArgs)
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using Moonlight.Core.Helpers;
|
using MoonCore.Attributes;
|
||||||
|
using MoonCore.Helpers;
|
||||||
|
|
||||||
namespace Moonlight.Core.Services;
|
namespace Moonlight.Core.Services;
|
||||||
|
|
||||||
|
[Singleton]
|
||||||
public class BucketService
|
public class BucketService
|
||||||
{
|
{
|
||||||
private readonly string BasePath;
|
private readonly string BasePath;
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
using Moonlight.Core.Configuration;
|
|
||||||
using Moonlight.Core.Helpers;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Moonlight.Core.Services;
|
|
||||||
|
|
||||||
public class ConfigService
|
|
||||||
{
|
|
||||||
private readonly string Path = PathBuilder.File("storage", "config.json");
|
|
||||||
private ConfigV1 Data;
|
|
||||||
|
|
||||||
public ConfigService()
|
|
||||||
{
|
|
||||||
Reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reload()
|
|
||||||
{
|
|
||||||
if(!File.Exists(Path))
|
|
||||||
File.WriteAllText(Path, "{}");
|
|
||||||
|
|
||||||
var text = File.ReadAllText(Path);
|
|
||||||
Data = JsonConvert.DeserializeObject<ConfigV1>(text) ?? new();
|
|
||||||
|
|
||||||
ApplyEnvironmentVariables("Moonlight", Data);
|
|
||||||
|
|
||||||
Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Save()
|
|
||||||
{
|
|
||||||
var text = JsonConvert.SerializeObject(Data, Formatting.Indented);
|
|
||||||
File.WriteAllText(Path, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConfigV1 Get()
|
|
||||||
{
|
|
||||||
return Data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetDiagnoseJson()
|
|
||||||
{
|
|
||||||
var text = File.ReadAllText(Path);
|
|
||||||
var data = JsonConvert.DeserializeObject<ConfigV1>(text) ?? new();
|
|
||||||
|
|
||||||
// Security token
|
|
||||||
data.Security.Token = "";
|
|
||||||
|
|
||||||
// Database
|
|
||||||
if (string.IsNullOrEmpty(data.Database.Password))
|
|
||||||
data.Database.Password = "WAS EMPTY";
|
|
||||||
else
|
|
||||||
data.Database.Password = "WAS NOT EMPTY";
|
|
||||||
|
|
||||||
// Mailserver
|
|
||||||
if (string.IsNullOrEmpty(data.MailServer.Password))
|
|
||||||
data.MailServer.Password = "WAS EMPTY";
|
|
||||||
else
|
|
||||||
data.MailServer.Password = "WAS NOT EMPTY";
|
|
||||||
|
|
||||||
return JsonConvert.SerializeObject(data, Formatting.Indented);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyEnvironmentVariables(string prefix, object objectToLookAt)
|
|
||||||
{
|
|
||||||
foreach (var property in objectToLookAt.GetType().GetProperties())
|
|
||||||
{
|
|
||||||
var envName = $"{prefix}_{property.Name}";
|
|
||||||
|
|
||||||
if (property.PropertyType.Assembly == GetType().Assembly)
|
|
||||||
{
|
|
||||||
ApplyEnvironmentVariables(envName, property.GetValue(objectToLookAt)!);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(!Environment.GetEnvironmentVariables().Contains(envName))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var envValue = Environment.GetEnvironmentVariable(envName)!;
|
|
||||||
|
|
||||||
if (property.PropertyType == typeof(string))
|
|
||||||
{
|
|
||||||
property.SetValue(objectToLookAt, envValue);
|
|
||||||
}
|
|
||||||
else if (property.PropertyType == typeof(int))
|
|
||||||
{
|
|
||||||
if(!int.TryParse(envValue, out int envIntValue))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
property.SetValue(objectToLookAt, envIntValue);
|
|
||||||
}
|
|
||||||
else if (property.PropertyType == typeof(bool))
|
|
||||||
{
|
|
||||||
if(!bool.TryParse(envValue, out bool envBoolValue))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
property.SetValue(objectToLookAt, envBoolValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
39
Moonlight/Core/Services/HotKeyService.cs
Normal file
39
Moonlight/Core/Services/HotKeyService.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using Microsoft.JSInterop;
|
||||||
|
using MoonCore.Attributes;
|
||||||
|
using MoonCore.Helpers;
|
||||||
|
|
||||||
|
namespace Moonlight.Core.Services;
|
||||||
|
|
||||||
|
[Scoped]
|
||||||
|
public class HotKeyService : IAsyncDisposable
|
||||||
|
{
|
||||||
|
private readonly IJSRuntime JsRuntime;
|
||||||
|
|
||||||
|
public SmartEventHandler<string> HotKeyPressed { get; set; } = new();
|
||||||
|
|
||||||
|
public HotKeyService(IJSRuntime jsRuntime)
|
||||||
|
{
|
||||||
|
JsRuntime = jsRuntime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Initialize()
|
||||||
|
{
|
||||||
|
var reference = DotNetObjectReference.Create(this);
|
||||||
|
await JsRuntime.InvokeVoidAsync("moonlight.hotkeys.registerListener", reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
[JSInvokable]
|
||||||
|
public async void OnHotkeyPressed(string hotKey)
|
||||||
|
{
|
||||||
|
await HotKeyPressed.Invoke(hotKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await JsRuntime.InvokeVoidAsync("moonlight.keyListener.unregisterListener");
|
||||||
|
}
|
||||||
|
catch (Exception) { /* ignored */}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using MoonCore.Abstractions;
|
||||||
|
using MoonCore.Attributes;
|
||||||
|
using MoonCore.Exceptions;
|
||||||
|
using MoonCore.Helpers;
|
||||||
using Moonlight.Core.Database.Entities;
|
using Moonlight.Core.Database.Entities;
|
||||||
using Moonlight.Core.Exceptions;
|
|
||||||
using Moonlight.Core.Helpers;
|
|
||||||
using Moonlight.Core.Models.Abstractions;
|
using Moonlight.Core.Models.Abstractions;
|
||||||
using Moonlight.Core.Models.Enums;
|
using Moonlight.Core.Models.Enums;
|
||||||
using Moonlight.Core.Repositories;
|
|
||||||
using Moonlight.Core.Services.Utils;
|
using Moonlight.Core.Services.Utils;
|
||||||
using Moonlight.Features.StoreSystem.Entities;
|
using Moonlight.Features.StoreSystem.Entities;
|
||||||
using OtpNet;
|
using OtpNet;
|
||||||
@@ -13,6 +14,7 @@ namespace Moonlight.Core.Services;
|
|||||||
|
|
||||||
// This service allows you to reauthenticate, login and force login
|
// This service allows you to reauthenticate, login and force login
|
||||||
// It does also contain the permission system accessor for the current user
|
// It does also contain the permission system accessor for the current user
|
||||||
|
[Scoped]
|
||||||
public class IdentityService
|
public class IdentityService
|
||||||
{
|
{
|
||||||
private readonly Repository<User> UserRepository;
|
private readonly Repository<User> UserRepository;
|
||||||
@@ -73,7 +75,7 @@ public class IdentityService
|
|||||||
if (string.IsNullOrEmpty(Token))
|
if (string.IsNullOrEmpty(Token))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!await JwtService.Validate(Token))
|
if (!await JwtService.Validate(Token, "User"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var data = await JwtService.Decode(Token);
|
var data = await JwtService.Decode(Token);
|
||||||
@@ -150,7 +152,7 @@ public class IdentityService
|
|||||||
{
|
{
|
||||||
data.Add("userId", user.Id.ToString());
|
data.Add("userId", user.Id.ToString());
|
||||||
data.Add("issuedAt", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString());
|
data.Add("issuedAt", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString());
|
||||||
}, TimeSpan.FromDays(10));
|
}, "User", TimeSpan.FromDays(10));
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
using Microsoft.JSInterop;
|
|
||||||
|
|
||||||
namespace Moonlight.Core.Services.Interop;
|
|
||||||
|
|
||||||
public class AlertService
|
|
||||||
{
|
|
||||||
private readonly IJSRuntime JsRuntime;
|
|
||||||
|
|
||||||
public AlertService(IJSRuntime jsRuntime)
|
|
||||||
{
|
|
||||||
JsRuntime = jsRuntime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Info(string title, string message)
|
|
||||||
{
|
|
||||||
await JsRuntime.InvokeVoidAsync("moonlight.alerts.info", title, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Success(string title, string message)
|
|
||||||
{
|
|
||||||
await JsRuntime.InvokeVoidAsync("moonlight.alerts.success", title, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Warning(string title, string message)
|
|
||||||
{
|
|
||||||
await JsRuntime.InvokeVoidAsync("moonlight.alerts.warning", title, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Error(string title, string message)
|
|
||||||
{
|
|
||||||
await JsRuntime.InvokeVoidAsync("moonlight.alerts.error", title, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> Text(string title, string message)
|
|
||||||
{
|
|
||||||
return await JsRuntime.InvokeAsync<string>("moonlight.alerts.text", title, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> YesNo(string title, string yes, string no)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return await JsRuntime.InvokeAsync<bool>("moonlight.alerts.yesno", title, yes, no);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
using Microsoft.JSInterop;
|
|
||||||
|
|
||||||
namespace Moonlight.Core.Services.Interop;
|
|
||||||
|
|
||||||
public class CookieService
|
|
||||||
{
|
|
||||||
private readonly IJSRuntime JsRuntime;
|
|
||||||
private string Expires = "";
|
|
||||||
|
|
||||||
public CookieService(IJSRuntime jsRuntime)
|
|
||||||
{
|
|
||||||
JsRuntime = jsRuntime;
|
|
||||||
ExpireDays = 300;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SetValue(string key, string value, int? days = null)
|
|
||||||
{
|
|
||||||
var curExp = (days != null) ? (days > 0 ? DateToUTC(days.Value) : "") : Expires;
|
|
||||||
await SetCookie($"{key}={value}; expires={curExp}; path=/");
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> GetValue(string key, string def = "")
|
|
||||||
{
|
|
||||||
var cookieString = await GetCookie();
|
|
||||||
|
|
||||||
var cookieParts = cookieString.Split(";");
|
|
||||||
|
|
||||||
foreach (var cookiePart in cookieParts)
|
|
||||||
{
|
|
||||||
if(string.IsNullOrEmpty(cookiePart))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var cookieKeyValue = cookiePart.Split("=")
|
|
||||||
.Select(x => x.Trim()) // There may be spaces e.g. with the "AspNetCore.Culture" key
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
if (cookieKeyValue.Length == 2)
|
|
||||||
{
|
|
||||||
if (cookieKeyValue[0] == key)
|
|
||||||
return cookieKeyValue[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return def;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SetCookie(string value)
|
|
||||||
{
|
|
||||||
await JsRuntime.InvokeVoidAsync("eval", $"document.cookie = \"{value}\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<string> GetCookie()
|
|
||||||
{
|
|
||||||
return await JsRuntime.InvokeAsync<string>("eval", $"document.cookie");
|
|
||||||
}
|
|
||||||
|
|
||||||
private int ExpireDays
|
|
||||||
{
|
|
||||||
set => Expires = DateToUTC(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string DateToUTC(int days) => DateTime.Now.AddDays(days).ToUniversalTime().ToString("R");
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
using System.Text;
|
|
||||||
using Microsoft.JSInterop;
|
|
||||||
|
|
||||||
namespace Moonlight.Core.Services.Interop;
|
|
||||||
|
|
||||||
public class FileDownloadService
|
|
||||||
{
|
|
||||||
private readonly IJSRuntime JsRuntime;
|
|
||||||
|
|
||||||
public FileDownloadService(IJSRuntime jsRuntime)
|
|
||||||
{
|
|
||||||
JsRuntime = jsRuntime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DownloadStream(string fileName, Stream stream)
|
|
||||||
{
|
|
||||||
using var streamRef = new DotNetStreamReference(stream);
|
|
||||||
|
|
||||||
await JsRuntime.InvokeVoidAsync("moonlight.utils.download", fileName, streamRef);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DownloadBytes(string fileName, byte[] bytes)
|
|
||||||
{
|
|
||||||
var ms = new MemoryStream(bytes);
|
|
||||||
|
|
||||||
await DownloadStream(fileName, ms);
|
|
||||||
|
|
||||||
ms.Close();
|
|
||||||
await ms.DisposeAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DownloadString(string fileName, string content) =>
|
|
||||||
await DownloadBytes(fileName, Encoding.UTF8.GetBytes(content));
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
using Microsoft.JSInterop;
|
|
||||||
|
|
||||||
namespace Moonlight.Core.Services.Interop;
|
|
||||||
|
|
||||||
public class ModalService
|
|
||||||
{
|
|
||||||
private readonly IJSRuntime JsRuntime;
|
|
||||||
|
|
||||||
public ModalService(IJSRuntime jsRuntime)
|
|
||||||
{
|
|
||||||
JsRuntime = jsRuntime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Show(string id, bool focus = true) // Focus can be specified to fix issues with other components
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await JsRuntime.InvokeVoidAsync("moonlight.modals.show", id, focus);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Hide(string id)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await JsRuntime.InvokeVoidAsync("moonlight.modals.hide", id);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
// Ignored
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
using Microsoft.JSInterop;
|
|
||||||
|
|
||||||
namespace Moonlight.Core.Services.Interop;
|
|
||||||
|
|
||||||
public class ToastService
|
|
||||||
{
|
|
||||||
private readonly IJSRuntime JsRuntime;
|
|
||||||
|
|
||||||
public ToastService(IJSRuntime jsRuntime)
|
|
||||||
{
|
|
||||||
JsRuntime = jsRuntime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Success(string title, string message, int timeout = 5000)
|
|
||||||
{
|
|
||||||
await JsRuntime.InvokeVoidAsync("moonlight.toasts.success", title, message, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Info(string title, string message, int timeout = 5000)
|
|
||||||
{
|
|
||||||
await JsRuntime.InvokeVoidAsync("moonlight.toasts.info", title, message, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Danger(string title, string message, int timeout = 5000)
|
|
||||||
{
|
|
||||||
await JsRuntime.InvokeVoidAsync("moonlight.toasts.danger", title, message, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Warning(string title, string message, int timeout = 5000)
|
|
||||||
{
|
|
||||||
await JsRuntime.InvokeVoidAsync("moonlight.toasts.warning", title, message, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overloads
|
|
||||||
|
|
||||||
public async Task Success(string message, int timeout = 5000)
|
|
||||||
{
|
|
||||||
await Success("", message, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Info(string message, int timeout = 5000)
|
|
||||||
{
|
|
||||||
await Info("", message, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Danger(string message, int timeout = 5000)
|
|
||||||
{
|
|
||||||
await Danger("", message, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Warning(string message, int timeout = 5000)
|
|
||||||
{
|
|
||||||
await Warning("", message, timeout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +1,20 @@
|
|||||||
using MailKit.Net.Smtp;
|
using MailKit.Net.Smtp;
|
||||||
using MimeKit;
|
using MimeKit;
|
||||||
|
using MoonCore.Attributes;
|
||||||
|
using MoonCore.Helpers;
|
||||||
|
using MoonCore.Services;
|
||||||
|
using Moonlight.Core.Configuration;
|
||||||
using Moonlight.Core.Database.Entities;
|
using Moonlight.Core.Database.Entities;
|
||||||
using Moonlight.Core.Helpers;
|
|
||||||
|
|
||||||
namespace Moonlight.Core.Services;
|
namespace Moonlight.Core.Services;
|
||||||
|
|
||||||
|
[Singleton]
|
||||||
public class MailService
|
public class MailService
|
||||||
{
|
{
|
||||||
private readonly ConfigService ConfigService;
|
private readonly ConfigService<ConfigV1> ConfigService;
|
||||||
private readonly string BasePath;
|
private readonly string BasePath;
|
||||||
|
|
||||||
public MailService(ConfigService configService)
|
public MailService(ConfigService<ConfigV1> configService)
|
||||||
{
|
{
|
||||||
ConfigService = configService;
|
ConfigService = configService;
|
||||||
|
|
||||||
@@ -52,6 +56,9 @@ public class MailService
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
Logger.Debug($"Sending {templateName} mail to {user.Email}");
|
||||||
|
Logger.Debug($"Body: {body.HtmlBody}");
|
||||||
|
|
||||||
await smtpClient.ConnectAsync(config.Host, config.Port, config.UseSsl);
|
await smtpClient.ConnectAsync(config.Host, config.Port, config.UseSsl);
|
||||||
await smtpClient.AuthenticateAsync(config.Email, config.Password);
|
await smtpClient.AuthenticateAsync(config.Email, config.Password);
|
||||||
await smtpClient.SendAsync(message);
|
await smtpClient.SendAsync(message);
|
||||||
|
|||||||
@@ -1,21 +1,28 @@
|
|||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
|
using MoonCore.Abstractions;
|
||||||
|
using MoonCore.Attributes;
|
||||||
|
using MoonCore.Helpers;
|
||||||
|
using MoonCore.Services;
|
||||||
|
using Moonlight.Core.Configuration;
|
||||||
using Moonlight.Core.Event;
|
using Moonlight.Core.Event;
|
||||||
using Moonlight.Core.Extensions;
|
using Moonlight.Core.Extensions;
|
||||||
using Moonlight.Core.Helpers;
|
using Moonlight.Features.Servers.Entities;
|
||||||
using Moonlight.Features.Theming.Services;
|
using Moonlight.Features.Theming.Services;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Moonlight.Core.Services;
|
namespace Moonlight.Core.Services;
|
||||||
|
|
||||||
|
[Singleton]
|
||||||
public class MoonlightService // This service can be used to perform strictly panel specific actions
|
public class MoonlightService // This service can be used to perform strictly panel specific actions
|
||||||
{
|
{
|
||||||
private readonly ConfigService ConfigService;
|
private readonly ConfigService<ConfigV1> ConfigService;
|
||||||
private readonly IServiceProvider ServiceProvider;
|
private readonly IServiceProvider ServiceProvider;
|
||||||
|
|
||||||
public WebApplication Application { get; set; } // Do NOT modify using a plugin
|
public WebApplication Application { get; set; } // Do NOT modify using a plugin
|
||||||
public string LogPath { get; set; } // Do NOT modify using a plugin
|
public string LogPath { get; set; } // Do NOT modify using a plugin
|
||||||
public ThemeService Theme => ServiceProvider.GetRequiredService<ThemeService>();
|
public ThemeService Theme => ServiceProvider.GetRequiredService<ThemeService>();
|
||||||
|
|
||||||
public MoonlightService(ConfigService configService, IServiceProvider serviceProvider)
|
public MoonlightService(ConfigService<ConfigV1> configService, IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
ConfigService = configService;
|
ConfigService = configService;
|
||||||
ServiceProvider = serviceProvider;
|
ServiceProvider = serviceProvider;
|
||||||
@@ -51,11 +58,22 @@ public class MoonlightService // This service can be used to perform strictly pa
|
|||||||
|
|
||||||
await zip.AddFromText("log.txt", log);
|
await zip.AddFromText("log.txt", log);
|
||||||
|
|
||||||
// TODO: Add node settings here
|
// Add node config
|
||||||
|
var nodeRepo = scope.ServiceProvider.GetRequiredService<Repository<ServerNode>>();
|
||||||
|
var nodes = nodeRepo.Get().ToArray();
|
||||||
|
|
||||||
|
foreach (var node in nodes)
|
||||||
|
{
|
||||||
|
// Remove sensitive data
|
||||||
|
node.Token = string.IsNullOrEmpty(node.Token) ? "IS EMPTY" : "IS NOT EMPTY";
|
||||||
|
}
|
||||||
|
|
||||||
|
var nodesJson = JsonConvert.SerializeObject(nodes, Formatting.Indented);
|
||||||
|
await zip.AddFromText("nodes.json", nodesJson);
|
||||||
|
|
||||||
// Add config
|
// Add config
|
||||||
var config = ConfigService.GetDiagnoseJson();
|
var configJson = ConfigService.GetDiagnosticJson();
|
||||||
await zip.AddFromText("config.json", config);
|
await zip.AddFromText("config.json", configJson);
|
||||||
|
|
||||||
// Make a list of plugins
|
// Make a list of plugins
|
||||||
var pluginService = scope.ServiceProvider.GetRequiredService<PluginService>();
|
var pluginService = scope.ServiceProvider.GetRequiredService<PluginService>();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Moonlight.Core.Helpers;
|
using MoonCore.Helpers;
|
||||||
using Moonlight.Core.Plugins;
|
using Moonlight.Core.Plugins;
|
||||||
using Moonlight.Core.Plugins.Contexts;
|
using Moonlight.Core.Plugins.Contexts;
|
||||||
using Moonlight.Features.ServiceManagement.Models.Abstractions;
|
using Moonlight.Features.ServiceManagement.Models.Abstractions;
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using Moonlight.Core.Models.Abstractions;
|
using MoonCore.Attributes;
|
||||||
|
using Moonlight.Core.Models.Abstractions;
|
||||||
|
|
||||||
namespace Moonlight.Core.Services;
|
namespace Moonlight.Core.Services;
|
||||||
|
|
||||||
|
[Singleton]
|
||||||
public class SessionService
|
public class SessionService
|
||||||
{
|
{
|
||||||
private readonly List<Session> AllSessions = new();
|
private readonly List<Session> AllSessions = new();
|
||||||
|
|||||||
@@ -1,28 +1,32 @@
|
|||||||
using Moonlight.Core.Database.Entities;
|
using MoonCore.Abstractions;
|
||||||
|
using MoonCore.Attributes;
|
||||||
|
using MoonCore.Exceptions;
|
||||||
|
using MoonCore.Helpers;
|
||||||
|
using MoonCore.Services;
|
||||||
|
using Moonlight.Core.Configuration;
|
||||||
|
using Moonlight.Core.Database.Entities;
|
||||||
using Moonlight.Core.Event;
|
using Moonlight.Core.Event;
|
||||||
using Moonlight.Core.Exceptions;
|
|
||||||
using Moonlight.Core.Extensions;
|
using Moonlight.Core.Extensions;
|
||||||
using Moonlight.Core.Helpers;
|
|
||||||
using Moonlight.Core.Models.Abstractions;
|
using Moonlight.Core.Models.Abstractions;
|
||||||
using Moonlight.Core.Models.Enums;
|
using Moonlight.Core.Models.Enums;
|
||||||
using Moonlight.Core.Models.Templates;
|
using Moonlight.Core.Models.Templates;
|
||||||
using Moonlight.Core.Repositories;
|
|
||||||
using Moonlight.Core.Services.Utils;
|
using Moonlight.Core.Services.Utils;
|
||||||
using OtpNet;
|
using OtpNet;
|
||||||
|
|
||||||
namespace Moonlight.Core.Services.Users;
|
namespace Moonlight.Core.Services.Users;
|
||||||
|
|
||||||
|
[Scoped]
|
||||||
public class UserAuthService
|
public class UserAuthService
|
||||||
{
|
{
|
||||||
private readonly Repository<User> UserRepository;
|
private readonly Repository<User> UserRepository;
|
||||||
private readonly JwtService JwtService;
|
private readonly JwtService JwtService;
|
||||||
private readonly ConfigService ConfigService;
|
private readonly ConfigService<ConfigV1> ConfigService;
|
||||||
private readonly MailService MailService;
|
private readonly MailService MailService;
|
||||||
|
|
||||||
public UserAuthService(
|
public UserAuthService(
|
||||||
Repository<User> userRepository,
|
Repository<User> userRepository,
|
||||||
JwtService jwtService,
|
JwtService jwtService,
|
||||||
ConfigService configService,
|
ConfigService<ConfigV1> configService,
|
||||||
MailService mailService)
|
MailService mailService)
|
||||||
{
|
{
|
||||||
UserRepository = userRepository;
|
UserRepository = userRepository;
|
||||||
@@ -97,7 +101,10 @@ public class UserAuthService
|
|||||||
|
|
||||||
public async Task SendVerification(User user)
|
public async Task SendVerification(User user)
|
||||||
{
|
{
|
||||||
var jwt = await JwtService.Create(data => { data.Add("mailToVerify", user.Email); }, TimeSpan.FromMinutes(10));
|
var jwt = await JwtService.Create(data =>
|
||||||
|
{
|
||||||
|
data.Add("mailToVerify", user.Email);
|
||||||
|
}, "EmailVerification", TimeSpan.FromMinutes(10));
|
||||||
|
|
||||||
await MailService.Send(user, "Verify your account", "verifyMail", user, new MailVerify()
|
await MailService.Send(user, "Verify your account", "verifyMail", user, new MailVerify()
|
||||||
{
|
{
|
||||||
@@ -114,7 +121,10 @@ public class UserAuthService
|
|||||||
if (user == null)
|
if (user == null)
|
||||||
throw new DisplayException("An account with that email was not found");
|
throw new DisplayException("An account with that email was not found");
|
||||||
|
|
||||||
var jwt = await JwtService.Create(data => { data.Add("accountToReset", user.Id.ToString()); });
|
var jwt = await JwtService.Create(data =>
|
||||||
|
{
|
||||||
|
data.Add("accountToReset", user.Id.ToString());
|
||||||
|
}, "PasswordReset", TimeSpan.FromHours(1));
|
||||||
|
|
||||||
await MailService.Send(user, "Password reset for your account", "passwordReset", user, new ResetPassword()
|
await MailService.Send(user, "Password reset for your account", "passwordReset", user, new ResetPassword()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using MoonCore.Abstractions;
|
||||||
|
using MoonCore.Attributes;
|
||||||
using Moonlight.Core.Database.Entities;
|
using Moonlight.Core.Database.Entities;
|
||||||
using Moonlight.Core.Repositories;
|
|
||||||
using Moonlight.Features.Community.Entities;
|
using Moonlight.Features.Community.Entities;
|
||||||
using Moonlight.Features.Community.Services;
|
using Moonlight.Features.Community.Services;
|
||||||
using Moonlight.Features.ServiceManagement.Entities;
|
using Moonlight.Features.ServiceManagement.Entities;
|
||||||
@@ -10,6 +12,7 @@ using Moonlight.Features.Ticketing.Entities;
|
|||||||
|
|
||||||
namespace Moonlight.Core.Services.Users;
|
namespace Moonlight.Core.Services.Users;
|
||||||
|
|
||||||
|
[Scoped]
|
||||||
public class UserDeleteService
|
public class UserDeleteService
|
||||||
{
|
{
|
||||||
private readonly Repository<Service> ServiceRepository;
|
private readonly Repository<Service> ServiceRepository;
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
using Moonlight.Core.Database.Entities;
|
using MoonCore.Abstractions;
|
||||||
using Moonlight.Core.Repositories;
|
using MoonCore.Attributes;
|
||||||
|
using Moonlight.Core.Database.Entities;
|
||||||
|
|
||||||
|
|
||||||
namespace Moonlight.Core.Services.Users;
|
namespace Moonlight.Core.Services.Users;
|
||||||
|
|
||||||
|
[Scoped]
|
||||||
public class UserDetailsService
|
public class UserDetailsService
|
||||||
{
|
{
|
||||||
private readonly BucketService BucketService;
|
private readonly BucketService BucketService;
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
using Moonlight.Core.Database.Entities;
|
using MoonCore.Abstractions;
|
||||||
using Moonlight.Core.Exceptions;
|
using MoonCore.Attributes;
|
||||||
using Moonlight.Core.Repositories;
|
using MoonCore.Exceptions;
|
||||||
|
using Moonlight.Core.Database.Entities;
|
||||||
|
|
||||||
namespace Moonlight.Core.Services.Users;
|
namespace Moonlight.Core.Services.Users;
|
||||||
|
|
||||||
|
[Scoped]
|
||||||
public class UserService
|
public class UserService
|
||||||
{
|
{
|
||||||
private readonly Repository<User> UserRepository;
|
private readonly Repository<User> UserRepository;
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
using Moonlight.Core.Helpers;
|
using MoonCore.Attributes;
|
||||||
|
using MoonCore.Helpers;
|
||||||
|
using MoonCore.Services;
|
||||||
|
using Moonlight.Core.Configuration;
|
||||||
|
|
||||||
namespace Moonlight.Core.Services.Utils;
|
namespace Moonlight.Core.Services.Utils;
|
||||||
|
|
||||||
|
[Scoped]
|
||||||
public class ConnectionService
|
public class ConnectionService
|
||||||
{
|
{
|
||||||
private readonly IHttpContextAccessor ContextAccessor;
|
private readonly IHttpContextAccessor ContextAccessor;
|
||||||
private readonly ConfigService ConfigService;
|
private readonly ConfigService<ConfigV1> ConfigService;
|
||||||
|
|
||||||
public ConnectionService(IHttpContextAccessor contextAccessor, ConfigService configService)
|
public ConnectionService(IHttpContextAccessor contextAccessor, ConfigService<ConfigV1> configService)
|
||||||
{
|
{
|
||||||
ContextAccessor = contextAccessor;
|
ContextAccessor = contextAccessor;
|
||||||
ConfigService = configService;
|
ConfigService = configService;
|
||||||
|
|||||||
@@ -1,25 +1,31 @@
|
|||||||
using JWT.Algorithms;
|
using JWT.Algorithms;
|
||||||
using JWT.Builder;
|
using JWT.Builder;
|
||||||
using Moonlight.Core.Helpers;
|
using JWT.Exceptions;
|
||||||
|
using MoonCore.Attributes;
|
||||||
|
using MoonCore.Helpers;
|
||||||
|
using MoonCore.Services;
|
||||||
|
using Moonlight.Core.Configuration;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Moonlight.Core.Services.Utils;
|
namespace Moonlight.Core.Services.Utils;
|
||||||
|
|
||||||
|
[Singleton]
|
||||||
public class JwtService
|
public class JwtService
|
||||||
{
|
{
|
||||||
private readonly ConfigService ConfigService;
|
private readonly ConfigService<ConfigV1> ConfigService;
|
||||||
private readonly TimeSpan DefaultDuration = TimeSpan.FromDays(365 * 10);
|
private readonly TimeSpan DefaultDuration = TimeSpan.FromDays(365 * 10);
|
||||||
|
|
||||||
public JwtService(ConfigService configService)
|
public JwtService(ConfigService<ConfigV1> configService)
|
||||||
{
|
{
|
||||||
ConfigService = configService;
|
ConfigService = configService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<string> Create(Action<Dictionary<string, string>> data, TimeSpan? validDuration = null)
|
public Task<string> Create(Action<Dictionary<string, string>> data, string type, TimeSpan? validDuration = null)
|
||||||
{
|
{
|
||||||
var builder = new JwtBuilder()
|
var builder = new JwtBuilder()
|
||||||
.WithSecret(ConfigService.Get().Security.Token)
|
.WithSecret(ConfigService.Get().Security.Token)
|
||||||
.IssuedAt(DateTime.UtcNow)
|
.IssuedAt(DateTime.UtcNow)
|
||||||
|
.AddHeader("Type", type)
|
||||||
.ExpirationTime(DateTime.UtcNow.Add(validDuration ?? DefaultDuration))
|
.ExpirationTime(DateTime.UtcNow.Add(validDuration ?? DefaultDuration))
|
||||||
.WithAlgorithm(new HMACSHA512Algorithm());
|
.WithAlgorithm(new HMACSHA512Algorithm());
|
||||||
|
|
||||||
@@ -34,17 +40,53 @@ public class JwtService
|
|||||||
return Task.FromResult(jwt);
|
return Task.FromResult(jwt);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<bool> Validate(string token)
|
public Task<bool> Validate(string token, params string[] allowedJwtTypes)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Without the body decode call the jwt validation would not work for some weird reason.
|
||||||
|
// It would not throw an error when the signature is invalid
|
||||||
_ = new JwtBuilder()
|
_ = new JwtBuilder()
|
||||||
.WithSecret(ConfigService.Get().Security.Token)
|
.WithSecret(ConfigService.Get().Security.Token)
|
||||||
.WithAlgorithm(new HMACSHA512Algorithm())
|
.WithAlgorithm(new HMACSHA512Algorithm())
|
||||||
.MustVerifySignature()
|
.MustVerifySignature()
|
||||||
.Decode(token);
|
.Decode(token);
|
||||||
|
|
||||||
return Task.FromResult(true);
|
var headerJson = new JwtBuilder()
|
||||||
|
.WithSecret(ConfigService.Get().Security.Token)
|
||||||
|
.WithAlgorithm(new HMACSHA512Algorithm())
|
||||||
|
.MustVerifySignature()
|
||||||
|
.DecodeHeader(token);
|
||||||
|
|
||||||
|
if (headerJson == null)
|
||||||
|
return Task.FromResult(false);
|
||||||
|
|
||||||
|
// Jwt type validation
|
||||||
|
if (allowedJwtTypes.Length == 0)
|
||||||
|
return Task.FromResult(true);
|
||||||
|
|
||||||
|
var headerData = JsonConvert.DeserializeObject<Dictionary<string, string>>(headerJson);
|
||||||
|
|
||||||
|
if (headerData == null) // => Invalid header
|
||||||
|
return Task.FromResult(false);
|
||||||
|
|
||||||
|
if (!headerData.ContainsKey("Type")) // => Invalid header, Type is missing
|
||||||
|
return Task.FromResult(false);
|
||||||
|
|
||||||
|
foreach (var name in allowedJwtTypes)
|
||||||
|
{
|
||||||
|
if (headerData["Type"] == name) // => Correct type found
|
||||||
|
return Task.FromResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// None found? Invalid type!
|
||||||
|
return Task.FromResult(false);
|
||||||
|
}
|
||||||
|
catch (SignatureVerificationException)
|
||||||
|
{
|
||||||
|
Logger.Warn($"A manipulated jwt has been found. Required jwt types: {string.Join(" ", allowedJwtTypes)} Jwt: {token}");
|
||||||
|
|
||||||
|
return Task.FromResult(false);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -67,8 +109,14 @@ public class JwtService
|
|||||||
|
|
||||||
return Task.FromResult(data)!;
|
return Task.FromResult(data)!;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (SignatureVerificationException)
|
||||||
{
|
{
|
||||||
|
return Task.FromResult(new Dictionary<string, string>());
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Warn("An unknown error occured while processing token");
|
||||||
|
Logger.Warn(e);
|
||||||
return Task.FromResult<Dictionary<string, string>>(null!);
|
return Task.FromResult<Dictionary<string, string>>(null!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
@using Moonlight.Core.Models.Forms
|
@using Moonlight.Core.Models.Forms
|
||||||
@using Moonlight.Core.Exceptions
|
|
||||||
@using Moonlight.Core.Models.Enums
|
@using Moonlight.Core.Models.Enums
|
||||||
@using Moonlight.Core.Models.Forms.Auth
|
@using Moonlight.Core.Models.Forms.Auth
|
||||||
@using Moonlight.Core.Services
|
@using Moonlight.Core.Services
|
||||||
@using Moonlight.Core.Services.Users
|
@using Moonlight.Core.Services.Users
|
||||||
|
@using MoonCore.Exceptions
|
||||||
|
|
||||||
@inject IdentityService IdentityService
|
@inject IdentityService IdentityService
|
||||||
@inject UserService UserService
|
@inject UserService UserService
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
@using Moonlight.Core.Models.Forms
|
@using Moonlight.Core.Models.Forms
|
||||||
@using Moonlight.Core.Models.Forms.Auth
|
@using Moonlight.Core.Models.Forms.Auth
|
||||||
@using Moonlight.Core.Services
|
@using Moonlight.Core.Services
|
||||||
@using Moonlight.Core.Services.Interop
|
@using MoonCoreUI.Services
|
||||||
|
|
||||||
@inject IdentityService IdentityService
|
@inject IdentityService IdentityService
|
||||||
@inject CookieService CookieService
|
@inject CookieService CookieService
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
Sign In
|
Sign In
|
||||||
</h1>
|
</h1>
|
||||||
<div class="text-gray-400 fw-semibold fs-6">
|
<div class="text-gray-400 fw-semibold fs-6">
|
||||||
Get unlimited access & earn money
|
Change me
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
@page "/register"
|
@page "/register"
|
||||||
@* Virtual route to trick blazor *@
|
@* Virtual route to trick blazor *@
|
||||||
@using Moonlight.Core.Models.Forms
|
@using Moonlight.Core.Models.Forms
|
||||||
@using Moonlight.Core.Exceptions
|
|
||||||
@using Moonlight.Core.Models.Forms.Auth
|
@using Moonlight.Core.Models.Forms.Auth
|
||||||
@using Moonlight.Core.Services
|
@using Moonlight.Core.Services
|
||||||
@using Moonlight.Core.Services.Interop
|
|
||||||
@using Moonlight.Core.Services.Users
|
@using Moonlight.Core.Services.Users
|
||||||
|
@using MoonCore.Exceptions
|
||||||
|
@using MoonCoreUI.Services
|
||||||
|
|
||||||
@inject IdentityService IdentityService
|
@inject IdentityService IdentityService
|
||||||
@inject UserService UserService
|
@inject UserService UserService
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
Sign Up
|
Sign Up
|
||||||
</h1>
|
</h1>
|
||||||
<div class="text-gray-400 fw-semibold fs-6">
|
<div class="text-gray-400 fw-semibold fs-6">
|
||||||
Get unlimited access & earn money
|
change me
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,242 +0,0 @@
|
|||||||
@using BlazorTable
|
|
||||||
@using System.Linq.Expressions
|
|
||||||
@using Mappy.Net
|
|
||||||
@using Moonlight.Core.Repositories
|
|
||||||
@using Moonlight.Core.Services.Interop
|
|
||||||
|
|
||||||
@typeparam TItem where TItem : class
|
|
||||||
@typeparam TCreateForm
|
|
||||||
@typeparam TUpdateForm
|
|
||||||
|
|
||||||
@inject Repository<TItem> ItemRepository
|
|
||||||
@inject ToastService ToastService
|
|
||||||
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3 class="card-title">@(Title)</h3>
|
|
||||||
<div class="card-toolbar">
|
|
||||||
@Toolbar
|
|
||||||
<button @onclick="StartCreate" class="btn btn-icon btn-success ms-3">
|
|
||||||
<i class="bx bx-sm bx-plus"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<LazyLoader @ref="LazyLoader" Load="LoadItems">
|
|
||||||
<Table TableItem="TItem"
|
|
||||||
Items="Items"
|
|
||||||
PageSize="50"
|
|
||||||
TableClass="table table-row-bordered table-row-gray-100 align-middle gs-0 gy-3 fs-6"
|
|
||||||
TableHeadClass="fw-bold text-muted">
|
|
||||||
@View
|
|
||||||
<Column TableItem="TItem" Field="IdExpression" Title="" Sortable="false" Filterable="false">
|
|
||||||
<Template>
|
|
||||||
<div class="text-end">
|
|
||||||
<div class="btn-group">
|
|
||||||
<button @onclick="() => StartUpdate(context)" class="btn btn-icon btn-warning">
|
|
||||||
<i class="bx bx-sm bx-slider"></i>
|
|
||||||
</button>
|
|
||||||
<button @onclick="() => StartDelete(context)" class="btn btn-icon btn-danger">
|
|
||||||
<i class="bx bx-sm bx-trash"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Template>
|
|
||||||
</Column>
|
|
||||||
</Table>
|
|
||||||
</LazyLoader>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<SmartModal @ref="CreateModal" CssClasses="modal-dialog-centered modal-lg">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title">Create</h5>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<SmartForm Model="CreateForm" OnValidSubmit="FinishCreate">
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="row">
|
|
||||||
<AutoForm Columns="@(CreateForm.GetType().GetProperties().Length > 1 ? 6 : 12)" Model="CreateForm"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
|
||||||
<button type="submit" class="btn btn-primary">Save changes</button>
|
|
||||||
</div>
|
|
||||||
</SmartForm>
|
|
||||||
</SmartModal>
|
|
||||||
|
|
||||||
<SmartModal @ref="UpdateModal" CssClasses="modal-dialog-centered modal-lg">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title">Update</h5>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<SmartForm Model="UpdateForm" OnValidSubmit="FinishUpdate">
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="row">
|
|
||||||
<AutoForm Columns="@(UpdateForm.GetType().GetProperties().Length > 1 ? 6 : 12)" Model="UpdateForm"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
|
||||||
<button type="submit" class="btn btn-primary">Save changes</button>
|
|
||||||
</div>
|
|
||||||
</SmartForm>
|
|
||||||
</SmartModal>
|
|
||||||
|
|
||||||
<SmartModal @ref="DeleteModal" CssClasses="modal-dialog-centered">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title">Do you want to delete this item?</h5>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<p class="text-gray-400 fs-5 fw-semibold">
|
|
||||||
This action cannot be undone. The data will be deleted and cannot be restored
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer p-3">
|
|
||||||
<div class="btn-group w-100">
|
|
||||||
<WButton OnClick="FinishDelete" Text="Delete" CssClasses="btn btn-danger w-50 me-3"/>
|
|
||||||
<button class="btn btn-secondary w-50" data-bs-dismiss="modal">Cancel</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</SmartModal>
|
|
||||||
|
|
||||||
@code
|
|
||||||
{
|
|
||||||
[Parameter]
|
|
||||||
public string Title { get; set; } = "";
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public Func<Repository<TItem>, TItem[]> Load { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment View { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment Toolbar { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public Func<TItem, Task>? ValidateAdd { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public Func<TItem, Task>? ValidateUpdate { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public Func<TItem, Task>? ValidateDelete { get; set; }
|
|
||||||
|
|
||||||
private TItem[] Items;
|
|
||||||
private TCreateForm CreateForm;
|
|
||||||
private TUpdateForm UpdateForm;
|
|
||||||
private TItem ItemToUpdate;
|
|
||||||
private TItem ItemToDelete;
|
|
||||||
|
|
||||||
private SmartModal CreateModal;
|
|
||||||
private SmartModal UpdateModal;
|
|
||||||
private SmartModal DeleteModal;
|
|
||||||
|
|
||||||
private Expression<Func<TItem, object>> IdExpression;
|
|
||||||
private LazyLoader LazyLoader;
|
|
||||||
|
|
||||||
protected override void OnInitialized()
|
|
||||||
{
|
|
||||||
if (Load == null)
|
|
||||||
throw new ArgumentNullException(nameof(Load));
|
|
||||||
|
|
||||||
CreateForm = Activator.CreateInstance<TCreateForm>()!;
|
|
||||||
UpdateForm = Activator.CreateInstance<TUpdateForm>()!;
|
|
||||||
|
|
||||||
IdExpression = CreateExpression();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Reload() => await LazyLoader.Reload();
|
|
||||||
|
|
||||||
private Task LoadItems(LazyLoader _)
|
|
||||||
{
|
|
||||||
Items = Load.Invoke(ItemRepository);
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task StartUpdate(TItem item)
|
|
||||||
{
|
|
||||||
UpdateForm = Mapper.Map<TUpdateForm>(item);
|
|
||||||
ItemToUpdate = item;
|
|
||||||
await UpdateModal.Show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task FinishUpdate()
|
|
||||||
{
|
|
||||||
var item = Mapper.Map(ItemToUpdate, UpdateForm!);
|
|
||||||
|
|
||||||
if (ValidateUpdate != null) // Optional additional validation
|
|
||||||
await ValidateUpdate.Invoke(item);
|
|
||||||
|
|
||||||
ItemRepository.Update(item);
|
|
||||||
|
|
||||||
// Reset
|
|
||||||
await UpdateModal.Hide();
|
|
||||||
await LazyLoader.Reload();
|
|
||||||
await ToastService.Success("Successfully updated item");
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task StartCreate()
|
|
||||||
{
|
|
||||||
CreateForm = Activator.CreateInstance<TCreateForm>()!;
|
|
||||||
await CreateModal.Show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task FinishCreate()
|
|
||||||
{
|
|
||||||
var item = Mapper.Map<TItem>(CreateForm!);
|
|
||||||
|
|
||||||
if (ValidateAdd != null) // Optional additional validation
|
|
||||||
await ValidateAdd.Invoke(item);
|
|
||||||
|
|
||||||
ItemRepository.Add(item);
|
|
||||||
|
|
||||||
// Reset
|
|
||||||
await CreateModal.Hide();
|
|
||||||
await LazyLoader.Reload();
|
|
||||||
await ToastService.Success("Successfully added item");
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task StartDelete(TItem item)
|
|
||||||
{
|
|
||||||
ItemToDelete = item;
|
|
||||||
await DeleteModal.Show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task FinishDelete()
|
|
||||||
{
|
|
||||||
if (ValidateDelete != null) // Optional additional validation
|
|
||||||
await ValidateDelete.Invoke(ItemToDelete);
|
|
||||||
|
|
||||||
ItemRepository.Delete(ItemToDelete);
|
|
||||||
|
|
||||||
// Reset
|
|
||||||
await DeleteModal.Hide();
|
|
||||||
await LazyLoader.Reload();
|
|
||||||
await ToastService.Success("Successfully deleted item");
|
|
||||||
}
|
|
||||||
|
|
||||||
private Expression<Func<TItem, object>> CreateExpression()
|
|
||||||
{
|
|
||||||
// Parameter expression for the input object
|
|
||||||
var inputParam = Expression.Parameter(typeof(TItem), "input");
|
|
||||||
|
|
||||||
// Convert the input object to the actual model type (MyModel in this example)
|
|
||||||
var castedInput = Expression.Convert(inputParam, typeof(TItem));
|
|
||||||
|
|
||||||
// Create a property access expression using the property name
|
|
||||||
var propertyAccess = Expression.Property(castedInput, "Id");
|
|
||||||
|
|
||||||
// Convert the property value to an object
|
|
||||||
var castedResult = Expression.Convert(propertyAccess, typeof(object));
|
|
||||||
|
|
||||||
// Create a lambda expression
|
|
||||||
var lambda = Expression.Lambda<Func<TItem, object>>(castedResult, inputParam);
|
|
||||||
|
|
||||||
return lambda;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
@using Moonlight.Core.Helpers
|
|
||||||
@typeparam TForm
|
|
||||||
|
|
||||||
@foreach (var prop in typeof(TForm).GetProperties())
|
|
||||||
{
|
|
||||||
<div class="col-md-@(Columns) col-12">
|
|
||||||
@{
|
|
||||||
var typeToCreate = typeof(AutoProperty<>).MakeGenericType(prop.PropertyType);
|
|
||||||
var rf = ComponentHelper.FromType(typeToCreate, parameters =>
|
|
||||||
{
|
|
||||||
parameters.Add("Data", Model);
|
|
||||||
parameters.Add("Property", prop);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@rf
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
@code
|
|
||||||
{
|
|
||||||
[Parameter]
|
|
||||||
public TForm Model { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public int Columns { get; set; } = 6;
|
|
||||||
}
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
@using System.Reflection
|
|
||||||
@using System.ComponentModel
|
|
||||||
@using Microsoft.AspNetCore.Components.Forms
|
|
||||||
@using Moonlight.Core.Extensions.Attributes
|
|
||||||
@using Moonlight.Core.Helpers
|
|
||||||
@using Moonlight.Core.Repositories
|
|
||||||
|
|
||||||
@typeparam TProp
|
|
||||||
@inject IServiceProvider ServiceProvider
|
|
||||||
|
|
||||||
<label class="form-label">
|
|
||||||
@(Formatter.ConvertCamelCaseToSpaces(Property.Name))
|
|
||||||
</label>
|
|
||||||
|
|
||||||
@* Description using attribute *@
|
|
||||||
|
|
||||||
@{
|
|
||||||
var attrs = Property.GetCustomAttributes(true);
|
|
||||||
|
|
||||||
var descAttr = attrs
|
|
||||||
.FirstOrDefault(x => x.GetType() == typeof(DescriptionAttribute));
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (descAttr != null)
|
|
||||||
{
|
|
||||||
var attribute = descAttr as DescriptionAttribute;
|
|
||||||
|
|
||||||
<div class="form-text fs-5 mb-2 mt-0">
|
|
||||||
@(attribute!.Description)
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
@* Actual value binding *@
|
|
||||||
|
|
||||||
<div class="input-group mb-5">
|
|
||||||
@if (Property.PropertyType == typeof(string))
|
|
||||||
{
|
|
||||||
<div class="w-100">
|
|
||||||
<InputText id="@Property.Name" @bind-Value="Binder.StringValue" class="form-control"/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
else if (Property.PropertyType == typeof(int))
|
|
||||||
{
|
|
||||||
<InputNumber id="@Property.Name" @bind-Value="Binder.IntValue" class="form-control"/>
|
|
||||||
}
|
|
||||||
else if (Property.PropertyType == typeof(double))
|
|
||||||
{
|
|
||||||
<InputNumber id="@Property.Name" @bind-Value="Binder.DoubleValue" class="form-control"/>
|
|
||||||
}
|
|
||||||
else if (Property.PropertyType == typeof(long))
|
|
||||||
{
|
|
||||||
<InputNumber id="@Property.Name" @bind-Value="Binder.LongValue" class="form-control"/>
|
|
||||||
}
|
|
||||||
else if (Property.PropertyType == typeof(bool))
|
|
||||||
{
|
|
||||||
<div class="form-check">
|
|
||||||
<InputCheckbox id="@Property.Name" @bind-Value="Binder.BoolValue" class="form-check-input"/>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
else if (Property.PropertyType == typeof(DateTime))
|
|
||||||
{
|
|
||||||
<InputDate id="@Property.Name" @bind-Value="Binder.DateTimeValue" class="form-control"/>
|
|
||||||
}
|
|
||||||
else if (Property.PropertyType == typeof(decimal))
|
|
||||||
{
|
|
||||||
<InputNumber id="@Property.Name" step="0.01" @bind-Value="Binder.DoubleValue" class="form-control"/>
|
|
||||||
}
|
|
||||||
else if (Property.PropertyType.IsEnum)
|
|
||||||
{
|
|
||||||
<select @bind="Binder.Class" class="form-select">
|
|
||||||
@foreach (var status in (TProp[])Enum.GetValues(typeof(TProp)))
|
|
||||||
{
|
|
||||||
if (Binder.Class.ToString() == status.ToString())
|
|
||||||
{
|
|
||||||
<option value="@(status)" selected="">@(status)</option>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<option value="@(status)">@(status)</option>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
}
|
|
||||||
else if (Property.PropertyType.IsClass)
|
|
||||||
{
|
|
||||||
var attribute = Property.GetCustomAttributes(true)
|
|
||||||
.FirstOrDefault(x => x.GetType() == typeof(SelectorAttribute)) as SelectorAttribute;
|
|
||||||
|
|
||||||
if (attribute != null)
|
|
||||||
{
|
|
||||||
if (attribute.UseDropdown)
|
|
||||||
{
|
|
||||||
var displayFunc = new Func<TProp, string>(x =>
|
|
||||||
{
|
|
||||||
var prop = typeof(TProp).GetProperties().First(x => x.Name == attribute.DisplayProp);
|
|
||||||
return prop.GetValue(x) as string ?? "N/A";
|
|
||||||
});
|
|
||||||
|
|
||||||
var searchFunc = new Func<TProp, string>(x =>
|
|
||||||
{
|
|
||||||
var prop = typeof(TProp).GetProperties().First(x => x.Name == attribute.SelectorProp);
|
|
||||||
return prop.GetValue(x) as string ?? "N/A";
|
|
||||||
});
|
|
||||||
|
|
||||||
<SmartDropdown @bind-Value="Binder.Class" DisplayFunc="displayFunc" SearchProp="searchFunc" Items="Items"/>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var displayFunc = new Func<TProp, string>(x =>
|
|
||||||
{
|
|
||||||
var prop = typeof(TProp).GetProperties().First(x => x.Name == attribute.DisplayProp);
|
|
||||||
return prop.GetValue(x) as string ?? "N/A";
|
|
||||||
});
|
|
||||||
|
|
||||||
<SmartSelect @bind-Value="Binder.Class" DisplayField="displayFunc" Items="Items" CanBeNull="true"/>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@code
|
|
||||||
{
|
|
||||||
[Parameter]
|
|
||||||
public object Data { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public PropertyInfo Property { get; set; }
|
|
||||||
|
|
||||||
private PropBinder<TProp> Binder;
|
|
||||||
private TProp[] Items = Array.Empty<TProp>();
|
|
||||||
|
|
||||||
protected override void OnInitialized()
|
|
||||||
{
|
|
||||||
Binder = new(Property, Data);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnParametersSet()
|
|
||||||
{
|
|
||||||
Binder = new(Property, Data);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
||||||
{
|
|
||||||
if (firstRender)
|
|
||||||
{
|
|
||||||
if (Property.GetCustomAttributes(true).Any(x => x.GetType() == typeof(SelectorAttribute)))
|
|
||||||
{
|
|
||||||
var typeToGetByDi = typeof(Repository<>).MakeGenericType(typeof(TProp));
|
|
||||||
var repo = ServiceProvider.GetRequiredService(typeToGetByDi);
|
|
||||||
var dbSet = repo.GetType().GetMethods().First(x => x.Name == "Get").Invoke(repo, null) as IEnumerable<TProp>;
|
|
||||||
Items = dbSet!.ToArray();
|
|
||||||
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
@if (ShowConfirm)
|
|
||||||
{
|
|
||||||
<div class="btn-group">
|
|
||||||
<button class="btn btn-success me-2 rounded-end" @onclick="Do">Confirm</button>
|
|
||||||
<button class="btn btn-danger rounded-start" @onclick="() => SetConfirm(false)">Cancel</button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (Working)
|
|
||||||
{
|
|
||||||
<button class="btn @(CssClasses) disabled" disabled="">
|
|
||||||
<span class="spinner-border spinner-border-sm align-middle me-2"></span>
|
|
||||||
@WorkingText
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<button class="btn @(CssClasses)" @onclick="() => SetConfirm(true)">
|
|
||||||
@Text
|
|
||||||
@ChildContent
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@code
|
|
||||||
{
|
|
||||||
private bool Working { get; set; } = false;
|
|
||||||
private bool ShowConfirm = false;
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public string CssClasses { get; set; } = "btn-primary";
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public string Text { get; set; } = "";
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public string WorkingText { get; set; } = "";
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public Func<Task>? OnClick { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment ChildContent { get; set; }
|
|
||||||
|
|
||||||
private async Task SetConfirm(bool b)
|
|
||||||
{
|
|
||||||
ShowConfirm = b;
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task Do()
|
|
||||||
{
|
|
||||||
Working = true;
|
|
||||||
ShowConfirm = false;
|
|
||||||
StateHasChanged();
|
|
||||||
await Task.Run(async () =>
|
|
||||||
{
|
|
||||||
if (OnClick != null)
|
|
||||||
await OnClick.Invoke();
|
|
||||||
|
|
||||||
Working = false;
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
@using Moonlight.Core.Helpers
|
|
||||||
@{
|
|
||||||
var typeToCreate = typeof(AutoForm<>).MakeGenericType(Model.GetType());
|
|
||||||
var rf = ComponentHelper.FromType(typeToCreate, parameter =>
|
|
||||||
{
|
|
||||||
parameter.Add("Model", Model);
|
|
||||||
parameter.Add("Columns", Columns);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@rf
|
|
||||||
|
|
||||||
@code
|
|
||||||
{
|
|
||||||
[Parameter]
|
|
||||||
public object Model { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public int Columns { get; set; } = 6;
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
@using Microsoft.AspNetCore.Components.Forms
|
|
||||||
@using Moonlight.Core.Helpers
|
|
||||||
@using Moonlight.Core.Services.Interop
|
|
||||||
|
|
||||||
@inject ToastService ToastService
|
|
||||||
|
|
||||||
<InputFile OnChange="OnFileChanged" type="file" id="fileUpload" hidden=""/>
|
|
||||||
<label for="fileUpload" class="">
|
|
||||||
@if (SelectedFile == null)
|
|
||||||
{
|
|
||||||
@ChildContent
|
|
||||||
}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
@code
|
|
||||||
{
|
|
||||||
[Parameter]
|
|
||||||
public IBrowserFile? SelectedFile { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public int MaxFileSize { get; set; } = 1024 * 1024 * 5;
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public Func<IBrowserFile, Task>? OnFileSelected { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment ChildContent { get; set; }
|
|
||||||
|
|
||||||
private async Task OnFileChanged(InputFileChangeEventArgs arg)
|
|
||||||
{
|
|
||||||
if (arg.FileCount > 0)
|
|
||||||
{
|
|
||||||
if (arg.File.Size < MaxFileSize)
|
|
||||||
{
|
|
||||||
SelectedFile = arg.File;
|
|
||||||
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
|
|
||||||
if(OnFileSelected != null)
|
|
||||||
await OnFileSelected.Invoke(SelectedFile);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await ToastService.Danger($"The uploaded file should not be bigger than {Formatter.FormatSize(MaxFileSize)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectedFile = null;
|
|
||||||
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task RemoveSelection()
|
|
||||||
{
|
|
||||||
SelectedFile = null;
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
@using Microsoft.AspNetCore.Components.Forms
|
|
||||||
|
|
||||||
@typeparam T
|
|
||||||
@inherits InputBase<T>
|
|
||||||
|
|
||||||
<div class="dropdown w-100">
|
|
||||||
<div class="input-group">
|
|
||||||
@if (CurrentValue == null)
|
|
||||||
{
|
|
||||||
<input class="form-control" type="text" @bind-value="SearchTerm" @bind-value:event="oninput" placeholder="@(Placeholder)">
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<input class="form-control" type="text" value="@(DisplayFunc(CurrentValue))">
|
|
||||||
<button class="btn btn-sm btn-primary" @onclick="() => SelectItem(default(T)!)">
|
|
||||||
<i class="bx bx-sm bx-x"></i>
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@{
|
|
||||||
var anyItems = FilteredItems.Any();
|
|
||||||
}
|
|
||||||
|
|
||||||
<div class="dropdown-menu w-100 @(anyItems ? "show" : "")" style="max-height: 200px; overflow-y: auto;">
|
|
||||||
@if (anyItems)
|
|
||||||
{
|
|
||||||
foreach (var item in FilteredItems)
|
|
||||||
{
|
|
||||||
<button class="dropdown-item py-2" type="button" @onclick="() => SelectItem(item)">@DisplayFunc(item)</button>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@code {
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public IEnumerable<T> Items { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public Func<T, string> DisplayFunc { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public Func<T, string> SearchProp { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public Func<Task>? OnSelected { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public string Placeholder { get; set; } = "Search...";
|
|
||||||
|
|
||||||
private string SearchTerm
|
|
||||||
{
|
|
||||||
get => searchTerm;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
searchTerm = value;
|
|
||||||
FilteredItems = Items.OrderByDescending(x => Matches(SearchProp(x), searchTerm)).Take(30).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string searchTerm = "";
|
|
||||||
|
|
||||||
private List<T> FilteredItems = new();
|
|
||||||
|
|
||||||
private async void SelectItem(T item)
|
|
||||||
{
|
|
||||||
CurrentValue = item;
|
|
||||||
SearchTerm = "";
|
|
||||||
FilteredItems.Clear();
|
|
||||||
|
|
||||||
if (OnSelected != null)
|
|
||||||
await OnSelected.Invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool TryParseValueFromString(string? value, out T result, out string? validationErrorMessage)
|
|
||||||
{
|
|
||||||
// Check if the value is null or empty
|
|
||||||
if (string.IsNullOrEmpty(value))
|
|
||||||
{
|
|
||||||
result = default(T)!;
|
|
||||||
validationErrorMessage = "Value cannot be null or empty";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to find an item that matches the search term
|
|
||||||
var item = FilteredItems.OrderByDescending(x => Matches(SearchProp(x), value)).FirstOrDefault();
|
|
||||||
if (item != null)
|
|
||||||
{
|
|
||||||
result = item;
|
|
||||||
validationErrorMessage = null;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = default(T)!;
|
|
||||||
validationErrorMessage = $"No item found for search term '{value}'";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private float Matches(string input, string search)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(search))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
var cleanedSearch = search
|
|
||||||
.ToLower()
|
|
||||||
.Replace(" ", "")
|
|
||||||
.Replace("-", "");
|
|
||||||
|
|
||||||
var cleanedInput = input
|
|
||||||
.ToLower()
|
|
||||||
.Replace(" ", "")
|
|
||||||
.Replace("-", "");
|
|
||||||
|
|
||||||
if (cleanedInput == cleanedSearch)
|
|
||||||
return 10000;
|
|
||||||
|
|
||||||
float matches = 0;
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
foreach (var c in cleanedInput)
|
|
||||||
{
|
|
||||||
if (cleanedSearch.Length > i)
|
|
||||||
{
|
|
||||||
if (c == cleanedSearch[i])
|
|
||||||
matches++;
|
|
||||||
else
|
|
||||||
matches--;
|
|
||||||
}
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
matches = matches / searchTerm.Length;
|
|
||||||
|
|
||||||
return matches;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
@typeparam TField where TField : struct
|
|
||||||
@using Microsoft.AspNetCore.Components.Forms
|
|
||||||
@inherits InputBase<TField>
|
|
||||||
|
|
||||||
<select @bind="CurrentValue" class="form-select">
|
|
||||||
@foreach (var status in (TField[])Enum.GetValues(typeof(TField)))
|
|
||||||
{
|
|
||||||
if (CurrentValue.ToString() == status.ToString())
|
|
||||||
{
|
|
||||||
<option value="@(status)" selected="">@(status)</option>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<option value="@(status)">@(status)</option>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
|
|
||||||
@code
|
|
||||||
{
|
|
||||||
protected override bool TryParseValueFromString(string? value, out TField result, out string? validationErrorMessage)
|
|
||||||
{
|
|
||||||
result = Enum.Parse<TField>(value);
|
|
||||||
validationErrorMessage = "";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
@using Microsoft.AspNetCore.Components.Forms
|
|
||||||
@using Moonlight.Core.Helpers
|
|
||||||
@using Moonlight.Core.Services.Interop
|
|
||||||
|
|
||||||
@inject ToastService ToastService
|
|
||||||
|
|
||||||
<InputFile OnChange="OnFileChanged" type="file" id="fileUpload" hidden=""/>
|
|
||||||
<label for="fileUpload" class="">
|
|
||||||
@if (SelectedFile != null)
|
|
||||||
{
|
|
||||||
<div class="input-group">
|
|
||||||
<input type="text" class="form-control disabled" value="@(SelectedFile.Name)" disabled="">
|
|
||||||
<button class="btn btn-danger" type="button" @onclick="RemoveSelection">
|
|
||||||
Remove
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<div class="btn btn-primary me-3 btn-icon">
|
|
||||||
<i class="bx bx-upload"></i>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
@code
|
|
||||||
{
|
|
||||||
[Parameter]
|
|
||||||
public IBrowserFile? SelectedFile { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public int MaxFileSize { get; set; } = 1024 * 1024 * 5;
|
|
||||||
|
|
||||||
private async Task OnFileChanged(InputFileChangeEventArgs arg)
|
|
||||||
{
|
|
||||||
if (arg.FileCount > 0)
|
|
||||||
{
|
|
||||||
if (arg.File.Size < MaxFileSize)
|
|
||||||
{
|
|
||||||
SelectedFile = arg.File;
|
|
||||||
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await ToastService.Danger($"The uploaded file should not be bigger than {Formatter.FormatSize(MaxFileSize)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectedFile = null;
|
|
||||||
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task RemoveSelection()
|
|
||||||
{
|
|
||||||
SelectedFile = null;
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
@using Microsoft.AspNetCore.Components.Forms
|
|
||||||
@using Moonlight.Core.Exceptions
|
|
||||||
|
|
||||||
<div class="form @CssClass">
|
|
||||||
<EditForm @ref="EditForm" Model="Model" OnValidSubmit="ValidSubmit" OnInvalidSubmit="InvalidSubmit">
|
|
||||||
<DataAnnotationsValidator></DataAnnotationsValidator>
|
|
||||||
@if (Working)
|
|
||||||
{
|
|
||||||
<div class="d-flex flex-center flex-column">
|
|
||||||
<span class="fs-1 spinner-border spinner-border-lg align-middle me-2"></span>
|
|
||||||
<span class="mt-3 fs-5">Proccessing</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (ErrorMessages.Any())
|
|
||||||
{
|
|
||||||
<div class="alert alert-danger bg-danger text-white p-10 mb-3 rounded-0">
|
|
||||||
@foreach (var msg in ErrorMessages)
|
|
||||||
{
|
|
||||||
@(msg)
|
|
||||||
<br/>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
@(ChildContent)
|
|
||||||
}
|
|
||||||
</EditForm>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@code
|
|
||||||
{
|
|
||||||
[Parameter]
|
|
||||||
public object Model { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public Func<Task>? OnValidSubmit { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public Func<Task>? OnInvalidSubmit { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public Func<Task>? OnSubmit { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment ChildContent { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public string CssClass { get; set; }
|
|
||||||
|
|
||||||
private EditForm EditForm;
|
|
||||||
|
|
||||||
private List<string> ErrorMessages = new();
|
|
||||||
|
|
||||||
private bool Working = false;
|
|
||||||
|
|
||||||
protected override void OnAfterRender(bool firstRender)
|
|
||||||
{
|
|
||||||
if (firstRender)
|
|
||||||
{
|
|
||||||
//EditForm.EditContext!.SetFieldCssClassProvider(new FieldCssHelper());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ValidSubmit(EditContext context)
|
|
||||||
{
|
|
||||||
ErrorMessages.Clear();
|
|
||||||
Working = true;
|
|
||||||
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
|
|
||||||
await Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await InvokeAsync(async () =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (OnValidSubmit != null)
|
|
||||||
await OnValidSubmit.Invoke();
|
|
||||||
|
|
||||||
if (OnSubmit != null)
|
|
||||||
await OnSubmit.Invoke();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
if (e is DisplayException displayException)
|
|
||||||
{
|
|
||||||
ErrorMessages.Add(displayException.Message);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Working = false;
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task InvalidSubmit(EditContext context)
|
|
||||||
{
|
|
||||||
ErrorMessages.Clear();
|
|
||||||
context.Validate();
|
|
||||||
|
|
||||||
foreach (var message in context.GetValidationMessages())
|
|
||||||
{
|
|
||||||
ErrorMessages.Add(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (OnInvalidSubmit != null)
|
|
||||||
await OnInvalidSubmit.Invoke();
|
|
||||||
|
|
||||||
if (OnSubmit != null)
|
|
||||||
await OnSubmit.Invoke();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
if (e is DisplayException displayException)
|
|
||||||
{
|
|
||||||
ErrorMessages.Add(displayException.Message);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
@typeparam TField
|
|
||||||
@using Microsoft.AspNetCore.Components.Forms
|
|
||||||
@inherits InputBase<TField>
|
|
||||||
|
|
||||||
<select class="form-select" @bind="Binding">
|
|
||||||
@if (CanBeNull)
|
|
||||||
{
|
|
||||||
<option value="-1">---</option>
|
|
||||||
}
|
|
||||||
@foreach (var item in Items)
|
|
||||||
{
|
|
||||||
<option value="@(item!.GetHashCode())">@(DisplayField(item))</option>
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
|
|
||||||
@code
|
|
||||||
{
|
|
||||||
[Parameter]
|
|
||||||
public IEnumerable<TField> Items { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public Func<TField, string> DisplayField { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public Func<Task>? OnChange { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public bool CanBeNull { get; set; } = false;
|
|
||||||
|
|
||||||
protected override void OnInitialized()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override string? FormatValueAsString(TField? value)
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return DisplayField.Invoke(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool TryParseValueFromString(string? value, out TField result, out string? validationErrorMessage)
|
|
||||||
{
|
|
||||||
validationErrorMessage = "";
|
|
||||||
result = default(TField)!;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int Binding
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (Value == null)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return Value.GetHashCode();
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
var i = Items.FirstOrDefault(x => x!.GetHashCode() == value);
|
|
||||||
|
|
||||||
if(i == null && !CanBeNull)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Value = i;
|
|
||||||
ValueChanged.InvokeAsync(i);
|
|
||||||
OnChange?.Invoke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
@inject IJSRuntime JsRuntime
|
@inject IJSRuntime JsRuntime
|
||||||
@using Microsoft.AspNetCore.Components.Forms
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
@using Ganss.Xss
|
@using Ganss.Xss
|
||||||
@using Moonlight.Core.Helpers
|
@using MoonCore.Helpers
|
||||||
@using Moonlight.Core.Services
|
@using Moonlight.Core.Services
|
||||||
@inherits InputBase<string>
|
@inherits InputBase<string>
|
||||||
|
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
@if (!Working)
|
|
||||||
{
|
|
||||||
<button class="btn @(CssClasses)" @onclick="Do">
|
|
||||||
@Text
|
|
||||||
@ChildContent
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<button class="btn @(CssClasses) disabled" disabled="">
|
|
||||||
<span class="spinner-border spinner-border-sm align-middle me-2"></span>
|
|
||||||
@WorkingText
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
|
|
||||||
@code
|
|
||||||
{
|
|
||||||
private bool Working { get; set; } = false;
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public string CssClasses { get; set; } = "btn-primary";
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public string Text { get; set; } = "";
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public string WorkingText { get; set; } = "";
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public Func<Task>? OnClick { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment ChildContent { get; set; }
|
|
||||||
|
|
||||||
private async Task Do()
|
|
||||||
{
|
|
||||||
Working = true;
|
|
||||||
StateHasChanged();
|
|
||||||
await Task.Run(async () =>
|
|
||||||
{
|
|
||||||
if (OnClick != null)
|
|
||||||
await OnClick.Invoke();
|
|
||||||
|
|
||||||
Working = false;
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
<div class="show">
|
<div class="show">
|
||||||
<button class="btn btn-light-danger" style="pointer-events: none;">
|
<button class="btn btn-light-danger" style="pointer-events: none;">
|
||||||
Reconnecting
|
Reconnecting
|
||||||
<i class="bx bx-wifi-off ms-1"></i>
|
<span class="spinner-border spinner-border-sm align-middle ms-2"></span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="failed">
|
<div class="failed">
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
@if (Loaded)
|
|
||||||
{
|
|
||||||
@ChildContent
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (ShowAsCard)
|
|
||||||
{
|
|
||||||
<div class="d-flex flex-column flex-center">
|
|
||||||
<div class="card card-body">
|
|
||||||
<div class="d-flex justify-content-center py-4">
|
|
||||||
<span class="fs-1 spinner-border spinner-border-lg align-middle me-2"></span>
|
|
||||||
<span class="ms-3 fs-5 align-middle">@(Text)</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<div class="d-flex justify-content-center py-4">
|
|
||||||
<span class="fs-1 spinner-border spinner-border-lg align-middle me-2"></span>
|
|
||||||
<span class="ms-3 fs-5 align-middle">@(Text)</span>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@code
|
|
||||||
{
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment ChildContent { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public bool ShowAsCard { get; set; } = false;
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public Func<LazyLoader, Task> Load { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public string Text { get; set; } = "";
|
|
||||||
|
|
||||||
private bool Loaded = false;
|
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
||||||
{
|
|
||||||
if (firstRender)
|
|
||||||
{
|
|
||||||
await Load.Invoke(this);
|
|
||||||
Loaded = true;
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SetText(string text)
|
|
||||||
{
|
|
||||||
Text = text;
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Reload()
|
|
||||||
{
|
|
||||||
Loaded = false;
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
await Load.Invoke(this);
|
|
||||||
Loaded = true;
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
@using Moonlight.Core.Services
|
@using Moonlight.Core.Services
|
||||||
@using Moonlight.Core.Services.Interop
|
|
||||||
@using Moonlight.Core.UI.Layouts
|
@using Moonlight.Core.UI.Layouts
|
||||||
|
@using Moonlight.Core.Configuration
|
||||||
|
@using MoonCore.Services
|
||||||
|
@using MoonCoreUI.Services
|
||||||
|
|
||||||
@inject IdentityService IdentityService
|
@inject IdentityService IdentityService
|
||||||
@inject ConfigService ConfigService
|
@inject ConfigService<ConfigV1> ConfigService
|
||||||
@inject CookieService CookieService
|
@inject CookieService CookieService
|
||||||
|
|
||||||
<div class="app-header">
|
<div class="app-header">
|
||||||
|
|||||||
20
Moonlight/Core/UI/Components/Partials/Route.razor
Normal file
20
Moonlight/Core/UI/Components/Partials/Route.razor
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
@{
|
||||||
|
var route = "/" + (Router.Route ?? "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (route == Path)
|
||||||
|
{
|
||||||
|
@ChildContent
|
||||||
|
}
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
[CascadingParameter]
|
||||||
|
public SmartRouter Router { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment ChildContent { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string Path { get; set; }
|
||||||
|
}
|
||||||
@@ -10,8 +10,8 @@
|
|||||||
<img alt="Logo" src="/img/logo.png" class="h-50px d-none d-sm-inline app-sidebar-logo-default">
|
<img alt="Logo" src="/img/logo.png" class="h-50px d-none d-sm-inline app-sidebar-logo-default">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="app-sidebar-navs flex-column-fluid py-6" id="kt_app_sidebar_navs">
|
<div class="app-sidebar-navs flex-column-fluid py-6">
|
||||||
<div class="app-sidebar-wrapper hover-scroll-y my-2" style="height: 811px;">
|
<div class="app-sidebar-wrapper scroll-y my-2">
|
||||||
<div class="app-sidebar-menu-secondary menu menu-rounded menu-column mb-6">
|
<div class="app-sidebar-menu-secondary menu menu-rounded menu-column mb-6">
|
||||||
<div class="menu-item">
|
<div class="menu-item">
|
||||||
<a class="menu-link " href="/">
|
<a class="menu-link " href="/">
|
||||||
@@ -95,6 +95,17 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="menu-item">
|
||||||
|
<a class="menu-link " href="/admin/servers">
|
||||||
|
<span class="menu-icon">
|
||||||
|
<i class="bx bx-sm bx-server"></i>
|
||||||
|
</span>
|
||||||
|
<span class="menu-title">
|
||||||
|
Servers
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="menu-item">
|
<div class="menu-item">
|
||||||
<a class="menu-link " href="/admin/store">
|
<a class="menu-link " href="/admin/store">
|
||||||
<span class="menu-icon">
|
<span class="menu-icon">
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
@using Moonlight.Core.Services.Interop
|
|
||||||
@inject ModalService ModalService
|
|
||||||
|
|
||||||
<div class="modal fade" id="modal@(Id)" tabindex="-1">
|
|
||||||
<div class="modal-dialog @(CssClasses)">
|
|
||||||
<div class="modal-content">
|
|
||||||
@if (ShouldShow)
|
|
||||||
{
|
|
||||||
@ChildContent
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@code
|
|
||||||
{
|
|
||||||
[Parameter]
|
|
||||||
public string CssClasses { get; set; } = "";
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment ChildContent { get; set; }
|
|
||||||
|
|
||||||
private int Id;
|
|
||||||
private bool ShouldShow = false;
|
|
||||||
|
|
||||||
protected override void OnInitialized()
|
|
||||||
{
|
|
||||||
Id = GetHashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Show(bool focus = true) // Focus can be specified to fix issues with other components
|
|
||||||
{
|
|
||||||
ShouldShow = true;
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
|
|
||||||
await ModalService.Show("modal" + Id, focus);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Hide()
|
|
||||||
{
|
|
||||||
await ModalService.Hide("modal" + Id);
|
|
||||||
|
|
||||||
ShouldShow = false;
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
12
Moonlight/Core/UI/Components/Partials/SmartRouter.razor
Normal file
12
Moonlight/Core/UI/Components/Partials/SmartRouter.razor
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<CascadingValue TValue="SmartRouter" Value="@this">
|
||||||
|
@ChildContent
|
||||||
|
</CascadingValue>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
[Parameter]
|
||||||
|
public string? Route { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment ChildContent { get; set; }
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
@using System.Diagnostics
|
@using System.Diagnostics
|
||||||
@using Moonlight.Core.Exceptions
|
@using MoonCore.Exceptions
|
||||||
@using Moonlight.Core.Helpers
|
@using MoonCore.Helpers
|
||||||
@using Moonlight.Core.Models.Enums
|
@using Moonlight.Core.Models.Enums
|
||||||
@using Moonlight.Core.Services
|
@using Moonlight.Core.Services
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +1,26 @@
|
|||||||
@using Moonlight.Core.Models.Abstractions
|
@using Moonlight.Core.Models.Abstractions
|
||||||
@using Moonlight.Core.Models.Enums
|
@using Moonlight.Core.Models.Enums
|
||||||
@using Moonlight.Core.Services
|
@using Moonlight.Core.Services
|
||||||
@using Moonlight.Core.Services.Interop
|
|
||||||
@using Moonlight.Core.Services.Utils
|
@using Moonlight.Core.Services.Utils
|
||||||
@using Moonlight.Core.Event
|
@using Moonlight.Core.Event
|
||||||
@using Moonlight.Core.UI.Components.Auth
|
@using Moonlight.Core.UI.Components.Auth
|
||||||
@using Moonlight.Features.Advertisement.Services
|
@using Moonlight.Features.Advertisement.Services
|
||||||
@using Moonlight.Features.Advertisement.UI.Components
|
@using Moonlight.Features.Advertisement.UI.Components
|
||||||
|
@using MoonCoreUI.Services
|
||||||
|
@using MoonCore.Services
|
||||||
|
@using Moonlight.Core.Configuration
|
||||||
|
|
||||||
@inherits LayoutComponentBase
|
@inherits LayoutComponentBase
|
||||||
@implements IDisposable
|
@implements IDisposable
|
||||||
|
|
||||||
@inject CookieService CookieService
|
@inject CookieService CookieService
|
||||||
@inject ConfigService ConfigService
|
@inject ConfigService<ConfigV1> ConfigService
|
||||||
@inject IdentityService IdentityService
|
@inject IdentityService IdentityService
|
||||||
@inject SessionService SessionService
|
@inject SessionService SessionService
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
@inject ConnectionService ConnectionService
|
@inject ConnectionService ConnectionService
|
||||||
@inject AdBlockService AdBlockService
|
@inject AdBlockService AdBlockService
|
||||||
|
@inject HotKeyService HotKeyService
|
||||||
|
|
||||||
@{
|
@{
|
||||||
var url = new Uri(Navigation.Uri);
|
var url = new Uri(Navigation.Uri);
|
||||||
@@ -162,6 +165,8 @@ else
|
|||||||
if(ConfigService.Get().Advertisement.PreventAdBlockers)
|
if(ConfigService.Get().Advertisement.PreventAdBlockers)
|
||||||
AdBlockerDetected = await AdBlockService.Detect();
|
AdBlockerDetected = await AdBlockService.Detect();
|
||||||
|
|
||||||
|
await HotKeyService.Initialize();
|
||||||
|
|
||||||
Initialized = true;
|
Initialized = true;
|
||||||
|
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
@page "/account"
|
@page "/account"
|
||||||
@using Moonlight.Core.Models.Forms
|
|
||||||
@using Moonlight.Core.Helpers
|
|
||||||
@using Moonlight.Core.Models.Forms.Auth
|
@using Moonlight.Core.Models.Forms.Auth
|
||||||
@using Moonlight.Core.Services
|
@using Moonlight.Core.Services
|
||||||
@using Moonlight.Core.Services.Interop
|
|
||||||
@using Moonlight.Core.Services.Users
|
@using Moonlight.Core.Services.Users
|
||||||
|
@using MoonCoreUI.Services
|
||||||
|
@using MoonCore.Helpers
|
||||||
|
|
||||||
@inject IdentityService IdentityService
|
@inject IdentityService IdentityService
|
||||||
@inject UserService UserService
|
@inject UserService UserService
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
@page "/account/payments"
|
@page "/account/payments"
|
||||||
@using BlazorTable
|
@using BlazorTable
|
||||||
@using Moonlight.Core.Exceptions
|
@using MoonCore.Exceptions
|
||||||
@using Moonlight.Core.Helpers
|
@using MoonCore.Helpers
|
||||||
@using Moonlight.Core.Models.Abstractions
|
@using MoonCore.Services
|
||||||
|
@using MoonCoreUI.Services
|
||||||
|
@using Moonlight.Core.Configuration
|
||||||
@using Moonlight.Core.Services
|
@using Moonlight.Core.Services
|
||||||
@using Moonlight.Core.Services.Interop
|
|
||||||
@using Moonlight.Features.StoreSystem.Entities
|
@using Moonlight.Features.StoreSystem.Entities
|
||||||
@using Moonlight.Features.StoreSystem.Models.Abstractions
|
@using Moonlight.Features.StoreSystem.Models.Abstractions
|
||||||
@using Moonlight.Features.StoreSystem.Services
|
@using Moonlight.Features.StoreSystem.Services
|
||||||
|
|
||||||
@inject IdentityService IdentityService
|
@inject IdentityService IdentityService
|
||||||
@inject ConfigService ConfigService
|
@inject ConfigService<ConfigV1> ConfigService
|
||||||
@inject StoreService StoreService
|
@inject StoreService StoreService
|
||||||
@inject ToastService ToastService
|
@inject ToastService ToastService
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
@using QRCoder
|
@using QRCoder
|
||||||
@using Moonlight.Core.Models.Forms.Auth
|
@using Moonlight.Core.Models.Forms.Auth
|
||||||
@using Moonlight.Core.Services
|
@using Moonlight.Core.Services
|
||||||
@using Moonlight.Core.Services.Interop
|
|
||||||
@using Moonlight.Core.Services.Users
|
@using Moonlight.Core.Services.Users
|
||||||
@using Moonlight.Core.Exceptions
|
|
||||||
@using Moonlight.Core.Models.Enums
|
@using Moonlight.Core.Models.Enums
|
||||||
|
@using MoonCore.Exceptions
|
||||||
|
@using MoonCoreUI.Services
|
||||||
|
|
||||||
@inject IdentityService IdentityService
|
@inject IdentityService IdentityService
|
||||||
@inject UserService UserService
|
@inject UserService UserService
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
@using Moonlight.Core.Extensions.Attributes
|
@using Moonlight.Core.Extensions.Attributes
|
||||||
@using Moonlight.Core.Models.Enums
|
@using Moonlight.Core.Models.Enums
|
||||||
@using Moonlight.Core.Services
|
@using Moonlight.Core.Services
|
||||||
@using Moonlight.Core.Services.Interop
|
@using MoonCoreUI.Services
|
||||||
|
|
||||||
@attribute [RequirePermission(Permission.AdminRoot)]
|
@attribute [RequirePermission(Permission.AdminRoot)]
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
@page "/admin/sys/settings"
|
@page "/admin/sys/settings"
|
||||||
@using System.Reflection
|
@using System.Reflection
|
||||||
|
@using MoonCore.Services
|
||||||
|
@using MoonCoreUI.Helpers
|
||||||
|
@using MoonCoreUI.Services
|
||||||
|
@using Moonlight.Core.Configuration
|
||||||
@using Moonlight.Core.Extensions.Attributes
|
@using Moonlight.Core.Extensions.Attributes
|
||||||
@using Moonlight.Core.Helpers
|
|
||||||
@using Moonlight.Core.Models.Enums
|
@using Moonlight.Core.Models.Enums
|
||||||
@using Moonlight.Core.Services
|
|
||||||
@using Moonlight.Core.Services.Interop
|
|
||||||
|
|
||||||
@attribute [RequirePermission(Permission.AdminRoot)]
|
@attribute [RequirePermission(Permission.AdminRoot)]
|
||||||
|
|
||||||
@inject ConfigService ConfigService
|
@inject ConfigService<ConfigV1> ConfigService
|
||||||
@inject ToastService ToastService
|
@inject ToastService ToastService
|
||||||
|
|
||||||
<AdminSysNavigation Index="1" />
|
<AdminSysNavigation Index="1" />
|
||||||
@@ -70,7 +71,7 @@ else
|
|||||||
|
|
||||||
@if (Path.Length != 0)
|
@if (Path.Length != 0)
|
||||||
{
|
{
|
||||||
<div class="d-flex flex-stack">
|
<div class="d-flex flex-stack mt-5">
|
||||||
<div class="d-flex align-items-center flex-row-fluid flex-wrap">
|
<div class="d-flex align-items-center flex-row-fluid flex-wrap">
|
||||||
<a href="/admin/sys/@(GetBackPath())" class="fs-4 text-primary">Back</a>
|
<a href="/admin/sys/@(GetBackPath())" class="fs-4 text-primary">Back</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
@page "/admin/users"
|
@page "/admin/users"
|
||||||
@using BlazorTable
|
@using BlazorTable
|
||||||
|
@using MoonCore.Abstractions
|
||||||
|
@using MoonCore.Helpers
|
||||||
@using Moonlight.Core.Database.Entities
|
@using Moonlight.Core.Database.Entities
|
||||||
@using Moonlight.Core.Extensions.Attributes
|
@using Moonlight.Core.Extensions.Attributes
|
||||||
@using Moonlight.Core.Helpers
|
|
||||||
@using Moonlight.Core.Models.Enums
|
@using Moonlight.Core.Models.Enums
|
||||||
@using Moonlight.Core.Repositories
|
|
||||||
|
|
||||||
@attribute [RequirePermission(Permission.AdminUsers)]
|
@attribute [RequirePermission(Permission.AdminUsers)]
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
@page "/admin/users/sessions"
|
@page "/admin/users/sessions"
|
||||||
@using BlazorTable
|
@using BlazorTable
|
||||||
|
@using MoonCore.Helpers
|
||||||
@using Moonlight.Core.Extensions.Attributes
|
@using Moonlight.Core.Extensions.Attributes
|
||||||
@using Moonlight.Core.Helpers
|
|
||||||
@using Moonlight.Core.Models.Abstractions
|
@using Moonlight.Core.Models.Abstractions
|
||||||
@using Moonlight.Core.Models.Enums
|
@using Moonlight.Core.Models.Enums
|
||||||
@using Moonlight.Core.Services
|
@using Moonlight.Core.Services
|
||||||
|
|||||||
@@ -2,14 +2,16 @@
|
|||||||
@using Microsoft.EntityFrameworkCore
|
@using Microsoft.EntityFrameworkCore
|
||||||
@using Mappy.Net
|
@using Mappy.Net
|
||||||
@using BlazorTable
|
@using BlazorTable
|
||||||
|
@using MoonCore.Abstractions
|
||||||
|
@using MoonCore.Helpers
|
||||||
|
@using MoonCore.Services
|
||||||
|
@using MoonCoreUI.Services
|
||||||
|
@using Moonlight.Core.Configuration
|
||||||
@using Moonlight.Core.Database.Entities
|
@using Moonlight.Core.Database.Entities
|
||||||
@using Moonlight.Core.Extensions.Attributes
|
@using Moonlight.Core.Extensions.Attributes
|
||||||
@using Moonlight.Core.Helpers
|
|
||||||
@using Moonlight.Core.Models.Enums
|
@using Moonlight.Core.Models.Enums
|
||||||
@using Moonlight.Core.Models.Forms.Admin.Users
|
@using Moonlight.Core.Models.Forms.Admin.Users
|
||||||
@using Moonlight.Core.Repositories
|
|
||||||
@using Moonlight.Core.Services
|
@using Moonlight.Core.Services
|
||||||
@using Moonlight.Core.Services.Interop
|
|
||||||
@using Moonlight.Core.Services.Users
|
@using Moonlight.Core.Services.Users
|
||||||
@using Moonlight.Features.ServiceManagement.Entities
|
@using Moonlight.Features.ServiceManagement.Entities
|
||||||
@using Moonlight.Features.StoreSystem.Entities
|
@using Moonlight.Features.StoreSystem.Entities
|
||||||
@@ -21,7 +23,7 @@
|
|||||||
@inject SessionService SessionService
|
@inject SessionService SessionService
|
||||||
@inject UserService UserService
|
@inject UserService UserService
|
||||||
@inject ToastService ToastService
|
@inject ToastService ToastService
|
||||||
@inject ConfigService ConfigService
|
@inject ConfigService<ConfigV1> ConfigService
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
|
|
||||||
<LazyLoader Load="Load" ShowAsCard="true">
|
<LazyLoader Load="Load" ShowAsCard="true">
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ WORKDIR /app
|
|||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
EXPOSE 443
|
EXPOSE 443
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
|
FROM mcr.microsoft.com/dotnet/sdk:7.0.100-preview.3 AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
COPY ["Moonlight/Moonlight.csproj", "Moonlight/"]
|
COPY ["Moonlight/Moonlight.csproj", "Moonlight/"]
|
||||||
RUN dotnet restore "Moonlight/Moonlight.csproj"
|
RUN dotnet restore "Moonlight/Moonlight.csproj"
|
||||||
@@ -17,4 +17,6 @@ RUN dotnet publish "Moonlight.csproj" -c Release -o /app/publish /p:UseAppHost=f
|
|||||||
FROM base AS final
|
FROM base AS final
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=publish /app/publish .
|
COPY --from=publish /app/publish .
|
||||||
|
RUN mkdir -p /app/storage
|
||||||
|
RUN rm -r /app/storage/*
|
||||||
ENTRYPOINT ["dotnet", "Moonlight.dll"]
|
ENTRYPOINT ["dotnet", "Moonlight.dll"]
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
using Microsoft.JSInterop;
|
using Microsoft.JSInterop;
|
||||||
using Moonlight.Core.Helpers;
|
using MoonCore.Attributes;
|
||||||
|
using MoonCore.Helpers;
|
||||||
|
|
||||||
namespace Moonlight.Features.Advertisement.Services;
|
namespace Moonlight.Features.Advertisement.Services;
|
||||||
|
|
||||||
|
[Scoped]
|
||||||
public class AdBlockService
|
public class AdBlockService
|
||||||
{
|
{
|
||||||
private readonly IJSRuntime JsRuntime;
|
private readonly IJSRuntime JsRuntime;
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using MoonCore.Abstractions;
|
||||||
|
using MoonCore.Attributes;
|
||||||
|
using MoonCore.Exceptions;
|
||||||
using Moonlight.Core.Database.Entities;
|
using Moonlight.Core.Database.Entities;
|
||||||
using Moonlight.Core.Event;
|
using Moonlight.Core.Event;
|
||||||
using Moonlight.Core.Exceptions;
|
|
||||||
using Moonlight.Core.Extensions;
|
using Moonlight.Core.Extensions;
|
||||||
using Moonlight.Core.Repositories;
|
|
||||||
using Moonlight.Features.Community.Entities;
|
using Moonlight.Features.Community.Entities;
|
||||||
using Moonlight.Features.Community.Entities.Enums;
|
using Moonlight.Features.Community.Entities.Enums;
|
||||||
|
|
||||||
namespace Moonlight.Features.Community.Services;
|
namespace Moonlight.Features.Community.Services;
|
||||||
|
|
||||||
|
[Scoped]
|
||||||
public class PostService
|
public class PostService
|
||||||
{
|
{
|
||||||
private readonly Repository<Post> PostRepository;
|
private readonly Repository<Post> PostRepository;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
@using Moonlight.Core.Services
|
@using Moonlight.Core.Services
|
||||||
@using Moonlight.Core.Services.Interop
|
|
||||||
@using Moonlight.Features.Community.Entities.Enums
|
@using Moonlight.Features.Community.Entities.Enums
|
||||||
@using Moonlight.Features.Community.Models.Forms
|
@using Moonlight.Features.Community.Models.Forms
|
||||||
@using Moonlight.Features.Community.Services
|
@using Moonlight.Features.Community.Services
|
||||||
|
@using MoonCoreUI.Services
|
||||||
|
|
||||||
@inject PostService PostService
|
@inject PostService PostService
|
||||||
@inject IdentityService IdentityService
|
@inject IdentityService IdentityService
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
@using Ganss.Xss
|
@using Ganss.Xss
|
||||||
@using Microsoft.EntityFrameworkCore
|
@using Microsoft.EntityFrameworkCore
|
||||||
@using Moonlight.Core.Helpers
|
@using MoonCore.Abstractions
|
||||||
|
@using MoonCore.Helpers
|
||||||
|
@using MoonCoreUI.Services
|
||||||
@using Moonlight.Core.Models.Enums
|
@using Moonlight.Core.Models.Enums
|
||||||
@using Moonlight.Core.Repositories
|
|
||||||
@using Moonlight.Core.Services
|
@using Moonlight.Core.Services
|
||||||
@using Moonlight.Core.Services.Interop
|
|
||||||
@using Moonlight.Features.Community.Entities
|
@using Moonlight.Features.Community.Entities
|
||||||
@using Moonlight.Features.Community.Services
|
@using Moonlight.Features.Community.Services
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
@page "/admin/community/filter"
|
@page "/admin/community/filter"
|
||||||
|
|
||||||
@using BlazorTable
|
@using BlazorTable
|
||||||
|
@using MoonCore.Abstractions
|
||||||
@using Moonlight.Core.Extensions.Attributes
|
@using Moonlight.Core.Extensions.Attributes
|
||||||
@using Moonlight.Core.Models.Enums
|
@using Moonlight.Core.Models.Enums
|
||||||
@using Moonlight.Core.Repositories
|
|
||||||
@using Moonlight.Features.Community.Entities
|
@using Moonlight.Features.Community.Entities
|
||||||
@using Moonlight.Features.Community.Models.Forms.Admin
|
@using Moonlight.Features.Community.Models.Forms.Admin
|
||||||
@using Moonlight.Features.Community.UI.Components
|
@using Moonlight.Features.Community.UI.Components
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
@page "/community/events"
|
@page "/community/events"
|
||||||
|
|
||||||
@using Microsoft.EntityFrameworkCore
|
@using Microsoft.EntityFrameworkCore
|
||||||
|
@using MoonCore.Abstractions
|
||||||
@using Moonlight.Core.Models.Enums
|
@using Moonlight.Core.Models.Enums
|
||||||
@using Moonlight.Core.Repositories
|
|
||||||
@using Moonlight.Core.Services
|
@using Moonlight.Core.Services
|
||||||
@using Moonlight.Features.Community.Entities
|
@using Moonlight.Features.Community.Entities
|
||||||
@using Moonlight.Features.Community.Entities.Enums
|
@using Moonlight.Features.Community.Entities.Enums
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
@page "/community"
|
@page "/community"
|
||||||
|
|
||||||
@using Microsoft.EntityFrameworkCore
|
@using Microsoft.EntityFrameworkCore
|
||||||
|
@using MoonCore.Abstractions
|
||||||
@using Moonlight.Core.Models.Enums
|
@using Moonlight.Core.Models.Enums
|
||||||
@using Moonlight.Core.Repositories
|
|
||||||
@using Moonlight.Core.Services
|
@using Moonlight.Core.Services
|
||||||
@using Moonlight.Features.Community.Entities
|
@using Moonlight.Features.Community.Entities
|
||||||
@using Moonlight.Features.Community.Entities.Enums
|
@using Moonlight.Features.Community.Entities.Enums
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
@page "/community/projects"
|
@page "/community/projects"
|
||||||
|
|
||||||
@using Microsoft.EntityFrameworkCore
|
@using Microsoft.EntityFrameworkCore
|
||||||
@using Moonlight.Core.Repositories
|
@using MoonCore.Abstractions
|
||||||
|
|
||||||
@using Moonlight.Features.Community.Entities
|
@using Moonlight.Features.Community.Entities
|
||||||
@using Moonlight.Features.Community.Entities.Enums
|
@using Moonlight.Features.Community.Entities.Enums
|
||||||
@using Moonlight.Features.Community.UI.Components
|
@using Moonlight.Features.Community.UI.Components
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Moonlight.Features.FileManager.Configuration;
|
||||||
|
|
||||||
|
public class FileManagerData
|
||||||
|
{
|
||||||
|
[JsonProperty("MaxFileOpenSize")]
|
||||||
|
[Description(
|
||||||
|
"This specifies the maximum file size a user will be able to open in the file editor in kilobytes")]
|
||||||
|
public int MaxFileOpenSize { get; set; } = 1024 * 5; // 5 MB
|
||||||
|
|
||||||
|
[JsonProperty("OperationTimeout")]
|
||||||
|
[Description("This specifies the general timeout for file manager operations. This can but has not to be used by file accesses")]
|
||||||
|
public int OperationTimeout { get; set; } = 5;
|
||||||
|
}
|
||||||
210
Moonlight/Features/FileManager/Helpers/EditorModeDetector.cs
Normal file
210
Moonlight/Features/FileManager/Helpers/EditorModeDetector.cs
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
namespace Moonlight.Features.FileManager.Helpers;
|
||||||
|
|
||||||
|
public static class EditorModeDetector
|
||||||
|
{
|
||||||
|
// We probably will never need every of this modes ;)
|
||||||
|
private static readonly Dictionary<string, string[]> ExtensionIndex = new()
|
||||||
|
{
|
||||||
|
{ "abap", new[] { "abap" } },
|
||||||
|
{ "abc", new[] { "abc" } },
|
||||||
|
{ "actionscript", new[] { "as" } },
|
||||||
|
{ "ada", new[] { "ada", "adb" } },
|
||||||
|
{ "alda", new[] { "alda" } },
|
||||||
|
{ "apache_conf", new[] { "htaccess", "htgroups", "htpasswd", "conf", "htaccess", "htgroups", "htpasswd" } },
|
||||||
|
{ "apex", new[] { "apex", "cls", "trigger", "tgr" } },
|
||||||
|
{ "aql", new[] { "aql" } },
|
||||||
|
{ "asciidoc", new[] { "asciidoc", "adoc" } },
|
||||||
|
{ "asl", new[] { "dsl", "asl", "asl.json" } },
|
||||||
|
{ "assembly_x86", new[] { "asm", "a" } },
|
||||||
|
{ "astro", new[] { "astro" } },
|
||||||
|
{ "autohotkey", new[] { "ahk" } },
|
||||||
|
{ "batchfile", new[] { "bat", "cmd" } },
|
||||||
|
{ "bibtex", new[] { "bib" } },
|
||||||
|
{ "c_cpp", new[] { "cpp", "c", "cc", "cxx", "h", "hh", "hpp", "ino" } },
|
||||||
|
{ "c9search", new[] { "c9search_results" } },
|
||||||
|
{ "cirru", new[] { "cirru", "cr" } },
|
||||||
|
{ "clojure", new[] { "clj", "cljs" } },
|
||||||
|
{ "cobol", new[] { "cbl", "cob" } },
|
||||||
|
{ "coffee", new[] { "coffee", "cf", "cson", "cakefile" } },
|
||||||
|
{ "coldfusion", new[] { "cfm", "cfc" } },
|
||||||
|
{ "crystal", new[] { "cr" } },
|
||||||
|
{ "csharp", new[] { "cs" } },
|
||||||
|
{ "csound_document", new[] { "csd" } },
|
||||||
|
{ "csound_orchestra", new[] { "orc" } },
|
||||||
|
{ "csound_score", new[] { "sco" } },
|
||||||
|
{ "css", new[] { "css" } },
|
||||||
|
{ "curly", new[] { "curly" } },
|
||||||
|
{ "cuttlefish", new[] { "conf" } },
|
||||||
|
{ "d", new[] { "d", "di" } },
|
||||||
|
{ "dart", new[] { "dart" } },
|
||||||
|
{ "diff", new[] { "diff", "patch" } },
|
||||||
|
{ "django", new[] { "djt", "html.djt", "dj.html", "djhtml" } },
|
||||||
|
{ "dockerfile", new[] { "dockerfile" } },
|
||||||
|
{ "dot", new[] { "dot" } },
|
||||||
|
{ "drools", new[] { "drl" } },
|
||||||
|
{ "edifact", new[] { "edi" } },
|
||||||
|
{ "eiffel", new[] { "e", "ge" } },
|
||||||
|
{ "ejs", new[] { "ejs" } },
|
||||||
|
{ "elixir", new[] { "ex", "exs" } },
|
||||||
|
{ "elm", new[] { "elm" } },
|
||||||
|
{ "erlang", new[] { "erl", "hrl" } },
|
||||||
|
{ "flix", new[] { "flix" } },
|
||||||
|
{ "forth", new[] { "frt", "fs", "ldr", "fth", "4th" } },
|
||||||
|
{ "fortran", new[] { "f", "f90" } },
|
||||||
|
{ "fsharp", new[] { "fsi", "fs", "ml", "mli", "fsx", "fsscript" } },
|
||||||
|
{ "fsl", new[] { "fsl" } },
|
||||||
|
{ "ftl", new[] { "ftl" } },
|
||||||
|
{ "gcode", new[] { "gcode" } },
|
||||||
|
{ "gherkin", new[] { "feature" } },
|
||||||
|
{ "gitignore", new[] { ".gitignore" } },
|
||||||
|
{ "glsl", new[] { "glsl", "frag", "vert" } },
|
||||||
|
{ "gobstones", new[] { "gbs" } },
|
||||||
|
{ "golang", new[] { "go" } },
|
||||||
|
{ "graphqlschema", new[] { "gql" } },
|
||||||
|
{ "groovy", new[] { "groovy" } },
|
||||||
|
{ "haml", new[] { "haml" } },
|
||||||
|
{ "handlebars", new[] { "hbs", "handlebars", "tpl", "mustache" } },
|
||||||
|
{ "haskell", new[] { "hs" } },
|
||||||
|
{ "haskell_cabal", new[] { "cabal" } },
|
||||||
|
{ "haxe", new[] { "hx" } },
|
||||||
|
{ "hjson", new[] { "hjson" } },
|
||||||
|
{ "html", new[] { "html", "htm", "xhtml", "vue", "we", "wpy" } },
|
||||||
|
{ "html_elixir", new[] { "eex", "html.eex" } },
|
||||||
|
{ "html_ruby", new[] { "erb", "rhtml", "html.erb" } },
|
||||||
|
{ "ini", new[] { "ini", "conf", "cfg", "prefs" } },
|
||||||
|
{ "io", new[] { "io" } },
|
||||||
|
{ "ion", new[] { "ion" } },
|
||||||
|
{ "jack", new[] { "jack" } },
|
||||||
|
{ "jade", new[] { "jade", "pug" } },
|
||||||
|
{ "java", new[] { "java" } },
|
||||||
|
{ "javascript", new[] { "js", "jsm", "jsx", "cjs", "mjs" } },
|
||||||
|
{ "jexl", new[] { "jexl" } },
|
||||||
|
{ "json", new[] { "json" } },
|
||||||
|
{ "json5", new[] { "json5" } },
|
||||||
|
{ "jsoniq", new[] { "jq" } },
|
||||||
|
{ "jsp", new[] { "jsp" } },
|
||||||
|
{ "jssm", new[] { "jssm", "jssm_state" } },
|
||||||
|
{ "jsx", new[] { "jsx" } },
|
||||||
|
{ "julia", new[] { "jl" } },
|
||||||
|
{ "kotlin", new[] { "kt", "kts" } },
|
||||||
|
{ "latex", new[] { "tex", "latex", "ltx", "bib" } },
|
||||||
|
{ "latte", new[] { "latte" } },
|
||||||
|
{ "less", new[] { "less" } },
|
||||||
|
{ "liquid", new[] { "liquid" } },
|
||||||
|
{ "lisp", new[] { "lisp" } },
|
||||||
|
{ "livescript", new[] { "ls" } },
|
||||||
|
{ "log", new[] { "log" } },
|
||||||
|
{ "logiql", new[] { "logic", "lql" } },
|
||||||
|
{ "logtalk", new[] { "lgt" } },
|
||||||
|
{ "lsl", new[] { "lsl" } },
|
||||||
|
{ "lua", new[] { "lua" } },
|
||||||
|
{ "luapage", new[] { "lp" } },
|
||||||
|
{ "lucene", new[] { "lucene" } },
|
||||||
|
{ "makefile", new[] { "makefile", "gnumakefile", "makefile", "ocamlmakefile", "make" } },
|
||||||
|
{ "markdown", new[] { "md", "markdown" } },
|
||||||
|
{ "mask", new[] { "mask" } },
|
||||||
|
{ "matlab", new[] { "matlab" } },
|
||||||
|
{ "maze", new[] { "mz" } },
|
||||||
|
{ "mediawiki", new[] { "wiki", "mediawiki" } },
|
||||||
|
{ "mel", new[] { "mel" } },
|
||||||
|
{ "mips", new[] { "s", "asm" } },
|
||||||
|
{ "mixal", new[] { "mixal" } },
|
||||||
|
{ "mushcode", new[] { "mc", "mush" } },
|
||||||
|
{ "mysql", new[] { "mysql" } },
|
||||||
|
{ "nasal", new[] { "nas" } },
|
||||||
|
{ "nginx", new[] { "nginx", "conf" } },
|
||||||
|
{ "nim", new[] { "nim" } },
|
||||||
|
{ "nix", new[] { "nix" } },
|
||||||
|
{ "nsis", new[] { "nsi", "nsh" } },
|
||||||
|
{ "nunjucks", new[] { "nunjucks", "nunjs", "nj", "njk" } },
|
||||||
|
{ "objectivec", new[] { "m", "mm" } },
|
||||||
|
{ "ocaml", new[] { "ml", "mli" } },
|
||||||
|
{ "odin", new[] { "odin" } },
|
||||||
|
{ "partiql", new[] { "partiql", "pql" } },
|
||||||
|
{ "pascal", new[] { "pas", "p" } },
|
||||||
|
{ "perl", new[] { "pl", "pm" } },
|
||||||
|
{ "pgsql", new[] { "pgsql" } },
|
||||||
|
{ "php", new[] { "php", "inc", "phtml", "shtml", "php3", "php4", "php5", "phps", "phpt", "aw", "ctp", "module" } },
|
||||||
|
{ "php_laravel_blade", new[] { "blade.php" } },
|
||||||
|
{ "pig", new[] { "pig" } },
|
||||||
|
{ "plsql", new[] { "plsql" } },
|
||||||
|
{ "powershell", new[] { "ps1" } },
|
||||||
|
{ "praat", new[] { "praat", "praatscript", "psc", "proc" } },
|
||||||
|
{ "prisma", new[] { "prisma" } },
|
||||||
|
{ "prolog", new[] { "plg", "prolog" } },
|
||||||
|
{ "properties", new[] { "properties" } },
|
||||||
|
{ "protobuf", new[] { "proto" } },
|
||||||
|
{ "prql", new[] { "prql" } },
|
||||||
|
{ "puppet", new[] { "epp", "pp" } },
|
||||||
|
{ "python", new[] { "py" } },
|
||||||
|
{ "qml", new[] { "qml" } },
|
||||||
|
{ "r", new[] { "r" } },
|
||||||
|
{ "raku", new[] { "raku", "rakumod", "rakutest", "p6", "pl6", "pm6" } },
|
||||||
|
{ "razor", new[] { "cshtml", "asp" } },
|
||||||
|
{ "rdoc", new[] { "rd" } },
|
||||||
|
{ "red", new[] { "red", "reds" } },
|
||||||
|
{ "rhtml", new[] { "rhtml" } },
|
||||||
|
{ "robot", new[] { "robot", "resource" } },
|
||||||
|
{ "rst", new[] { "rst" } },
|
||||||
|
{ "ruby", new[] { "rb", "ru", "gemspec", "rake", "guardfile", "rakefile", "gemfile" } },
|
||||||
|
{ "rust", new[] { "rs" } },
|
||||||
|
{ "sac", new[] { "sac" } },
|
||||||
|
{ "sass", new[] { "sass" } },
|
||||||
|
{ "scad", new[] { "scad" } },
|
||||||
|
{ "scala", new[] { "scala", "sbt" } },
|
||||||
|
{ "scheme", new[] { "scm", "sm", "rkt", "oak", "scheme" } },
|
||||||
|
{ "scrypt", new[] { "scrypt" } },
|
||||||
|
{ "scss", new[] { "scss" } },
|
||||||
|
{ "sh", new[] { "sh", "bash", ".bashrc" } },
|
||||||
|
{ "sjs", new[] { "sjs" } },
|
||||||
|
{ "slim", new[] { "slim", "skim" } },
|
||||||
|
{ "smarty", new[] { "smarty", "tpl" } },
|
||||||
|
{ "smithy", new[] { "smithy" } },
|
||||||
|
{ "snippets", new[] { "snippets" } },
|
||||||
|
{ "soy_template", new[] { "soy" } },
|
||||||
|
{ "space", new[] { "space" } },
|
||||||
|
{ "sparql", new[] { "rq" } },
|
||||||
|
{ "sql", new[] { "sql" } },
|
||||||
|
{ "sqlserver", new[] { "sqlserver" } },
|
||||||
|
{ "stylus", new[] { "styl", "stylus" } },
|
||||||
|
{ "svg", new[] { "svg" } },
|
||||||
|
{ "swift", new[] { "swift" } },
|
||||||
|
{ "tcl", new[] { "tcl" } },
|
||||||
|
{ "terraform", new[] { "tf", "tfvars", "terragrunt" } },
|
||||||
|
{ "tex", new[] { "tex" } },
|
||||||
|
{ "text", new[] { "txt" } },
|
||||||
|
{ "textile", new[] { "textile" } },
|
||||||
|
{ "toml", new[] { "toml" } },
|
||||||
|
{ "tsx", new[] { "tsx" } },
|
||||||
|
{ "turtle", new[] { "ttl" } },
|
||||||
|
{ "twig", new[] { "twig", "swig" } },
|
||||||
|
{ "typescript", new[] { "ts", "mts", "cts", "typescript", "str" } },
|
||||||
|
{ "vala", new[] { "vala" } },
|
||||||
|
{ "vbscript", new[] { "vbs", "vb" } },
|
||||||
|
{ "velocity", new[] { "vm" } },
|
||||||
|
{ "verilog", new[] { "v", "vh", "sv", "svh" } },
|
||||||
|
{ "vhdl", new[] { "vhd", "vhdl" } },
|
||||||
|
{ "visualforce", new[] { "vfp", "component", "page" } },
|
||||||
|
{ "wollok", new[] { "wlk", "wpgm", "wtest" } },
|
||||||
|
{ "xml", new[] { "xml", "rdf", "rss", "wsdl", "xslt", "atom", "mathml", "mml", "xul", "xbl", "xaml" } },
|
||||||
|
{ "xquery", new[] { "xq" } },
|
||||||
|
{ "yaml", new[] { "yaml", "yml" } },
|
||||||
|
{ "zeek", new[] { "zeek", "bro" } }
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string GetModeFromFile(string fileName)
|
||||||
|
{
|
||||||
|
var extension = Path.GetExtension(fileName).Replace(".", "");
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(extension))
|
||||||
|
return "text";
|
||||||
|
|
||||||
|
foreach (var entry in ExtensionIndex)
|
||||||
|
{
|
||||||
|
if (entry.Value.Any(x => string.Equals(x, extension, StringComparison.InvariantCultureIgnoreCase)))
|
||||||
|
return entry.Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "text";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using MoonCore.Helpers;
|
||||||
|
using Moonlight.Core.Services.Utils;
|
||||||
|
using Moonlight.Features.FileManager.Services;
|
||||||
|
|
||||||
|
namespace Moonlight.Features.FileManager.Http.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/download")]
|
||||||
|
public class DownloadController : Controller
|
||||||
|
{
|
||||||
|
private readonly JwtService JwtService;
|
||||||
|
private readonly SharedFileAccessService SharedFileAccessService;
|
||||||
|
|
||||||
|
public DownloadController(JwtService jwtService, SharedFileAccessService sharedFileAccessService)
|
||||||
|
{
|
||||||
|
JwtService = jwtService;
|
||||||
|
SharedFileAccessService = sharedFileAccessService;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<ActionResult> Upload([FromQuery(Name = "token")] string downloadToken, [FromQuery(Name = "name")] string name)
|
||||||
|
{
|
||||||
|
if (name.Contains(".."))
|
||||||
|
{
|
||||||
|
Logger.Warn($"A user tried to access a file via path transversal. Name: {name}");
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate request
|
||||||
|
if (!await JwtService.Validate(downloadToken, "FileAccess"))
|
||||||
|
return StatusCode(403);
|
||||||
|
|
||||||
|
var downloadContext = await JwtService.Decode(downloadToken);
|
||||||
|
|
||||||
|
if (!downloadContext.ContainsKey("FileAccessId"))
|
||||||
|
return BadRequest();
|
||||||
|
|
||||||
|
if (!int.TryParse(downloadContext["FileAccessId"], out int fileAccessId))
|
||||||
|
return BadRequest();
|
||||||
|
|
||||||
|
// Load file access for this file
|
||||||
|
var fileAccess = await SharedFileAccessService.Get(fileAccessId);
|
||||||
|
|
||||||
|
if (fileAccess == null)
|
||||||
|
return BadRequest("Invalid file access id");
|
||||||
|
|
||||||
|
var files = await fileAccess.List();
|
||||||
|
|
||||||
|
if (files.All(x => !x.IsFile && x.Name != name))
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
var stream = await fileAccess.ReadFileStream(name);
|
||||||
|
|
||||||
|
return File(stream, "application/octet-stream", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Moonlight.Core.Services.Utils;
|
||||||
|
using Moonlight.Features.FileManager.Services;
|
||||||
|
|
||||||
|
namespace Moonlight.Features.FileManager.Http.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/upload")]
|
||||||
|
public class UploadController : Controller
|
||||||
|
{
|
||||||
|
private readonly JwtService JwtService;
|
||||||
|
private readonly SharedFileAccessService SharedFileAccessService;
|
||||||
|
|
||||||
|
public UploadController(
|
||||||
|
JwtService jwtService,
|
||||||
|
SharedFileAccessService sharedFileAccessService)
|
||||||
|
{
|
||||||
|
JwtService = jwtService;
|
||||||
|
SharedFileAccessService = sharedFileAccessService;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following method/api endpoint needs some explanation:
|
||||||
|
// Because of blazor and dropzone.js, we need an api endpoint
|
||||||
|
// to upload files via the built in file manager.
|
||||||
|
// As we learned from user experiences in v1b
|
||||||
|
// a large data transfer via the signal r connection might lead to
|
||||||
|
// failing uploads for some users with a unstable connection. That's
|
||||||
|
// why we implement this api endpoint. It can potentially prevent
|
||||||
|
// upload from malicious scripts as well. To verify the user is
|
||||||
|
// authenticated we use a jwt.
|
||||||
|
// The jwt specifies what
|
||||||
|
// connection we want to upload the file. This jwt
|
||||||
|
// will be generated every 5 minutes in the file upload service
|
||||||
|
// and only last 6 minutes.
|
||||||
|
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<ActionResult> Upload([FromQuery(Name = "token")] string uploadToken)
|
||||||
|
{
|
||||||
|
// Check if a file exist and if it is not too big
|
||||||
|
if (!Request.Form.Files.Any())
|
||||||
|
return BadRequest("File is missing in request");
|
||||||
|
|
||||||
|
if (Request.Form.Files.Count > 1)
|
||||||
|
return BadRequest("Too many files sent");
|
||||||
|
|
||||||
|
// Validate request
|
||||||
|
if (!await JwtService.Validate(uploadToken, "FileAccess"))
|
||||||
|
return StatusCode(403);
|
||||||
|
|
||||||
|
var uploadContext = await JwtService.Decode(uploadToken);
|
||||||
|
|
||||||
|
if (!uploadContext.ContainsKey("FileAccessId"))
|
||||||
|
return BadRequest();
|
||||||
|
|
||||||
|
if (!int.TryParse(uploadContext["FileAccessId"], out int fileAccessId))
|
||||||
|
return BadRequest();
|
||||||
|
|
||||||
|
// Load file access for this file
|
||||||
|
var fileAccess = await SharedFileAccessService.Get(fileAccessId);
|
||||||
|
|
||||||
|
if (fileAccess == null)
|
||||||
|
return BadRequest("Invalid file access id");
|
||||||
|
|
||||||
|
// Actually upload the file
|
||||||
|
var file = Request.Form.Files.First();
|
||||||
|
await fileAccess.WriteFileStream(file.FileName, file.OpenReadStream());
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
fileAccess.Dispose();
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
|
||||||
|
|
||||||
|
public class FileEntry
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public long Size { get; set; }
|
||||||
|
public bool IsFile { get; set; }
|
||||||
|
public bool IsDirectory { get; set; }
|
||||||
|
public DateTime LastModifiedAt { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
namespace Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
|
||||||
|
|
||||||
|
public interface IFileAccess : IDisposable
|
||||||
|
{
|
||||||
|
public Task<FileEntry[]> List();
|
||||||
|
public Task ChangeDirectory(string relativePath);
|
||||||
|
public Task SetDirectory(string path);
|
||||||
|
public Task<string> GetCurrentDirectory();
|
||||||
|
public Task Delete(string path);
|
||||||
|
public Task Move(string from, string to);
|
||||||
|
public Task CreateDirectory(string name);
|
||||||
|
public Task CreateFile(string name);
|
||||||
|
public Task<string> ReadFile(string name);
|
||||||
|
public Task WriteFile(string name, string content);
|
||||||
|
public Task<Stream> ReadFileStream(string name);
|
||||||
|
public Task WriteFileStream(string name, Stream dataStream);
|
||||||
|
public IFileAccess Clone();
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
|
||||||
|
|
||||||
|
public interface IFileCompressAccess
|
||||||
|
{
|
||||||
|
public Task Compress(string[] names);
|
||||||
|
public Task Decompress(string name);
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace Moonlight.Features.FileManager.Models.Abstractions.FileAccess;
|
||||||
|
|
||||||
|
public interface IFileLaunchAccess
|
||||||
|
{
|
||||||
|
public Task<string> GetLaunchUrl();
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user