Merge branch 'main' into DiscordBot

This commit is contained in:
Spielepapagei
2023-04-04 01:20:12 +02:00
123 changed files with 10387 additions and 1389 deletions

View File

@@ -2,6 +2,7 @@
using Moonlight.App.Database.Entities;
using Moonlight.App.Database.Entities.LogsEntries;
using Moonlight.App.Database.Entities.Notification;
using Moonlight.App.Models.Misc;
using Moonlight.App.Services;
namespace Moonlight.App.Database;
@@ -34,14 +35,13 @@ public class DataContext : DbContext
public DbSet<SharedDomain> SharedDomains { get; set; }
public DbSet<Domain> Domains { get; set; }
public DbSet<Subscription> Subscriptions { get; set; }
public DbSet<SubscriptionLimit> SubscriptionLimits { get; set; }
public DbSet<Revoke> Revokes { get; set; }
public DbSet<NotificationClient> NotificationClients { get; set; }
public DbSet<NotificationAction> NotificationActions { get; set; }
public DbSet<AaPanel> AaPanels { get; set; }
public DbSet<Website> Websites { get; set; }
public DbSet<DdosAttack> DdosAttacks { get; set; }
public DbSet<Subscription> Subscriptions { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{

View File

@@ -1,8 +1,12 @@
namespace Moonlight.App.Database.Entities;
using System.ComponentModel.DataAnnotations;
namespace Moonlight.App.Database.Entities;
public class Domain
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public SharedDomain SharedDomain { get; set; }
public User Owner { get; set; }

View File

@@ -20,4 +20,5 @@ public class Server
public NodeAllocation MainAllocation { get; set; } = null!;
public Node Node { get; set; } = null!;
public User Owner { get; set; } = null!;
public bool IsCleanupException { get; set; } = false;
}

View File

@@ -5,7 +5,5 @@ public class Subscription
public int Id { get; set; }
public string Name { get; set; } = "";
public string Description { get; set; } = "";
public string SellPassId { get; set; } = "";
public int Duration { get; set; }
public List<SubscriptionLimit> Limits { get; set; } = new();
public string LimitsJson { get; set; } = "";
}

View File

@@ -1,11 +0,0 @@
namespace Moonlight.App.Database.Entities;
public class SubscriptionLimit
{
public int Id { get; set; }
public Image Image { get; set; } = null!;
public int Amount { get; set; }
public int Cpu { get; set; }
public int Memory { get; set; }
public int Disk { get; set; }
}

View File

@@ -1,4 +1,5 @@
using Moonlight.App.Models.Misc;
using System.ComponentModel.DataAnnotations;
using Moonlight.App.Models.Misc;
namespace Moonlight.App.Database.Entities;
@@ -7,14 +8,21 @@ public class User
public int Id { get; set; }
// Personal data
public string FirstName { get; set; } = "";
public string LastName { get; set; } = "";
public string Email { get; set; } = "";
public string Password { get; set; } = "";
public string Address { get; set; } = "";
public string City { get; set; } = "";
public string State { get; set; } = "";
public string Country { get; set; } = "";
// States
@@ -34,9 +42,10 @@ public class User
// Date stuff
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
// Subscriptions
public Subscription? Subscription { get; set; } = null;
public DateTime? SubscriptionSince { get; set; }
public Subscription? CurrentSubscription { get; set; } = null;
public DateTime SubscriptionSince { get; set; } = DateTime.Now;
public int SubscriptionDuration { get; set; }
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,170 @@
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
/// <inheritdoc />
public partial class AddCleanupExceptionsTable : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "State",
table: "Users",
type: "varchar(64)",
maxLength: 64,
nullable: false,
oldClrType: typeof(string),
oldType: "longtext")
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "LastName",
table: "Users",
type: "varchar(64)",
maxLength: 64,
nullable: false,
oldClrType: typeof(string),
oldType: "longtext")
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "FirstName",
table: "Users",
type: "varchar(64)",
maxLength: 64,
nullable: false,
oldClrType: typeof(string),
oldType: "longtext")
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "Country",
table: "Users",
type: "varchar(64)",
maxLength: 64,
nullable: false,
oldClrType: typeof(string),
oldType: "longtext")
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "City",
table: "Users",
type: "varchar(128)",
maxLength: 128,
nullable: false,
oldClrType: typeof(string),
oldType: "longtext")
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "Address",
table: "Users",
type: "varchar(128)",
maxLength: 128,
nullable: false,
oldClrType: typeof(string),
oldType: "longtext")
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "CleanupExceptions",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
ServerId = table.Column<int>(type: "int", nullable: false),
Note = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_CleanupExceptions", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "CleanupExceptions");
migrationBuilder.AlterColumn<string>(
name: "State",
table: "Users",
type: "longtext",
nullable: false,
oldClrType: typeof(string),
oldType: "varchar(64)",
oldMaxLength: 64)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "LastName",
table: "Users",
type: "longtext",
nullable: false,
oldClrType: typeof(string),
oldType: "varchar(64)",
oldMaxLength: 64)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "FirstName",
table: "Users",
type: "longtext",
nullable: false,
oldClrType: typeof(string),
oldType: "varchar(64)",
oldMaxLength: 64)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "Country",
table: "Users",
type: "longtext",
nullable: false,
oldClrType: typeof(string),
oldType: "varchar(64)",
oldMaxLength: 64)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "City",
table: "Users",
type: "longtext",
nullable: false,
oldClrType: typeof(string),
oldType: "varchar(128)",
oldMaxLength: 128)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "Address",
table: "Users",
type: "longtext",
nullable: false,
oldClrType: typeof(string),
oldType: "varchar(128)",
oldMaxLength: 128)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,51 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
/// <inheritdoc />
public partial class ChengedCleanupExceptionModel : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "CreatedAt",
table: "CleanupExceptions",
type: "datetime(6)",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.CreateIndex(
name: "IX_CleanupExceptions_ServerId",
table: "CleanupExceptions",
column: "ServerId");
migrationBuilder.AddForeignKey(
name: "FK_CleanupExceptions_Servers_ServerId",
table: "CleanupExceptions",
column: "ServerId",
principalTable: "Servers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_CleanupExceptions_Servers_ServerId",
table: "CleanupExceptions");
migrationBuilder.DropIndex(
name: "IX_CleanupExceptions_ServerId",
table: "CleanupExceptions");
migrationBuilder.DropColumn(
name: "CreatedAt",
table: "CleanupExceptions");
}
}
}

View File

@@ -0,0 +1,62 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
/// <inheritdoc />
public partial class RemovedCleanupExceptionChangedServerModel : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "CleanupExceptions");
migrationBuilder.AddColumn<bool>(
name: "IsCleanupException",
table: "Servers",
type: "tinyint(1)",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsCleanupException",
table: "Servers");
migrationBuilder.CreateTable(
name: "CleanupExceptions",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
ServerId = table.Column<int>(type: "int", nullable: false),
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
Note = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_CleanupExceptions", x => x.Id);
table.ForeignKey(
name: "FK_CleanupExceptions_Servers_ServerId",
column: x => x.ServerId,
principalTable: "Servers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_CleanupExceptions_ServerId",
table: "CleanupExceptions",
column: "ServerId");
}
}
}

View File

@@ -0,0 +1,962 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Moonlight.App.Database;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
[DbContext(typeof(DataContext))]
[Migration("20230403130734_RemovedOldSubscriptionData")]
partial class RemovedOldSubscriptionData
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.3")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
modelBuilder.Entity("Moonlight.App.Database.Entities.AaPanel", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("BaseDomain")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Key")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Url")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("AaPanels");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Database", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("AaPanelId")
.HasColumnType("int");
b.Property<int>("InternalAaPanelId")
.HasColumnType("int");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("OwnerId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("AaPanelId");
b.HasIndex("OwnerId");
b.ToTable("Databases");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.DdosAttack", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<long>("Data")
.HasColumnType("bigint");
b.Property<string>("Ip")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("NodeId")
.HasColumnType("int");
b.Property<bool>("Ongoing")
.HasColumnType("tinyint(1)");
b.HasKey("Id");
b.HasIndex("NodeId");
b.ToTable("DdosAttacks");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<bool>("Default")
.HasColumnType("tinyint(1)");
b.Property<int?>("ImageId")
.HasColumnType("int");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("ImageId");
b.ToTable("DockerImages");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Domain", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("OwnerId")
.HasColumnType("int");
b.Property<int>("SharedDomainId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("OwnerId");
b.HasIndex("SharedDomainId");
b.ToTable("Domains");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("Allocations")
.HasColumnType("int");
b.Property<string>("ConfigFiles")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Description")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("InstallDockerImage")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("InstallEntrypoint")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("InstallScript")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Startup")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("StartupDetection")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("StopCommand")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("TagsJson")
.IsRequired()
.HasColumnType("longtext");
b.Property<Guid>("Uuid")
.HasColumnType("char(36)");
b.HasKey("Id");
b.ToTable("Images");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.ImageTag", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("ImageTags");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.ImageVariable", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("DefaultValue")
.IsRequired()
.HasColumnType("longtext");
b.Property<int?>("ImageId")
.HasColumnType("int");
b.Property<string>("Key")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("ImageId");
b.ToTable("ImageVariables");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.LoadingMessage", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Message")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("LoadingMessages");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.AuditLogEntry", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Ip")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("JsonData")
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("System")
.HasColumnType("tinyint(1)");
b.Property<int>("Type")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("AuditLog");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.ErrorLogEntry", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Class")
.IsRequired()
.HasColumnType("longtext");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Ip")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("JsonData")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Stacktrace")
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("System")
.HasColumnType("tinyint(1)");
b.HasKey("Id");
b.ToTable("ErrorLog");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.SecurityLogEntry", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Ip")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("JsonData")
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("System")
.HasColumnType("tinyint(1)");
b.Property<int>("Type")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("SecurityLog");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Node", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Fqdn")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("HttpPort")
.HasColumnType("int");
b.Property<int>("MoonlightDaemonPort")
.HasColumnType("int");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("SftpPort")
.HasColumnType("int");
b.Property<bool>("Ssl")
.HasColumnType("tinyint(1)");
b.Property<string>("Token")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("TokenId")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("Nodes");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.NodeAllocation", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int?>("NodeId")
.HasColumnType("int");
b.Property<int>("Port")
.HasColumnType("int");
b.Property<int?>("ServerId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("NodeId");
b.HasIndex("ServerId");
b.ToTable("NodeAllocations");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationAction", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Action")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("NotificationClientId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("NotificationClientId");
b.ToTable("NotificationActions");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationClient", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("NotificationClients");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Revoke", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Identifier")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("Revokes");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("Cpu")
.HasColumnType("int");
b.Property<long>("Disk")
.HasColumnType("bigint");
b.Property<int>("DockerImageIndex")
.HasColumnType("int");
b.Property<int>("ImageId")
.HasColumnType("int");
b.Property<bool>("Installing")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsCleanupException")
.HasColumnType("tinyint(1)");
b.Property<int>("MainAllocationId")
.HasColumnType("int");
b.Property<long>("Memory")
.HasColumnType("bigint");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("NodeId")
.HasColumnType("int");
b.Property<string>("OverrideStartup")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("OwnerId")
.HasColumnType("int");
b.Property<bool>("Suspended")
.HasColumnType("tinyint(1)");
b.Property<Guid>("Uuid")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("ImageId");
b.HasIndex("MainAllocationId");
b.HasIndex("NodeId");
b.HasIndex("OwnerId");
b.ToTable("Servers");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.ServerBackup", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<long>("Bytes")
.HasColumnType("bigint");
b.Property<bool>("Created")
.HasColumnType("tinyint(1)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<int?>("ServerId")
.HasColumnType("int");
b.Property<Guid>("Uuid")
.HasColumnType("char(36)");
b.HasKey("Id");
b.HasIndex("ServerId");
b.ToTable("ServerBackups");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.ServerVariable", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Key")
.IsRequired()
.HasColumnType("longtext");
b.Property<int?>("ServerId")
.HasColumnType("int");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("ServerId");
b.ToTable("ServerVariables");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.SharedDomain", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("CloudflareId")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("SharedDomains");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Answer")
.IsRequired()
.HasColumnType("longtext");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<bool>("IsQuestion")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsSupport")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsSystem")
.HasColumnType("tinyint(1)");
b.Property<string>("Message")
.IsRequired()
.HasColumnType("longtext");
b.Property<int?>("RecipientId")
.HasColumnType("int");
b.Property<int?>("SenderId")
.HasColumnType("int");
b.Property<int>("Type")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("RecipientId");
b.HasIndex("SenderId");
b.ToTable("SupportMessages");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Address")
.IsRequired()
.HasColumnType("longtext");
b.Property<bool>("Admin")
.HasColumnType("tinyint(1)");
b.Property<string>("City")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Country")
.IsRequired()
.HasColumnType("longtext");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<long>("DiscordId")
.HasColumnType("bigint");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("State")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<bool>("SupportPending")
.HasColumnType("tinyint(1)");
b.Property<DateTime>("TokenValidTime")
.HasColumnType("datetime(6)");
b.Property<bool>("TotpEnabled")
.HasColumnType("tinyint(1)");
b.Property<string>("TotpSecret")
.IsRequired()
.HasColumnType("longtext");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("datetime(6)");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("AaPanelId")
.HasColumnType("int");
b.Property<string>("DomainName")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("FtpPassword")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("FtpUsername")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("InternalAaPanelId")
.HasColumnType("int");
b.Property<int>("OwnerId")
.HasColumnType("int");
b.Property<string>("PhpVersion")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("AaPanelId");
b.HasIndex("OwnerId");
b.ToTable("Websites");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Database", b =>
{
b.HasOne("Moonlight.App.Database.Entities.AaPanel", "AaPanel")
.WithMany()
.HasForeignKey("AaPanelId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.User", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("AaPanel");
b.Navigation("Owner");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.DdosAttack", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Node", "Node")
.WithMany()
.HasForeignKey("NodeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Node");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.DockerImage", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Image", null)
.WithMany("DockerImages")
.HasForeignKey("ImageId");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Domain", b =>
{
b.HasOne("Moonlight.App.Database.Entities.User", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.SharedDomain", "SharedDomain")
.WithMany()
.HasForeignKey("SharedDomainId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Owner");
b.Navigation("SharedDomain");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.ImageVariable", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Image", null)
.WithMany("Variables")
.HasForeignKey("ImageId");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.NodeAllocation", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Node", null)
.WithMany("Allocations")
.HasForeignKey("NodeId");
b.HasOne("Moonlight.App.Database.Entities.Server", null)
.WithMany("Allocations")
.HasForeignKey("ServerId");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationAction", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Notification.NotificationClient", "NotificationClient")
.WithMany()
.HasForeignKey("NotificationClientId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("NotificationClient");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Notification.NotificationClient", b =>
{
b.HasOne("Moonlight.App.Database.Entities.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Image", "Image")
.WithMany()
.HasForeignKey("ImageId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.NodeAllocation", "MainAllocation")
.WithMany()
.HasForeignKey("MainAllocationId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.Node", "Node")
.WithMany()
.HasForeignKey("NodeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.User", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Image");
b.Navigation("MainAllocation");
b.Navigation("Node");
b.Navigation("Owner");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.ServerBackup", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Server", null)
.WithMany("Backups")
.HasForeignKey("ServerId");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.ServerVariable", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Server", null)
.WithMany("Variables")
.HasForeignKey("ServerId");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b =>
{
b.HasOne("Moonlight.App.Database.Entities.User", "Recipient")
.WithMany()
.HasForeignKey("RecipientId");
b.HasOne("Moonlight.App.Database.Entities.User", "Sender")
.WithMany()
.HasForeignKey("SenderId");
b.Navigation("Recipient");
b.Navigation("Sender");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b =>
{
b.HasOne("Moonlight.App.Database.Entities.AaPanel", "AaPanel")
.WithMany()
.HasForeignKey("AaPanelId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.User", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("AaPanel");
b.Navigation("Owner");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b =>
{
b.Navigation("DockerImages");
b.Navigation("Variables");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Node", b =>
{
b.Navigation("Allocations");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b =>
{
b.Navigation("Allocations");
b.Navigation("Backups");
b.Navigation("Variables");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,269 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
/// <inheritdoc />
public partial class RemovedOldSubscriptionData : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Users_Subscriptions_SubscriptionId",
table: "Users");
migrationBuilder.DropTable(
name: "SubscriptionLimits");
migrationBuilder.DropTable(
name: "Subscriptions");
migrationBuilder.DropIndex(
name: "IX_Users_SubscriptionId",
table: "Users");
migrationBuilder.DropColumn(
name: "SubscriptionDuration",
table: "Users");
migrationBuilder.DropColumn(
name: "SubscriptionId",
table: "Users");
migrationBuilder.DropColumn(
name: "SubscriptionSince",
table: "Users");
migrationBuilder.AlterColumn<string>(
name: "State",
table: "Users",
type: "longtext",
nullable: false,
oldClrType: typeof(string),
oldType: "varchar(64)",
oldMaxLength: 64)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "LastName",
table: "Users",
type: "longtext",
nullable: false,
oldClrType: typeof(string),
oldType: "varchar(64)",
oldMaxLength: 64)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "FirstName",
table: "Users",
type: "longtext",
nullable: false,
oldClrType: typeof(string),
oldType: "varchar(64)",
oldMaxLength: 64)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "Country",
table: "Users",
type: "longtext",
nullable: false,
oldClrType: typeof(string),
oldType: "varchar(64)",
oldMaxLength: 64)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "City",
table: "Users",
type: "longtext",
nullable: false,
oldClrType: typeof(string),
oldType: "varchar(128)",
oldMaxLength: 128)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "Address",
table: "Users",
type: "longtext",
nullable: false,
oldClrType: typeof(string),
oldType: "varchar(128)",
oldMaxLength: 128)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "State",
table: "Users",
type: "varchar(64)",
maxLength: 64,
nullable: false,
oldClrType: typeof(string),
oldType: "longtext")
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "LastName",
table: "Users",
type: "varchar(64)",
maxLength: 64,
nullable: false,
oldClrType: typeof(string),
oldType: "longtext")
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "FirstName",
table: "Users",
type: "varchar(64)",
maxLength: 64,
nullable: false,
oldClrType: typeof(string),
oldType: "longtext")
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "Country",
table: "Users",
type: "varchar(64)",
maxLength: 64,
nullable: false,
oldClrType: typeof(string),
oldType: "longtext")
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "City",
table: "Users",
type: "varchar(128)",
maxLength: 128,
nullable: false,
oldClrType: typeof(string),
oldType: "longtext")
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AlterColumn<string>(
name: "Address",
table: "Users",
type: "varchar(128)",
maxLength: 128,
nullable: false,
oldClrType: typeof(string),
oldType: "longtext")
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AddColumn<int>(
name: "SubscriptionDuration",
table: "Users",
type: "int",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<int>(
name: "SubscriptionId",
table: "Users",
type: "int",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "SubscriptionSince",
table: "Users",
type: "datetime(6)",
nullable: true);
migrationBuilder.CreateTable(
name: "Subscriptions",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Description = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Duration = table.Column<int>(type: "int", nullable: false),
Name = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
SellPassId = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_Subscriptions", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateTable(
name: "SubscriptionLimits",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
ImageId = table.Column<int>(type: "int", nullable: false),
Amount = table.Column<int>(type: "int", nullable: false),
Cpu = table.Column<int>(type: "int", nullable: false),
Disk = table.Column<int>(type: "int", nullable: false),
Memory = table.Column<int>(type: "int", nullable: false),
SubscriptionId = table.Column<int>(type: "int", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_SubscriptionLimits", x => x.Id);
table.ForeignKey(
name: "FK_SubscriptionLimits_Images_ImageId",
column: x => x.ImageId,
principalTable: "Images",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_SubscriptionLimits_Subscriptions_SubscriptionId",
column: x => x.SubscriptionId,
principalTable: "Subscriptions",
principalColumn: "Id");
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_Users_SubscriptionId",
table: "Users",
column: "SubscriptionId");
migrationBuilder.CreateIndex(
name: "IX_SubscriptionLimits_ImageId",
table: "SubscriptionLimits",
column: "ImageId");
migrationBuilder.CreateIndex(
name: "IX_SubscriptionLimits_SubscriptionId",
table: "SubscriptionLimits",
column: "SubscriptionId");
migrationBuilder.AddForeignKey(
name: "FK_Users_Subscriptions_SubscriptionId",
table: "Users",
column: "SubscriptionId",
principalTable: "Subscriptions",
principalColumn: "Id");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,94 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
/// <inheritdoc />
public partial class AddedNewSubscriptionData : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "CurrentSubscriptionId",
table: "Users",
type: "int",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "SubscriptionDuration",
table: "Users",
type: "int",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<DateTime>(
name: "SubscriptionSince",
table: "Users",
type: "datetime(6)",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.CreateTable(
name: "Subscriptions",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Name = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Description = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
LimitsJson = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_Subscriptions", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_Users_CurrentSubscriptionId",
table: "Users",
column: "CurrentSubscriptionId");
migrationBuilder.AddForeignKey(
name: "FK_Users_Subscriptions_CurrentSubscriptionId",
table: "Users",
column: "CurrentSubscriptionId",
principalTable: "Subscriptions",
principalColumn: "Id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Users_Subscriptions_CurrentSubscriptionId",
table: "Users");
migrationBuilder.DropTable(
name: "Subscriptions");
migrationBuilder.DropIndex(
name: "IX_Users_CurrentSubscriptionId",
table: "Users");
migrationBuilder.DropColumn(
name: "CurrentSubscriptionId",
table: "Users");
migrationBuilder.DropColumn(
name: "SubscriptionDuration",
table: "Users");
migrationBuilder.DropColumn(
name: "SubscriptionSince",
table: "Users");
}
}
}

View File

@@ -482,6 +482,9 @@ namespace Moonlight.App.Database.Migrations
b.Property<bool>("Installing")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsCleanupException")
.HasColumnType("tinyint(1)");
b.Property<int>("MainAllocationId")
.HasColumnType("int");
@@ -606,14 +609,11 @@ namespace Moonlight.App.Database.Migrations
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("Duration")
.HasColumnType("int");
b.Property<string>("Name")
b.Property<string>("LimitsJson")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("SellPassId")
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
@@ -622,39 +622,6 @@ namespace Moonlight.App.Database.Migrations
b.ToTable("Subscriptions");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.SubscriptionLimit", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("Amount")
.HasColumnType("int");
b.Property<int>("Cpu")
.HasColumnType("int");
b.Property<int>("Disk")
.HasColumnType("int");
b.Property<int>("ImageId")
.HasColumnType("int");
b.Property<int>("Memory")
.HasColumnType("int");
b.Property<int?>("SubscriptionId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("ImageId");
b.HasIndex("SubscriptionId");
b.ToTable("SubscriptionLimits");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b =>
{
b.Property<int>("Id")
@@ -723,6 +690,9 @@ namespace Moonlight.App.Database.Migrations
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<int?>("CurrentSubscriptionId")
.HasColumnType("int");
b.Property<long>("DiscordId")
.HasColumnType("bigint");
@@ -752,10 +722,7 @@ namespace Moonlight.App.Database.Migrations
b.Property<int>("SubscriptionDuration")
.HasColumnType("int");
b.Property<int?>("SubscriptionId")
.HasColumnType("int");
b.Property<DateTime?>("SubscriptionSince")
b.Property<DateTime>("SubscriptionSince")
.HasColumnType("datetime(6)");
b.Property<bool>("SupportPending")
@@ -776,7 +743,7 @@ namespace Moonlight.App.Database.Migrations
b.HasKey("Id");
b.HasIndex("SubscriptionId");
b.HasIndex("CurrentSubscriptionId");
b.ToTable("Users");
});
@@ -966,21 +933,6 @@ namespace Moonlight.App.Database.Migrations
.HasForeignKey("ServerId");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.SubscriptionLimit", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Image", "Image")
.WithMany()
.HasForeignKey("ImageId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.Subscription", null)
.WithMany("Limits")
.HasForeignKey("SubscriptionId");
b.Navigation("Image");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b =>
{
b.HasOne("Moonlight.App.Database.Entities.User", "Recipient")
@@ -998,11 +950,11 @@ namespace Moonlight.App.Database.Migrations
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
{
b.HasOne("Moonlight.App.Database.Entities.Subscription", "Subscription")
b.HasOne("Moonlight.App.Database.Entities.Subscription", "CurrentSubscription")
.WithMany()
.HasForeignKey("SubscriptionId");
.HasForeignKey("CurrentSubscriptionId");
b.Navigation("Subscription");
b.Navigation("CurrentSubscription");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b =>
@@ -1044,11 +996,6 @@ namespace Moonlight.App.Database.Migrations
b.Navigation("Variables");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Subscription", b =>
{
b.Navigation("Limits");
});
#pragma warning restore 612, 618
}
}

View File

@@ -0,0 +1,11 @@
using Microsoft.AspNetCore.Components.Forms;
namespace Moonlight.App.Helpers;
public class FieldCssHelper : FieldCssClassProvider
{
public override string GetFieldCssClass(EditContext editContext, in FieldIdentifier fieldIdentifier)
{
return editContext.GetValidationMessages(fieldIdentifier).Any() ? "is-invalid" : "is-valid";
}
}

View File

@@ -0,0 +1,8 @@
namespace Moonlight.App.Helpers.Files;
public class ContextAction
{
public string Id { get; set; } = "";
public string Name { get; set; } = "";
public Action<FileData> Action { get; set; }
}

View File

@@ -0,0 +1,24 @@
namespace Moonlight.App.Helpers.Files;
public abstract class FileAccess : ICloneable
{
public string CurrentPath { get; set; } = "/";
public abstract Task<FileData[]> Ls();
public abstract Task Cd(string dir);
public abstract Task Up();
public abstract Task SetDir(string dir);
public abstract Task<string> Read(FileData fileData);
public abstract Task Write(FileData fileData, string content);
public abstract Task Upload(string name, Stream stream, Action<int>? progressUpdated = null);
public abstract Task MkDir(string name);
public abstract Task<string> Pwd();
public abstract Task<string> DownloadUrl(FileData fileData);
public abstract Task<Stream> DownloadStream(FileData fileData);
public abstract Task Delete(FileData fileData);
public abstract Task Move(FileData fileData, string newPath);
public abstract Task Compress(params FileData[] files);
public abstract Task Decompress(FileData fileData);
public abstract Task<string> GetLaunchUrl();
public abstract object Clone();
}

View File

@@ -0,0 +1,8 @@
namespace Moonlight.App.Helpers.Files;
public class FileData
{
public string Name { get; set; } = "";
public long Size { get; set; }
public bool IsFile { get; set; }
}

View File

@@ -0,0 +1,232 @@
using System.Web;
using Moonlight.App.Database.Entities;
using Moonlight.App.Models.Wings.Requests;
using Moonlight.App.Models.Wings.Resources;
using Moonlight.App.Services;
using RestSharp;
namespace Moonlight.App.Helpers.Files;
public class WingsFileAccess : FileAccess
{
private readonly WingsApiHelper WingsApiHelper;
private readonly WingsJwtHelper WingsJwtHelper;
private readonly ConfigService ConfigService;
private readonly Server Server;
private readonly User User;
public WingsFileAccess(
WingsApiHelper wingsApiHelper,
WingsJwtHelper wingsJwtHelper,
Server server,
ConfigService configService,
User user)
{
WingsApiHelper = wingsApiHelper;
WingsJwtHelper = wingsJwtHelper;
Server = server;
ConfigService = configService;
User = user;
if (server.Node == null)
{
throw new ArgumentException("The wings file access server model needs to include the node data");
}
}
public override async Task<FileData[]> Ls()
{
var res = await WingsApiHelper.Get<ListDirectory[]>(
Server.Node,
$"api/servers/{Server.Uuid}/files/list-directory?directory={CurrentPath}"
);
var x = new List<FileData>();
foreach (var response in res)
{
x.Add(new()
{
Name = response.Name,
Size = response.File ? response.Size : 0,
IsFile = response.File,
});
}
return x.ToArray();
}
public override Task Cd(string dir)
{
var x = Path.Combine(CurrentPath, dir).Replace("\\", "/") + "/";
x = x.Replace("//", "/");
CurrentPath = x;
return Task.CompletedTask;
}
public override Task Up()
{
CurrentPath = Path.GetFullPath(Path.Combine(CurrentPath, "..")).Replace("\\", "/").Replace("C:", "");
return Task.CompletedTask;
}
public override Task SetDir(string dir)
{
CurrentPath = dir;
return Task.CompletedTask;
}
public override async Task<string> Read(FileData fileData)
{
return await WingsApiHelper.GetRaw(Server.Node,
$"api/servers/{Server.Uuid}/files/contents?file={CurrentPath}{fileData.Name}");
}
public override async Task Write(FileData fileData, string content)
{
await WingsApiHelper.PostRaw(Server.Node,
$"api/servers/{Server.Uuid}/files/write?file={CurrentPath}{fileData.Name}", content);
}
public override async Task Upload(string name, Stream dataStream, Action<int>? progressUpdated = null)
{
var token = WingsJwtHelper.Generate(
Server.Node.Token,
claims => { claims.Add("server_uuid", Server.Uuid.ToString()); }
);
var client = new RestClient();
var request = new RestRequest();
if (Server.Node.Ssl)
request.Resource =
$"https://{Server.Node.Fqdn}:{Server.Node.HttpPort}/upload/file?token={token}&directory={CurrentPath}";
else
request.Resource =
$"http://{Server.Node.Fqdn}:{Server.Node.HttpPort}/upload/file?token={token}&directory={CurrentPath}";
request.AddParameter("name", "files");
request.AddParameter("filename", name);
request.AddHeader("Content-Type", "multipart/form-data");
request.AddHeader("Origin", ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl"));
request.AddFile("files", () =>
{
return new StreamProgressHelper(dataStream)
{
Progress = i => { progressUpdated?.Invoke(i); }
};
}, name);
await client.ExecutePostAsync(request);
client.Dispose();
dataStream.Close();
}
public override async Task MkDir(string name)
{
await WingsApiHelper.Post(Server.Node, $"api/servers/{Server.Uuid}/files/create-directory",
new CreateDirectory()
{
Name = name,
Path = CurrentPath
}
);
}
public override Task<string> Pwd()
{
return Task.FromResult(CurrentPath);
}
public override Task<string> DownloadUrl(FileData fileData)
{
var token = WingsJwtHelper.Generate(Server.Node.Token, claims =>
{
claims.Add("server_uuid", Server.Uuid.ToString());
claims.Add("file_path", CurrentPath + "/" + fileData.Name);
});
if (Server.Node.Ssl)
{
return Task.FromResult(
$"https://{Server.Node.Fqdn}:{Server.Node.HttpPort}/download/file?token={token}"
);
}
else
{
return Task.FromResult(
$"http://{Server.Node.Fqdn}:{Server.Node.HttpPort}/download/file?token={token}"
);
}
}
public override Task<Stream> DownloadStream(FileData fileData)
{
throw new NotImplementedException();
}
public override async Task Delete(FileData fileData)
{
await WingsApiHelper.Post(Server.Node, $"api/servers/{Server.Uuid}/files/delete", new DeleteFiles()
{
Root = CurrentPath,
Files = new()
{
fileData.Name
}
});
}
public override async Task Move(FileData fileData, string newPath)
{
var req = new RenameFiles()
{
Root = "/",
Files = new[]
{
new RenameFilesData()
{
From = (CurrentPath + fileData.Name),
To = newPath
}
}
};
await WingsApiHelper.Put(Server.Node, $"api/servers/{Server.Uuid}/files/rename", req);
}
public override async Task Compress(params FileData[] files)
{
var req = new CompressFiles()
{
Root = CurrentPath,
Files = files.Select(x => x.Name).ToArray()
};
await WingsApiHelper.Post(Server.Node, $"api/servers/{Server.Uuid}/files/compress", req);
}
public override async Task Decompress(FileData fileData)
{
var req = new DecompressFile()
{
Root = CurrentPath,
File = fileData.Name
};
await WingsApiHelper.Post(Server.Node, $"api/servers/{Server.Uuid}/files/decompress", req);
}
public override Task<string> GetLaunchUrl()
{
return Task.FromResult(
$"sftp://{User.Id}.{StringHelper.IntToStringWithLeadingZeros(Server.Id, 8)}@{Server.Node.Fqdn}:{Server.Node.SftpPort}");
}
public override object Clone()
{
return new WingsFileAccess(WingsApiHelper, WingsJwtHelper, Server, ConfigService, User);
}
}

View File

@@ -24,11 +24,11 @@ public class PaperApiHelper
else
requrl = ApiUrl + "/" + url;
RestRequest request = new(requrl);
RestRequest request = new(requrl, Method.Get);
request.AddHeader("Content-Type", "application/json");
var response = await client.GetAsync(request);
var response = await client.ExecuteAsync(request);
if (!response.IsSuccessful)
{

View File

@@ -14,24 +14,13 @@ public class WingsApiHelper
Client = new();
}
private string GetApiUrl(Node node)
{
if(node.Ssl)
return $"https://{node.Fqdn}:{node.HttpPort}/";
else
return $"http://{node.Fqdn}:{node.HttpPort}/";
//return $"https://{node.Fqdn}:{node.HttpPort}/";
}
public async Task<T> Get<T>(Node node, string resource)
{
RestRequest request = new(GetApiUrl(node) + resource);
var request = CreateRequest(node, resource);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Accept", "application/json");
request.AddHeader("Authorization", "Bearer " + node.Token);
request.Method = Method.Get;
var response = await Client.GetAsync(request);
var response = await Client.ExecuteAsync(request);
if (!response.IsSuccessful)
{
@@ -53,13 +42,11 @@ public class WingsApiHelper
public async Task<string> GetRaw(Node node, string resource)
{
RestRequest request = new(GetApiUrl(node) + resource);
var request = CreateRequest(node, resource);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Accept", "application/json");
request.AddHeader("Authorization", "Bearer " + node.Token);
request.Method = Method.Get;
var response = await Client.GetAsync(request);
var response = await Client.ExecuteAsync(request);
if (!response.IsSuccessful)
{
@@ -81,18 +68,16 @@ public class WingsApiHelper
public async Task<T> Post<T>(Node node, string resource, object? body)
{
RestRequest request = new(GetApiUrl(node) + resource);
var request = CreateRequest(node, resource);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Accept", "application/json");
request.AddHeader("Authorization", "Bearer " + node.Token);
request.Method = Method.Post;
request.AddParameter("text/plain",
JsonConvert.SerializeObject(body),
ParameterType.RequestBody
);
var response = await Client.PostAsync(request);
var response = await Client.ExecuteAsync(request);
if (!response.IsSuccessful)
{
@@ -114,16 +99,14 @@ public class WingsApiHelper
public async Task Post(Node node, string resource, object? body)
{
RestRequest request = new(GetApiUrl(node) + resource);
var request = CreateRequest(node, resource);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Accept", "application/json");
request.AddHeader("Authorization", "Bearer " + node.Token);
request.Method = Method.Post;
if(body != null)
if(body != null)
request.AddParameter("text/plain", JsonConvert.SerializeObject(body), ParameterType.RequestBody);
var response = await Client.PostAsync(request);
var response = await Client.ExecuteAsync(request);
if (!response.IsSuccessful)
{
@@ -143,15 +126,13 @@ public class WingsApiHelper
public async Task PostRaw(Node node, string resource, object body)
{
RestRequest request = new(GetApiUrl(node) + resource);
var request = CreateRequest(node, resource);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Accept", "application/json");
request.AddHeader("Authorization", "Bearer " + node.Token);
request.Method = Method.Post;
request.AddParameter("text/plain", body, ParameterType.RequestBody);
var response = await Client.PostAsync(request);
var response = await Client.ExecuteAsync(request);
if (!response.IsSuccessful)
{
@@ -171,16 +152,14 @@ public class WingsApiHelper
public async Task Delete(Node node, string resource, object? body)
{
RestRequest request = new(GetApiUrl(node) + resource);
var request = CreateRequest(node, resource);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Accept", "application/json");
request.AddHeader("Authorization", "Bearer " + node.Token);
request.Method = Method.Delete;
if(body != null)
if(body != null)
request.AddParameter("text/plain", JsonConvert.SerializeObject(body), ParameterType.RequestBody);
var response = await Client.DeleteAsync(request);
var response = await Client.ExecuteAsync(request);
if (!response.IsSuccessful)
{
@@ -200,15 +179,13 @@ public class WingsApiHelper
public async Task Put(Node node, string resource, object? body)
{
RestRequest request = new(GetApiUrl(node) + resource);
var request = CreateRequest(node, resource);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Accept", "application/json");
request.AddHeader("Authorization", "Bearer " + node.Token);
request.Method = Method.Put;
request.AddParameter("text/plain", JsonConvert.SerializeObject(body), ParameterType.RequestBody);
var response = await Client.PutAsync(request);
var response = await Client.ExecuteAsync(request);
if (!response.IsSuccessful)
{
@@ -225,4 +202,20 @@ public class WingsApiHelper
}
}
}
private RestRequest CreateRequest(Node node, string resource)
{
var url = (node.Ssl ? "https" : "http") + $"://{node.Fqdn}:{node.HttpPort}/" + resource;
var request = new RestRequest(url)
{
Timeout = 60 * 15
};
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Accept", "application/json");
request.AddHeader("Authorization", "Bearer " + node.Token);
return request;
}
}

View File

@@ -80,6 +80,13 @@ public class WingsServerConverter
wingsServer.Settings.Environment.Add(v.Key, v.Value);
}
}
int i = 0;
foreach (var allocation in server.Allocations)
{
wingsServer.Settings.Environment.Add("ML_PORT_" + i, allocation.Port.ToString());
i++;
}
// Stop
if (image.StopCommand.StartsWith("!"))

View File

@@ -1,50 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using Moonlight.App.Services;
namespace Moonlight.App.Http.Controllers.Api.Moonlight;
[ApiController]
[Route("api/moonlight/payments")]
public class PaymentsController : Controller
{
private readonly ConfigService ConfigService;
private readonly SubscriptionService SubscriptionService;
public PaymentsController(ConfigService configService, SubscriptionService subscriptionService)
{
ConfigService = configService;
SubscriptionService = subscriptionService;
}
[HttpGet("generate")]
public async Task<ActionResult> GenerateGet([FromQuery] string key, [FromQuery] int subscriptionId)
{
var validKey = ConfigService
.GetSection("Moonlight")
.GetSection("Payments")
.GetValue<string>("Key");
if (key != validKey)
return StatusCode(403);
var token = await SubscriptionService.ProcessGenerate(subscriptionId);
return Ok(token);
}
[HttpPost("generate")]
public async Task<ActionResult> GeneratePost([FromQuery] string key, [FromQuery] int subscriptionId)
{
var validKey = ConfigService
.GetSection("Moonlight")
.GetSection("Payments")
.GetValue<string>("Key");
if (key != validKey)
return StatusCode(403);
var token = await SubscriptionService.ProcessGenerate(subscriptionId);
return Ok(token);
}
}

View File

@@ -21,7 +21,10 @@ public class ResourcesController : Controller
{
if (name.Contains(".."))
{
await SecurityLogService.Log(SecurityLogType.PathTransversal, name);
await SecurityLogService.Log(SecurityLogType.PathTransversal, x =>
{
x.Add<string>(name);
});
return NotFound();
}

View File

@@ -39,7 +39,7 @@ public class WingsFileAccess : IFileAccess
public async Task<FileManagerObject[]> GetDirectoryContent()
{
var res = await WingsApiHelper.Get<ListDirectoryRequest[]>(Node,
var res = await WingsApiHelper.Get<ListDirectory[]>(Node,
$"api/servers/{Server.Uuid}/files/list-directory?directory={Path}");
var x = new List<FileManagerObject>();
@@ -130,7 +130,7 @@ public class WingsFileAccess : IFileAccess
public async Task CreateDirectory(string name)
{
await WingsApiHelper.Post(Node, $"api/servers/{Server.Uuid}/files/create-directory",
new CreateDirectoryRequest()
new CreateDirectory()
{
Name = name,
Path = Path
@@ -171,7 +171,7 @@ public class WingsFileAccess : IFileAccess
public async Task Delete(FileManagerObject managerObject)
{
await WingsApiHelper.Post(Node, $"api/servers/{Server.Uuid}/files/delete", new DeleteFilesRequest()
await WingsApiHelper.Post(Node, $"api/servers/{Server.Uuid}/files/delete", new DeleteFiles()
{
Root = Path,
Files = new()
@@ -183,7 +183,7 @@ public class WingsFileAccess : IFileAccess
public async Task Move(FileManagerObject managerObject, string newPath)
{
await WingsApiHelper.Put(Node, $"api/servers/{Server.Uuid}/files/rename", new RenameFilesRequest()
await WingsApiHelper.Put(Node, $"api/servers/{Server.Uuid}/files/rename", new RenameFiles()
{
Root = "/",
Files = new[]

View File

@@ -0,0 +1,8 @@
namespace Moonlight.App.Models.Files;
public class FileContextAction
{
public string Id { get; set; }
public string Name { get; set; }
public Action<FileManagerObject> Action { get; set; }
}

View File

@@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations;
namespace Moonlight.App.Models.Forms;
public class LoginDataModel
{
[Required(ErrorMessage = "You need to enter an email address")]
[EmailAddress(ErrorMessage = "You need to enter a valid email address")]
public string Email { get; set; }
[Required(ErrorMessage = "You need to enter a password")]
[MinLength(8, ErrorMessage = "You need to enter a password with minimum 8 characters in lenght")]
public string Password { get; set; }
}

View File

@@ -0,0 +1,9 @@
using System.ComponentModel.DataAnnotations;
namespace Moonlight.App.Models.Forms;
public class LoginTotpDataModel
{
[Required(ErrorMessage = "You need to enter a 2fa code")]
public string Code { get; set; } = "";
}

View File

@@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations;
namespace Moonlight.App.Models.Forms;
public class NameModel
{
[Required]
[MinLength(2, ErrorMessage = "Do you think, that works?")]
public string FirstName { get; set; }
[Required]
[MinLength(2, ErrorMessage = "Do you think, that works?")]
public string LastName { get; set; }
}

View File

@@ -0,0 +1,10 @@
using System.ComponentModel.DataAnnotations;
namespace Moonlight.App.Models.Forms;
public class PasswordModel
{
[Required(ErrorMessage = "You need to enter a password")]
[MinLength(8, ErrorMessage = "You need to enter a password with minimum 8 characters in lenght")]
public string Password { get; set; }
}

View File

@@ -0,0 +1,13 @@
using System.ComponentModel.DataAnnotations;
namespace Moonlight.App.Models.Forms;
public class SubscriptionDataModel
{
[Required(ErrorMessage = "You need to enter a name")]
[MaxLength(32, ErrorMessage = "Max lenght for name is 32")]
public string Name { get; set; } = "";
[Required(ErrorMessage = "You need to enter a description")]
public string Description { get; set; } = "";
}

View File

@@ -0,0 +1,7 @@
namespace Moonlight.App.Models.Log;
public class LogData
{
public Type Type { get; set; }
public string Value { get; set; }
}

View File

@@ -19,5 +19,9 @@ public enum AuditLogType
AddDomainRecord,
UpdateDomainRecord,
DeleteDomainRecord,
PasswordReset
PasswordReset,
CleanupEnabled,
CleanupDisabled,
CleanupTriggered,
PasswordChange,
}

View File

@@ -0,0 +1,14 @@
namespace Moonlight.App.Models.Misc;
public class SubscriptionLimit
{
public string Identifier { get; set; } = "";
public int Amount { get; set; }
public List<LimitOption> Options { get; set; } = new();
public class LimitOption
{
public string Key { get; set; } = "";
public string Value { get; set; } = "";
}
}

View File

@@ -9,5 +9,6 @@ public enum UserStatus
Warned,
Banned,
Disabled,
DataPending
DataPending,
PasswordPending
}

View File

@@ -0,0 +1,12 @@
using Newtonsoft.Json;
namespace Moonlight.App.Models.Wings.Requests;
public class CompressFiles
{
[JsonProperty("root")]
public string Root { get; set; }
[JsonProperty("files")]
public string[] Files { get; set; }
}

View File

@@ -2,7 +2,7 @@
namespace Moonlight.App.Models.Wings.Requests;
public class CreateBackupRequest
public class CreateBackup
{
[JsonProperty("adapter")]
public string Adapter { get; set; }

View File

@@ -2,7 +2,7 @@
namespace Moonlight.App.Models.Wings.Requests;
public class CreateDirectoryRequest
public class CreateDirectory
{
[JsonProperty("name")]
public string Name { get; set; }

View File

@@ -2,7 +2,7 @@
namespace Moonlight.App.Models.Wings.Requests;
public class CreateServerRequest
public class CreateServer
{
[JsonProperty("uuid")]
public Guid Uuid { get; set; }

View File

@@ -0,0 +1,12 @@
using Newtonsoft.Json;
namespace Moonlight.App.Models.Wings.Requests;
public class DecompressFile
{
[JsonProperty("root")]
public string Root { get; set; }
[JsonProperty("file")]
public string File { get; set; }
}

View File

@@ -2,7 +2,7 @@
namespace Moonlight.App.Models.Wings.Requests;
public class DeleteFilesRequest
public class DeleteFiles
{
[JsonProperty("root")]
public string Root { get; set; }

View File

@@ -2,7 +2,7 @@
namespace Moonlight.App.Models.Wings.Requests;
public class RenameFilesRequest
public class RenameFiles
{
[JsonProperty("root")]
public string Root { get; set; }

View File

@@ -2,7 +2,7 @@
namespace Moonlight.App.Models.Wings.Requests;
public class RestoreBackupRequest
public class RestoreBackup
{
[JsonProperty("adapter")]
public string Adapter { get; set; }

View File

@@ -2,7 +2,7 @@
namespace Moonlight.App.Models.Wings.Requests;
public class ServerPowerRequest
public class ServerPower
{
[JsonProperty("action")]
public string Action { get; set; }

View File

@@ -2,7 +2,7 @@
namespace Moonlight.App.Models.Wings.Resources;
public class ListDirectoryRequest
public class ListDirectory
{
[JsonProperty("name")]
public string Name { get; set; }

View File

@@ -2,7 +2,7 @@
namespace Moonlight.App.Models.Wings.Resources;
public class ServerDetailsResponse
public class ServerDetails
{
[JsonProperty("state")]
public string State { get; set; }
@@ -11,9 +11,9 @@ public class ServerDetailsResponse
public bool IsSuspended { get; set; }
[JsonProperty("utilization")]
public ServerDetailsResponseUtilization Utilization { get; set; }
public ServerDetailsUtilization Utilization { get; set; }
public class ServerDetailsResponseUtilization
public class ServerDetailsUtilization
{
[JsonProperty("memory_bytes")]
public long MemoryBytes { get; set; }
@@ -25,7 +25,7 @@ public class ServerDetailsResponse
public double CpuAbsolute { get; set; }
[JsonProperty("network")]
public ServerDetailsResponseNetwork Network { get; set; }
public ServerDetailsNetwork Network { get; set; }
[JsonProperty("uptime")]
public long Uptime { get; set; }
@@ -37,7 +37,7 @@ public class ServerDetailsResponse
public long DiskBytes { get; set; }
}
public class ServerDetailsResponseNetwork
public class ServerDetailsNetwork
{
[JsonProperty("rx_bytes")]
public long RxBytes { get; set; }

View File

@@ -2,7 +2,7 @@
using Moonlight.App.Database;
using Moonlight.App.Database.Entities;
namespace Moonlight.App.Repositories.Subscriptions;
namespace Moonlight.App.Repositories;
public class SubscriptionRepository : IDisposable
{

View File

@@ -1,44 +0,0 @@
using Microsoft.EntityFrameworkCore;
using Moonlight.App.Database;
using Moonlight.App.Database.Entities;
namespace Moonlight.App.Repositories.Subscriptions;
public class SubscriptionLimitRepository : IDisposable
{
private readonly DataContext DataContext;
public SubscriptionLimitRepository(DataContext dataContext)
{
DataContext = dataContext;
}
public DbSet<SubscriptionLimit> Get()
{
return DataContext.SubscriptionLimits;
}
public SubscriptionLimit Add(SubscriptionLimit subscription)
{
var x = DataContext.SubscriptionLimits.Add(subscription);
DataContext.SaveChanges();
return x.Entity;
}
public void Update(SubscriptionLimit subscription)
{
DataContext.SubscriptionLimits.Update(subscription);
DataContext.SaveChanges();
}
public void Delete(SubscriptionLimit subscription)
{
DataContext.SubscriptionLimits.Remove(subscription);
DataContext.SaveChanges();
}
public void Dispose()
{
DataContext.Dispose();
}
}

View File

@@ -0,0 +1,216 @@
using System.Diagnostics;
using Microsoft.EntityFrameworkCore;
using MineStatLib;
using Moonlight.App.Database.Entities;
using Moonlight.App.Models.Daemon.Resources;
using Moonlight.App.Models.Wings;
using Moonlight.App.Repositories;
using Moonlight.App.Repositories.Servers;
using Logging.Net;
using Newtonsoft.Json;
namespace Moonlight.App.Services;
public class CleanupService
{
#region Stats
public DateTime StartedAt { get; private set; }
public DateTime CompletedAt { get; private set; }
public int ServersCleaned { get; private set; }
public int CleanupsPerformed { get; private set; }
public int ServersRunning { get; private set; }
public bool IsRunning { get; private set; }
#endregion
private readonly ConfigService ConfigService;
private readonly MessageService MessageService;
private readonly IServiceScopeFactory ServiceScopeFactory;
private readonly PeriodicTimer Timer;
public CleanupService(
ConfigService configService,
IServiceScopeFactory serviceScopeFactory,
MessageService messageService)
{
ServiceScopeFactory = serviceScopeFactory;
MessageService = messageService;
ConfigService = configService;
StartedAt = DateTime.Now;
CompletedAt = DateTime.Now;
IsRunning = false;
var config = ConfigService.GetSection("Moonlight").GetSection("Cleanup");
if (!config.GetValue<bool>("Enable") || ConfigService.DebugMode)
{
Logger.Info("Disabling cleanup service");
return;
}
Timer = new(TimeSpan.FromMinutes(config.GetValue<int>("Wait")));
Task.Run(Run);
}
private async Task Run()
{
while (await Timer.WaitForNextTickAsync())
{
IsRunning = true;
using var scope = ServiceScopeFactory.CreateScope();
var config = ConfigService.GetSection("Moonlight").GetSection("Cleanup");
var maxCpu = config.GetValue<int>("Cpu");
var minMemory = config.GetValue<int>("Memory");
var maxUptime = config.GetValue<int>("Uptime");
var minUptime = config.GetValue<int>("MinUptime");
var nodeRepository = scope.ServiceProvider.GetRequiredService<NodeRepository>();
var nodeService = scope.ServiceProvider.GetRequiredService<NodeService>();
var nodes = nodeRepository
.Get()
.ToArray();
foreach (var node in nodes)
{
try
{
var cpuStats = await nodeService.GetCpuStats(node);
var memoryStats = await nodeService.GetMemoryStats(node);
if (cpuStats.Usage > maxCpu || memoryStats.Free < minMemory)
{
var containerStats = await nodeService.GetContainerStats(node);
var serverRepository = scope.ServiceProvider.GetRequiredService<ServerRepository>();
var imageRepository = scope.ServiceProvider.GetRequiredService<ImageRepository>();
var images = imageRepository
.Get()
.ToArray();
var imagesWithFlag = images
.Where(x =>
(JsonConvert.DeserializeObject<string[]>(x.TagsJson) ?? Array.Empty<string>()).Contains("cleanup")
)
.ToArray();
var containerMappedToServers = new Dictionary<ContainerStats.Container, Server>();
foreach (var container in containerStats.Containers)
{
if (Guid.TryParse(container.Name, out Guid uuid))
{
var server = serverRepository
.Get()
.Include(x => x.Image)
.Include(x => x.MainAllocation)
.Include(x => x.Variables)
.FirstOrDefault(x => x.Uuid == uuid);
if (server != null && imagesWithFlag.Any(y => y.Id == server.Image.Id))
{
containerMappedToServers.Add(container, server);
}
}
}
var serverService = scope.ServiceProvider.GetRequiredService<ServerService>();
foreach (var containerMapped in containerMappedToServers)
{
var server = containerMapped.Value;
try
{
var stats = await serverService.GetDetails(server);
if (server.IsCleanupException)
{
if (stats.Utilization.Uptime > TimeSpan.FromHours(maxUptime).TotalMilliseconds)
{
var players = GetPlayers(node, server.MainAllocation);
if (players == 0)
{
await serverService.SetPowerState(server, PowerSignal.Restart);
ServersCleaned++;
}
else
{
ServersRunning++;
}
await MessageService.Emit("cleanup.updated", null);
}
}
else
{
if (stats.Utilization.Uptime > TimeSpan.FromMinutes(minUptime).TotalMilliseconds)
{
var players = GetPlayers(node, server.MainAllocation);
if (players < 1)
{
var j2SVar = server.Variables.FirstOrDefault(x => x.Key == "J2S");
var handleJ2S = j2SVar != null && j2SVar.Value == "1";
if (handleJ2S)
{
await serverService.SetPowerState(server, PowerSignal.Restart);
}
else
{
await serverService.SetPowerState(server, PowerSignal.Stop);
}
ServersCleaned++;
}
else
{
ServersRunning++;
}
await MessageService.Emit("cleanup.updated", null);
}
}
}
catch (Exception e)
{
Logger.Warn($"Error checking server {server.Name} ({server.Id})");
Logger.Warn(e);
}
}
}
}
catch (Exception e)
{
Logger.Error($"Error performing cleanup on node {node.Name} ({node.Id})");
Logger.Error(e);
}
}
IsRunning = false;
CleanupsPerformed++;
await MessageService.Emit("cleanup.updated", null);
}
}
private int GetPlayers(Node node, NodeAllocation allocation)
{
var ms = new MineStat(node.Fqdn, (ushort)allocation.Port);
//TODO: Add fake player check
if (ms.ServerUp)
{
return ms.CurrentPlayersInt;
}
return -1;
}
}

View File

@@ -1,4 +1,5 @@
using System.Text;
using Logging.Net;
using Microsoft.Extensions.Primitives;
using Moonlight.App.Helpers;
@@ -21,6 +22,9 @@ public class ConfigService : IConfiguration
if (debugVar != null)
DebugMode = bool.Parse(debugVar);
if(DebugMode)
Logger.Debug("Debug mode enabled");
}
public IEnumerable<IConfigurationSection> GetChildren()

View File

@@ -169,7 +169,11 @@ public class DomainService
}));
}
await AuditLogService.Log(AuditLogType.AddDomainRecord, new[] { d.Id.ToString(), dnsRecord.Name });
await AuditLogService.Log(AuditLogType.AddDomainRecord, x =>
{
x.Add<Domain>(d.Id);
x.Add<DnsRecord>(dnsRecord.Name);
});
}
public async Task UpdateDnsRecord(Domain d, DnsRecord dnsRecord)
@@ -199,7 +203,11 @@ public class DomainService
}));
}
await AuditLogService.Log(AuditLogType.UpdateDomainRecord, new[] { d.Id.ToString(), dnsRecord.Name });
await AuditLogService.Log(AuditLogType.UpdateDomainRecord, x =>
{
x.Add<Domain>(d.Id);
x.Add<DnsRecord>(dnsRecord.Name);
});
}
public async Task DeleteDnsRecord(Domain d, DnsRecord dnsRecord)
@@ -210,7 +218,11 @@ public class DomainService
await Client.Zones.DnsRecords.DeleteAsync(domain.SharedDomain.CloudflareId, dnsRecord.Id)
);
await AuditLogService.Log(AuditLogType.DeleteDomainRecord, new[] { d.Id.ToString(), dnsRecord.Name });
await AuditLogService.Log(AuditLogType.DeleteDomainRecord, x =>
{
x.Add<Domain>(d.Id);
x.Add<DnsRecord>(dnsRecord.Name);
});
}
private Domain EnsureData(Domain domain)

View File

@@ -0,0 +1,23 @@
using Microsoft.JSInterop;
namespace Moonlight.App.Services.Interop;
public class ModalService
{
private readonly IJSRuntime JsRuntime;
public ModalService(IJSRuntime jsRuntime)
{
JsRuntime = jsRuntime;
}
public async Task Show(string name)
{
await JsRuntime.InvokeVoidAsync("moonlight.modals.show", name);
}
public async Task Hide(string name)
{
await JsRuntime.InvokeVoidAsync("moonlight.modals.hide", name);
}
}

View File

@@ -30,4 +30,19 @@ public class ToastService
{
await JsRuntime.InvokeVoidAsync("showSuccessToast", message);
}
public async Task CreateProcessToast(string id, string text)
{
await JsRuntime.InvokeVoidAsync("createToast", id, text);
}
public async Task UpdateProcessToast(string id, string text)
{
await JsRuntime.InvokeVoidAsync("modifyToast", id, text);
}
public async Task RemoveProcessToast(string id)
{
await JsRuntime.InvokeVoidAsync("removeToast", id);
}
}

View File

@@ -1,4 +1,5 @@
using Moonlight.App.Database.Entities.LogsEntries;
using Moonlight.App.Models.Log;
using Moonlight.App.Models.Misc;
using Moonlight.App.Repositories.LogEntries;
using Moonlight.App.Services.Sessions;
@@ -19,16 +20,18 @@ public class AuditLogService
HttpContextAccessor = httpContextAccessor;
}
public Task Log(AuditLogType type, params object[] data)
public Task Log(AuditLogType type, Action<AuditLogParameters> data)
{
var ip = GetIp();
var al = new AuditLogParameters();
data(al);
var entry = new AuditLogEntry()
{
Ip = ip,
Type = type,
System = false,
JsonData = data.Length == 0 ? "" : JsonConvert.SerializeObject(data)
JsonData = al.Build()
};
Repository.Add(entry);
@@ -36,13 +39,16 @@ public class AuditLogService
return Task.CompletedTask;
}
public Task LogSystem(AuditLogType type, params object[] data)
public Task LogSystem(AuditLogType type, Action<AuditLogParameters> data)
{
var al = new AuditLogParameters();
data(al);
var entry = new AuditLogEntry()
{
Type = type,
System = true,
JsonData = data.Length == 0 ? "" : JsonConvert.SerializeObject(data)
JsonData = al.Build()
};
Repository.Add(entry);
@@ -62,4 +68,23 @@ public class AuditLogService
return HttpContextAccessor.HttpContext.Connection.RemoteIpAddress!.ToString();
}
public class AuditLogParameters
{
private List<LogData> Data = new List<LogData>();
public void Add<T>(object data)
{
Data.Add(new LogData()
{
Type = typeof(T),
Value = data.ToString()
});
}
internal string Build()
{
return JsonConvert.SerializeObject(Data);
}
}
}

View File

@@ -1,6 +1,7 @@
using System.Diagnostics;
using System.Reflection;
using Moonlight.App.Database.Entities.LogsEntries;
using Moonlight.App.Models.Log;
using Moonlight.App.Repositories.LogEntries;
using Moonlight.App.Services.Sessions;
using Newtonsoft.Json;
@@ -18,15 +19,17 @@ public class ErrorLogService
HttpContextAccessor = httpContextAccessor;
}
public Task Log(Exception exception, params object[] objects)
public Task Log(Exception exception, Action<ErrorLogParameters> data)
{
var ip = GetIp();
var al = new ErrorLogParameters();
data(al);
var entry = new ErrorLogEntry()
{
Ip = ip,
System = false,
JsonData = !objects.Any() ? "" : JsonConvert.SerializeObject(objects),
JsonData = al.Build(),
Class = NameOfCallingClass(),
Stacktrace = exception.ToStringDemystified()
};
@@ -36,12 +39,15 @@ public class ErrorLogService
return Task.CompletedTask;
}
public Task LogSystem(Exception exception, params object[] objects)
public Task LogSystem(Exception exception, Action<ErrorLogParameters> data)
{
var al = new ErrorLogParameters();
data(al);
var entry = new ErrorLogEntry()
{
System = true,
JsonData = !objects.Any() ? "" : JsonConvert.SerializeObject(objects),
JsonData = al.Build(),
Class = NameOfCallingClass(),
Stacktrace = exception.ToStringDemystified()
};
@@ -87,4 +93,23 @@ public class ErrorLogService
return HttpContextAccessor.HttpContext.Connection.RemoteIpAddress!.ToString();
}
public class ErrorLogParameters
{
private List<LogData> Data = new List<LogData>();
public void Add<T>(object data)
{
Data.Add(new LogData()
{
Type = typeof(T),
Value = data.ToString()
});
}
internal string Build()
{
return JsonConvert.SerializeObject(Data);
}
}
}

View File

@@ -1,4 +1,5 @@
using Moonlight.App.Database.Entities.LogsEntries;
using Moonlight.App.Models.Log;
using Moonlight.App.Models.Misc;
using Moonlight.App.Repositories.LogEntries;
using Moonlight.App.Services.Sessions;
@@ -17,16 +18,18 @@ public class SecurityLogService
HttpContextAccessor = httpContextAccessor;
}
public Task Log(SecurityLogType type, params object[] data)
public Task Log(SecurityLogType type, Action<SecurityLogParameters> data)
{
var ip = GetIp();
var al = new SecurityLogParameters();
data(al);
var entry = new SecurityLogEntry()
{
Ip = ip,
Type = type,
System = false,
JsonData = data.Length == 0 ? "" : JsonConvert.SerializeObject(data)
JsonData = al.Build()
};
Repository.Add(entry);
@@ -34,13 +37,16 @@ public class SecurityLogService
return Task.CompletedTask;
}
public Task LogSystem(SecurityLogType type, params object[] data)
public Task LogSystem(SecurityLogType type, Action<SecurityLogParameters> data)
{
var al = new SecurityLogParameters();
data(al);
var entry = new SecurityLogEntry()
{
Type = type,
System = true,
JsonData = data.Length == 0 ? "" : JsonConvert.SerializeObject(data)
JsonData = al.Build()
};
Repository.Add(entry);
@@ -60,4 +66,24 @@ public class SecurityLogService
return HttpContextAccessor.HttpContext.Connection.RemoteIpAddress!.ToString();
}
public class SecurityLogParameters
{
private List<LogData> Data = new List<LogData>();
public void Add<T>(object data)
{
Data.Add(new LogData()
{
Type = typeof(T),
Value = data.ToString()
});
}
internal string Build()
{
return JsonConvert.SerializeObject(Data);
}
}
}

View File

@@ -76,7 +76,10 @@ public class OneTimeJwtService
}
catch (SignatureVerificationException)
{
await SecurityLogService.LogSystem(SecurityLogType.ManipulatedJwt, token);
await SecurityLogService.LogSystem(SecurityLogType.ManipulatedJwt, x =>
{
x.Add<string>(token);
});
return null;
}
catch (Exception e)

View File

@@ -75,11 +75,11 @@ public class ServerService
return s;
}
public async Task<ServerDetailsResponse> GetDetails(Server s)
public async Task<ServerDetails> GetDetails(Server s)
{
Server server = EnsureNodeData(s);
return await WingsApiHelper.Get<ServerDetailsResponse>(
return await WingsApiHelper.Get<ServerDetails>(
server.Node,
$"api/servers/{server.Uuid}"
);
@@ -91,12 +91,16 @@ public class ServerService
var rawSignal = signal.ToString().ToLower();
await WingsApiHelper.Post(server.Node, $"api/servers/{server.Uuid}/power", new ServerPowerRequest()
await WingsApiHelper.Post(server.Node, $"api/servers/{server.Uuid}/power", new ServerPower()
{
Action = rawSignal
});
await AuditLogService.Log(AuditLogType.ChangePowerState, new[] { server.Uuid.ToString(), rawSignal });
await AuditLogService.Log(AuditLogType.ChangePowerState, x =>
{
x.Add<Server>(server.Uuid);
x.Add<PowerSignal>(rawSignal);
});
}
public async Task<ServerBackup> CreateBackup(Server server)
@@ -118,7 +122,7 @@ public class ServerService
serverData.Backups.Add(backup);
ServerRepository.Update(serverData);
await WingsApiHelper.Post(serverData.Node, $"api/servers/{serverData.Uuid}/backup", new CreateBackupRequest()
await WingsApiHelper.Post(serverData.Node, $"api/servers/{serverData.Uuid}/backup", new CreateBackup()
{
Adapter = "wings",
Uuid = backup.Uuid,
@@ -126,7 +130,11 @@ public class ServerService
});
await AuditLogService.Log(AuditLogType.CreateBackup,
new[] { serverData.Uuid.ToString(), backup.Uuid.ToString() });
x =>
{
x.Add<Server>(server.Uuid);
x.Add<ServerBackup>(backup.Uuid);
});
return backup;
}
@@ -158,13 +166,17 @@ public class ServerService
Server server = EnsureNodeData(s);
await WingsApiHelper.Post(server.Node, $"api/servers/{server.Uuid}/backup/{serverBackup.Uuid}/restore",
new RestoreBackupRequest()
new RestoreBackup()
{
Adapter = "wings"
});
await AuditLogService.Log(AuditLogType.RestoreBackup,
new[] { s.Uuid.ToString(), serverBackup.Uuid.ToString() });
x =>
{
x.Add<Server>(server.Uuid);
x.Add<ServerBackup>(serverBackup.Uuid);
});
}
public async Task DeleteBackup(Server server, ServerBackup serverBackup)
@@ -186,7 +198,11 @@ public class ServerService
await MessageService.Emit("wings.backups.delete", backup);
await AuditLogService.Log(AuditLogType.DeleteBackup,
new[] { serverBackup.Uuid.ToString(), serverBackup.Uuid.ToString() });
x =>
{
x.Add<Server>(server.Uuid);
x.Add<ServerBackup>(backup.Uuid);
});
}
public async Task<string> DownloadBackup(Server s, ServerBackup serverBackup)
@@ -200,7 +216,11 @@ public class ServerService
});
await AuditLogService.Log(AuditLogType.DownloadBackup,
new[] { serverBackup.Uuid.ToString(), serverBackup.Uuid.ToString() });
x =>
{
x.Add<Server>(server.Uuid);
x.Add<ServerBackup>(serverBackup.Uuid);
});
return $"https://{server.Node.Fqdn}:{server.Node.HttpPort}/download/backup?token={token}";
}
@@ -299,19 +319,26 @@ public class ServerService
try
{
await WingsApiHelper.Post(node, $"api/servers", new CreateServerRequest()
await WingsApiHelper.Post(node, $"api/servers", new CreateServer()
{
Uuid = newServerData.Uuid,
StartOnCompletion = false
});
await AuditLogService.Log(AuditLogType.CreateServer, newServerData.Uuid.ToString());
await AuditLogService.Log(AuditLogType.CreateServer, x =>
{
x.Add<Server>(newServerData.Uuid);
});
return newServerData;
}
catch (Exception e)
{
await ErrorLogService.Log(e, new[] { newServerData.Uuid.ToString(), node.Id.ToString() });
await ErrorLogService.Log(e, x =>
{
x.Add<Server>(newServerData.Uuid);
x.Add<Node>(node.Id);
});
ServerRepository.Delete(newServerData);
@@ -325,7 +352,10 @@ public class ServerService
await WingsApiHelper.Post(server.Node, $"api/servers/{server.Uuid}/reinstall", null);
await AuditLogService.Log(AuditLogType.ReinstallServer, server.Uuid.ToString());
await AuditLogService.Log(AuditLogType.ReinstallServer, x =>
{
x.Add<Server>(server.Uuid);
});
}
public async Task<Server> SftpServerLogin(int serverId, int id, string password)
@@ -334,7 +364,10 @@ public class ServerService
if (server == null)
{
await SecurityLogService.LogSystem(SecurityLogType.SftpBruteForce, serverId);
await SecurityLogService.LogSystem(SecurityLogType.SftpBruteForce, x =>
{
x.Add<int>(id);
});
throw new Exception("Server not found");
}

View File

@@ -89,12 +89,15 @@ public class IdentityService
}
catch (SignatureVerificationException)
{
await SecurityLogService.Log(SecurityLogType.ManipulatedJwt, token);
await SecurityLogService.Log(SecurityLogType.ManipulatedJwt, x =>
{
x.Add<string>(token);
});
return null;
}
catch (Exception e)
{
await ErrorLogService.Log(e);
await ErrorLogService.Log(e, x => {});
return null;
}
@@ -130,7 +133,7 @@ public class IdentityService
}
catch (Exception e)
{
await ErrorLogService.Log(e);
await ErrorLogService.Log(e, x => {});
return null;
}
}

View File

@@ -0,0 +1,45 @@
using Moonlight.App.Database.Entities;
using Moonlight.App.Models.Misc;
using Moonlight.App.Repositories;
using Newtonsoft.Json;
namespace Moonlight.App.Services;
public class SubscriptionAdminService
{
private readonly SubscriptionRepository SubscriptionRepository;
private readonly OneTimeJwtService OneTimeJwtService;
public SubscriptionAdminService(OneTimeJwtService oneTimeJwtService, SubscriptionRepository subscriptionRepository)
{
OneTimeJwtService = oneTimeJwtService;
SubscriptionRepository = subscriptionRepository;
}
public Task<SubscriptionLimit[]> GetLimits(Subscription subscription)
{
return Task.FromResult(
JsonConvert.DeserializeObject<SubscriptionLimit[]>(subscription.LimitsJson)
?? Array.Empty<SubscriptionLimit>()
);
}
public Task SaveLimits(Subscription subscription, SubscriptionLimit[] limits)
{
subscription.LimitsJson = JsonConvert.SerializeObject(limits);
SubscriptionRepository.Update(subscription);
return Task.CompletedTask;
}
public Task<string> GenerateCode(Subscription subscription, int duration)
{
return Task.FromResult(
OneTimeJwtService.Generate(data =>
{
data.Add("subscription", subscription.Id.ToString());
data.Add("duration", duration.ToString());
}, TimeSpan.FromDays(10324))
);
}
}

View File

@@ -3,126 +3,138 @@ using Moonlight.App.Database.Entities;
using Moonlight.App.Exceptions;
using Moonlight.App.Models.Misc;
using Moonlight.App.Repositories;
using Moonlight.App.Repositories.Subscriptions;
using Moonlight.App.Services.LogServices;
using Moonlight.App.Services.Sessions;
using Newtonsoft.Json;
namespace Moonlight.App.Services;
public class SubscriptionService
{
private readonly SubscriptionRepository SubscriptionRepository;
private readonly UserRepository UserRepository;
private readonly IdentityService IdentityService;
private readonly ConfigService ConfigService;
private readonly OneTimeJwtService OneTimeJwtService;
private readonly AuditLogService AuditLogService;
private readonly IdentityService IdentityService;
private readonly UserRepository UserRepository;
private readonly ConfigService ConfigService;
public SubscriptionService(SubscriptionRepository subscriptionRepository,
UserRepository userRepository,
IdentityService identityService,
ConfigService configService,
public SubscriptionService(
SubscriptionRepository subscriptionRepository,
OneTimeJwtService oneTimeJwtService,
AuditLogService auditLogService)
IdentityService identityService,
UserRepository userRepository,
ConfigService configService)
{
SubscriptionRepository = subscriptionRepository;
UserRepository = userRepository;
IdentityService = identityService;
ConfigService = configService;
OneTimeJwtService = oneTimeJwtService;
AuditLogService = auditLogService;
IdentityService = identityService;
UserRepository = userRepository;
ConfigService = configService;
}
public async Task<Subscription?> Get()
public async Task<Subscription?> GetCurrent()
{
var user = await IdentityService.Get();
var advancedUser = UserRepository
.Get()
.Include(x => x.Subscription)
.First(x => x.Id == user!.Id);
var user = await GetCurrentUser();
if (advancedUser.Subscription == null)
if (user == null || user.CurrentSubscription == null)
return null;
return SubscriptionRepository
.Get()
.Include(x => x.Limits)
.Include("Limits.Image")
.First(x => x.Id == advancedUser.Subscription.Id);
}
public async Task Cancel()
{
var user = await IdentityService.Get();
user!.Subscription = null;
UserRepository.Update(user!);
var subscriptionEnd = user.SubscriptionSince.ToUniversalTime().AddDays(user.SubscriptionDuration);
await AuditLogService.Log(AuditLogType.CancelSubscription, new[] { user.Email });
}
public Task<Subscription[]> GetAvailable()
{
return Task.FromResult(
SubscriptionRepository
.Get()
.Include(x => x.Limits)
.ToArray()
);
}
public Task<string> GenerateBuyUrl(Subscription subscription)
{
var url = ConfigService
.GetSection("Moonlight")
.GetSection("Payments")
.GetValue<string>("BaseUrl");
if (subscriptionEnd > DateTime.UtcNow)
{
return user.CurrentSubscription;
}
return Task.FromResult<string>($"{url}/products/{subscription.SellPassId}");
}
public Task<string> ProcessGenerate(int subscriptionId)
{
var subscription = SubscriptionRepository
.Get()
.FirstOrDefault(x => x.Id == subscriptionId);
if (subscription == null)
throw new DisplayException("Unknown subscription id");
var token = OneTimeJwtService.Generate(
options =>
{
options.Add("id", subscription.Id.ToString());
}
);
return Task.FromResult(token);
return null;
}
public async Task ApplyCode(string code)
{
var user = (await IdentityService.Get())!;
var values = await OneTimeJwtService.Validate(code);
var data = await OneTimeJwtService.Validate(code);
if (values == null)
throw new DisplayException("Invalid subscription code");
if (data == null)
throw new DisplayException("Invalid or expired subscription code");
if (!values.ContainsKey("id"))
throw new DisplayException("Subscription code is missing the id");
var id = int.Parse(values["id"]);
var id = int.Parse(data["subscription"]);
var duration = int.Parse(data["duration"]);
var subscription = SubscriptionRepository
.Get()
.FirstOrDefault(x => x.Id == id);
if (subscription == null)
throw new DisplayException("The subscription the code is referring does not exist");
throw new DisplayException("The subscription the code is associated with does not exist");
user.Subscription = subscription;
user.SubscriptionDuration = subscription.Duration;
user.SubscriptionSince = DateTime.Now;
var user = await GetCurrentUser();
if (user == null)
throw new DisplayException("Unable to determine current user");
user.CurrentSubscription = subscription;
user.SubscriptionDuration = duration;
user.SubscriptionSince = DateTime.UtcNow;
UserRepository.Update(user);
await OneTimeJwtService.Revoke(code);
await AuditLogService.Log(AuditLogType.ApplySubscriptionCode, new[] { user.Email, subscription.Id.ToString() });
await OneTimeJwtService.Revoke(code);
}
public async Task<SubscriptionLimit> GetLimit(string identifier)
{
var configSection = ConfigService.GetSection("Moonlight").GetSection("Subscriptions");
var defaultLimits = configSection.GetValue<SubscriptionLimit[]>("defaultLimits");
var subscription = await GetCurrent();
if (subscription == null)
{
var foundDefault = defaultLimits.FirstOrDefault(x => x.Identifier == identifier);
if (foundDefault != null)
return foundDefault;
return new()
{
Identifier = identifier,
Amount = 0
};
}
else
{
var subscriptionLimits =
JsonConvert.DeserializeObject<SubscriptionLimit[]>(subscription.LimitsJson)
?? Array.Empty<SubscriptionLimit>();
var foundLimit = subscriptionLimits.FirstOrDefault(x => x.Identifier == identifier);
if (foundLimit != null)
return foundLimit;
var foundDefault = defaultLimits.FirstOrDefault(x => x.Identifier == identifier);
if (foundDefault != null)
return foundDefault;
return new()
{
Identifier = identifier,
Amount = 0
};
}
}
private async Task<User?> GetCurrentUser()
{
var user = await IdentityService.Get();
if (user == null)
return null;
var userWithData = UserRepository
.Get()
.Include(x => x.CurrentSubscription)
.First(x => x.Id == user.Id);
return userWithData;
}
}

View File

@@ -1,4 +1,5 @@
using Moonlight.App.Models.Misc;
using Moonlight.App.Database.Entities;
using Moonlight.App.Models.Misc;
using Moonlight.App.Repositories;
using Moonlight.App.Services.LogServices;
using Moonlight.App.Services.Sessions;
@@ -46,13 +47,23 @@ public class TotpService
public async Task Enable()
{
var user = (await IdentityService.Get())!;
user.TotpEnabled = true;
user.TotpSecret = GenerateSecret();
UserRepository.Update(user);
await AuditLogService.Log(AuditLogType.EnableTotp, user.Email);
await AuditLogService.Log(AuditLogType.EnableTotp, x =>
{
x.Add<User>(user.Email);
});
}
public async Task EnforceTotpLogin()
{
var user = (await IdentityService.Get())!;
user.TotpEnabled = true;
UserRepository.Update(user);
}
public async Task Disable()
@@ -63,7 +74,10 @@ public class TotpService
UserRepository.Update(user);
await AuditLogService.Log(AuditLogType.DisableTotp, user.Email);
await AuditLogService.Log(AuditLogType.DisableTotp,x =>
{
x.Add<User>(user.Email);
});
}
private string GenerateSecret()

View File

@@ -77,7 +77,10 @@ public class UserService
});
await MailService.SendMail(user!, "register", values => {});
await AuditLogService.Log(AuditLogType.Register, user.Email);
await AuditLogService.Log(AuditLogType.Register, x =>
{
x.Add<User>(user.Email);
});
return await GenerateToken(user);
}
@@ -91,7 +94,11 @@ public class UserService
if (user == null)
{
await SecurityLogService.Log(SecurityLogType.LoginFail, new[] { email, password });
await SecurityLogService.Log(SecurityLogType.LoginFail, x =>
{
x.Add<User>(email);
x.Add<string>(password);
});
throw new DisplayException("Email and password combination not found");
}
@@ -100,7 +107,11 @@ public class UserService
return user.TotpEnabled;
}
await SecurityLogService.Log(SecurityLogType.LoginFail, new[] { email, password });
await SecurityLogService.Log(SecurityLogType.LoginFail, x =>
{
x.Add<User>(email);
x.Add<string>(password);
});
throw new DisplayException("Email and password combination not found");;
}
@@ -125,18 +136,28 @@ public class UserService
if (totpCodeValid)
{
await AuditLogService.Log(AuditLogType.Login, email);
await AuditLogService.Log(AuditLogType.Login, x =>
{
x.Add<User>(email);
});
return await GenerateToken(user, true);
}
else
{
await SecurityLogService.Log(SecurityLogType.LoginFail, new[] { email, password });
await SecurityLogService.Log(SecurityLogType.LoginFail, x =>
{
x.Add<User>(email);
x.Add<string>(password);
});
throw new DisplayException("2FA code invalid");
}
}
else
{
await AuditLogService.Log(AuditLogType.Login, email);
await AuditLogService.Log(AuditLogType.Login, x =>
{
x.Add<User>(email);
});
return await GenerateToken(user!, true);
}
}
@@ -149,7 +170,10 @@ public class UserService
if (isSystemAction)
{
await AuditLogService.LogSystem(AuditLogType.ChangePassword, user.Email);
await AuditLogService.LogSystem(AuditLogType.ChangePassword, x=>
{
x.Add<User>(user.Email);
});
}
else
{
@@ -160,7 +184,10 @@ public class UserService
values.Add("Location", "In your walls");
});
await AuditLogService.Log(AuditLogType.ChangePassword, user.Email);
await AuditLogService.Log(AuditLogType.ChangePassword, x =>
{
x.Add<User>(user.Email);
});
}
}
@@ -170,17 +197,27 @@ public class UserService
if (user == null)
{
await SecurityLogService.LogSystem(SecurityLogType.SftpBruteForce, id);
await SecurityLogService.LogSystem(SecurityLogType.SftpBruteForce, x =>
{
x.Add<int>(id);
});
throw new Exception("Invalid username");
}
if (BCrypt.Net.BCrypt.Verify(password, user.Password))
{
await AuditLogService.LogSystem(AuditLogType.Login, user.Email);
await AuditLogService.LogSystem(AuditLogType.Login, x =>
{
x.Add<User>(user.Email);
});
return user;
}
await SecurityLogService.LogSystem(SecurityLogType.SftpBruteForce, new[] { id.ToString(), password });
await SecurityLogService.LogSystem(SecurityLogType.SftpBruteForce, x =>
{
x.Add<int>(id);
x.Add<string>(password);
});
throw new Exception("Invalid userid or password");
}
@@ -218,7 +255,7 @@ public class UserService
var newPassword = StringHelper.GenerateString(16);
await ChangePassword(user, newPassword, true);
await AuditLogService.Log(AuditLogType.PasswordReset);
await AuditLogService.Log(AuditLogType.PasswordReset, x => {});
await MailService.SendMail(user, "passwordReset", values =>
{