Added support chat. Added resource service. Added server backups
This commit is contained in:
parent
c3eadf9133
commit
0b6882d3f8
26 changed files with 2408 additions and 9 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -442,3 +442,4 @@ Moonlight/obj/Moonlight.csproj.nuget.dgspec.json
|
|||
Moonlight/obj/project.assets.json
|
||||
Moonlight/obj/project.nuget.cache
|
||||
Moonlight/obj/project.packagespec.json
|
||||
Moonlight/obj/Debug/net6.0/Moonlight.GeneratedMSBuildEditorConfig.editorconfig
|
||||
|
|
|
@ -26,6 +26,7 @@ public class DataContext : DbContext
|
|||
public DbSet<LoadingMessage> LoadingMessages { get; set; }
|
||||
public DbSet<AuditLogEntry> AuditLog { get; set; }
|
||||
public DbSet<Entities.Database> Databases { get; set; }
|
||||
public DbSet<SupportMessage> SupportMessages { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
|
|
17
Moonlight/App/Database/Entities/SupportMessage.cs
Normal file
17
Moonlight/App/Database/Entities/SupportMessage.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
using Moonlight.App.Models.Misc;
|
||||
|
||||
namespace Moonlight.App.Database.Entities;
|
||||
|
||||
public class SupportMessage
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Message { get; set; } = "";
|
||||
public User? Sender { get; set; } = null;
|
||||
public User? Recipient { get; set; } = null;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public bool IsQuestion { get; set; } = false;
|
||||
public QuestionType Type { get; set; }
|
||||
public string Answer { get; set; } = "";
|
||||
public bool IsSystem { get; set; } = false;
|
||||
public bool IsSupport { get; set; } = false;
|
||||
}
|
|
@ -14,13 +14,14 @@ public class User
|
|||
public string State { get; set; } = "";
|
||||
public string Country { get; set; } = "";
|
||||
public UserStatus Status { get; set; } = UserStatus.Unverified;
|
||||
public bool TotpEnabled { get; set; }
|
||||
public bool TotpEnabled { get; set; } = false;
|
||||
public string TotpSecret { get; set; } = "";
|
||||
public DateTime TokenValidTime { get; set; } = DateTime.Now;
|
||||
public long DiscordId { get; set; }
|
||||
public long DiscordId { get; set; } = -1;
|
||||
public string DiscordUsername { get; set; } = "";
|
||||
public string DiscordDiscriminator { get; set; } = "";
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
public bool Admin { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
public bool Admin { get; set; } = false;
|
||||
public bool SupportPending { get; set; } = false;
|
||||
}
|
617
Moonlight/App/Database/Migrations/20230221173242_AddSupportMessage.Designer.cs
generated
Normal file
617
Moonlight/App/Database/Migrations/20230221173242_AddSupportMessage.Designer.cs
generated
Normal file
|
@ -0,0 +1,617 @@
|
|||
// <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("20230221173242_AddSupportMessage")]
|
||||
partial class AddSupportMessage
|
||||
{
|
||||
/// <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.AuditLogEntry", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
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.Database", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("AaPanelId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("OwnerId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("OwnerId");
|
||||
|
||||
b.ToTable("Databases");
|
||||
});
|
||||
|
||||
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.Image", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.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<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<int?>("ImageId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ImageId");
|
||||
|
||||
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.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.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<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.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<string>("Message")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("SenderId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
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<string>("DiscordDiscriminator")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<long>("DiscordId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("DiscordUsername")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
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<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.Database", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.App.Database.Entities.User", "Owner")
|
||||
.WithMany()
|
||||
.HasForeignKey("OwnerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Owner");
|
||||
});
|
||||
|
||||
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.ImageTag", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.App.Database.Entities.Image", null)
|
||||
.WithMany("Tags")
|
||||
.HasForeignKey("ImageId");
|
||||
});
|
||||
|
||||
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.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", "Sender")
|
||||
.WithMany()
|
||||
.HasForeignKey("SenderId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Sender");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b =>
|
||||
{
|
||||
b.Navigation("DockerImages");
|
||||
|
||||
b.Navigation("Tags");
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Moonlight.App.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddSupportMessage : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "SupportMessages",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
Message = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
SenderId = table.Column<int>(type: "int", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
IsQuestion = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
Type = table.Column<int>(type: "int", nullable: false),
|
||||
Answer = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_SupportMessages", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_SupportMessages_Users_SenderId",
|
||||
column: x => x.SenderId,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_SupportMessages_SenderId",
|
||||
table: "SupportMessages",
|
||||
column: "SenderId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "SupportMessages");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -374,6 +374,50 @@ namespace Moonlight.App.Database.Migrations
|
|||
b.ToTable("ServerVariables");
|
||||
});
|
||||
|
||||
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")
|
||||
|
@ -432,6 +476,9 @@ namespace Moonlight.App.Database.Migrations
|
|||
b.Property<int>("Status")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<bool>("SupportPending")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<DateTime>("TokenValidTime")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
|
@ -542,6 +589,21 @@ namespace Moonlight.App.Database.Migrations
|
|||
.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.Image", b =>
|
||||
{
|
||||
b.Navigation("DockerImages");
|
||||
|
|
635
Moonlight/App/Databse/Migrations/20230221183730_UpdatedSupportAndUserModel.Designer.cs
generated
Normal file
635
Moonlight/App/Databse/Migrations/20230221183730_UpdatedSupportAndUserModel.Designer.cs
generated
Normal file
|
@ -0,0 +1,635 @@
|
|||
// <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.Databse.Migrations
|
||||
{
|
||||
[DbContext(typeof(DataContext))]
|
||||
[Migration("20230221183730_UpdatedSupportAndUserModel")]
|
||||
partial class UpdatedSupportAndUserModel
|
||||
{
|
||||
/// <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.AuditLogEntry", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
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.Database", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("AaPanelId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("OwnerId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("OwnerId");
|
||||
|
||||
b.ToTable("Databases");
|
||||
});
|
||||
|
||||
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.Image", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.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<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<int?>("ImageId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ImageId");
|
||||
|
||||
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.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.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<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.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<string>("DiscordDiscriminator")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<long>("DiscordId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("DiscordUsername")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
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.Database", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.App.Database.Entities.User", "Owner")
|
||||
.WithMany()
|
||||
.HasForeignKey("OwnerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Owner");
|
||||
});
|
||||
|
||||
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.ImageTag", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.App.Database.Entities.Image", null)
|
||||
.WithMany("Tags")
|
||||
.HasForeignKey("ImageId");
|
||||
});
|
||||
|
||||
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.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.Image", b =>
|
||||
{
|
||||
b.Navigation("DockerImages");
|
||||
|
||||
b.Navigation("Tags");
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Moonlight.App.Databse.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class UpdatedSupportAndUserModel : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_SupportMessages_Users_SenderId",
|
||||
table: "SupportMessages");
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "SupportPending",
|
||||
table: "Users",
|
||||
type: "tinyint(1)",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "SenderId",
|
||||
table: "SupportMessages",
|
||||
type: "int",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int");
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "IsSupport",
|
||||
table: "SupportMessages",
|
||||
type: "tinyint(1)",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "IsSystem",
|
||||
table: "SupportMessages",
|
||||
type: "tinyint(1)",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "RecipientId",
|
||||
table: "SupportMessages",
|
||||
type: "int",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_SupportMessages_RecipientId",
|
||||
table: "SupportMessages",
|
||||
column: "RecipientId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_SupportMessages_Users_RecipientId",
|
||||
table: "SupportMessages",
|
||||
column: "RecipientId",
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_SupportMessages_Users_SenderId",
|
||||
table: "SupportMessages",
|
||||
column: "SenderId",
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_SupportMessages_Users_RecipientId",
|
||||
table: "SupportMessages");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_SupportMessages_Users_SenderId",
|
||||
table: "SupportMessages");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_SupportMessages_RecipientId",
|
||||
table: "SupportMessages");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SupportPending",
|
||||
table: "Users");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "IsSupport",
|
||||
table: "SupportMessages");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "IsSystem",
|
||||
table: "SupportMessages");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "RecipientId",
|
||||
table: "SupportMessages");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "SenderId",
|
||||
table: "SupportMessages",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 0,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_SupportMessages_Users_SenderId",
|
||||
table: "SupportMessages",
|
||||
column: "SenderId",
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
namespace Moonlight.App.Helpers;
|
||||
using Moonlight.App.Services;
|
||||
|
||||
namespace Moonlight.App.Helpers;
|
||||
|
||||
public static class Formatter
|
||||
{
|
||||
|
@ -34,4 +36,32 @@ public static class Formatter
|
|||
return (i / (1024D * 1024D)).Round(2) + " GB";
|
||||
}
|
||||
}
|
||||
|
||||
public static string FormatAgoFromDateTime(DateTime dt, SmartTranslateService translateService = null)
|
||||
{
|
||||
TimeSpan timeSince = DateTime.UtcNow.Subtract(dt);
|
||||
|
||||
if (timeSince.TotalMilliseconds < 1)
|
||||
return translateService == null ? "just now" : translateService.Translate("just now");
|
||||
|
||||
if (timeSince.TotalMinutes < 1)
|
||||
return translateService == null ? "less than a minute ago" : translateService.Translate("less than a minute ago");
|
||||
|
||||
if (timeSince.TotalMinutes < 2)
|
||||
return translateService == null ? "1 minute ago" : translateService.Translate("1 minute ago");
|
||||
|
||||
if (timeSince.TotalMinutes < 60)
|
||||
return Math.Round(timeSince.TotalMinutes) + (translateService == null ? " minutes ago" : translateService.Translate(" minutes ago"));
|
||||
|
||||
if (timeSince.TotalHours < 2)
|
||||
return translateService == null ? "1 hour ago" : translateService.Translate("1 hour ago");
|
||||
|
||||
if (timeSince.TotalHours < 24)
|
||||
return Math.Round(timeSince.TotalHours) + (translateService == null ? " hours ago" : translateService.Translate(" hours ago"));
|
||||
|
||||
if (timeSince.TotalDays < 2)
|
||||
return translateService == null ? "1 day ago" : translateService.Translate("1 day ago");
|
||||
|
||||
return Math.Round(timeSince.TotalDays) + (translateService == null ? " days ago" : translateService.Translate(" days ago"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moonlight.App.Http.Requests.Wings;
|
||||
using Moonlight.App.Repositories;
|
||||
using Moonlight.App.Repositories.Servers;
|
||||
using Moonlight.App.Services;
|
||||
|
||||
namespace Moonlight.App.Http.Controllers.Api.Remote;
|
||||
|
||||
[Route("api/remote/backups")]
|
||||
[ApiController]
|
||||
public class BackupController : Controller
|
||||
{
|
||||
private readonly ServerBackupRepository ServerBackupRepository;
|
||||
private readonly MessageService MessageService;
|
||||
private readonly NodeRepository NodeRepository;
|
||||
|
||||
public BackupController(
|
||||
ServerBackupRepository serverBackupRepository,
|
||||
NodeRepository nodeRepository,
|
||||
MessageService messageService)
|
||||
{
|
||||
ServerBackupRepository = serverBackupRepository;
|
||||
NodeRepository = nodeRepository;
|
||||
MessageService = messageService;
|
||||
}
|
||||
|
||||
[HttpGet("{uuid}")]
|
||||
public ActionResult<string> Download(Guid uuid)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
[HttpPost("{uuid}")]
|
||||
public async Task<ActionResult> SetStatus([FromRoute] Guid uuid, [FromBody] ReportBackupCompleteRequest request)
|
||||
{
|
||||
var tokenData = Request.Headers.Authorization.ToString().Replace("Bearer ", "");
|
||||
var id = tokenData.Split(".")[0];
|
||||
var token = tokenData.Split(".")[1];
|
||||
|
||||
var node = NodeRepository.Get().FirstOrDefault(x => x.TokenId == id);
|
||||
|
||||
if (node == null)
|
||||
return NotFound();
|
||||
|
||||
if (token != node.Token)
|
||||
return Unauthorized();
|
||||
|
||||
var backup = ServerBackupRepository.Get().FirstOrDefault(x => x.Uuid == uuid);
|
||||
|
||||
if (backup == null)
|
||||
return NotFound();
|
||||
|
||||
if (request.Successful)
|
||||
{
|
||||
backup.Created = true;
|
||||
backup.Bytes = request.Size;
|
||||
|
||||
ServerBackupRepository.Update(backup);
|
||||
|
||||
await MessageService.Emit($"wings.backups.create", backup);
|
||||
}
|
||||
else
|
||||
{
|
||||
await MessageService.Emit($"wings.backups.createfailed", backup);
|
||||
ServerBackupRepository.Delete(backup);
|
||||
}
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpPost("{uuid}/restore")]
|
||||
public async Task<ActionResult> SetRestoreStatus([FromRoute] Guid uuid)
|
||||
{
|
||||
var tokenData = Request.Headers.Authorization.ToString().Replace("Bearer ", "");
|
||||
var id = tokenData.Split(".")[0];
|
||||
var token = tokenData.Split(".")[1];
|
||||
|
||||
var node = NodeRepository.Get().FirstOrDefault(x => x.TokenId == id);
|
||||
|
||||
if (node == null)
|
||||
return NotFound();
|
||||
|
||||
if (token != node.Token)
|
||||
return Unauthorized();
|
||||
|
||||
var backup = ServerBackupRepository.Get().FirstOrDefault(x => x.Uuid == uuid);
|
||||
|
||||
if (backup == null)
|
||||
return NotFound();
|
||||
|
||||
await MessageService.Emit($"wings.backups.restore", backup);
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
}
|
115
Moonlight/App/Http/Controllers/Api/Remote/SftpAuthController.cs
Normal file
115
Moonlight/App/Http/Controllers/Api/Remote/SftpAuthController.cs
Normal file
|
@ -0,0 +1,115 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moonlight.App.Http.Requests.Wings;
|
||||
using Moonlight.App.Http.Resources.Wings;
|
||||
using Moonlight.App.Repositories;
|
||||
using Moonlight.App.Services;
|
||||
|
||||
namespace Moonlight.App.Http.Controllers.Api.Remote;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/remote/sftp/auth")]
|
||||
public class SftpAuthController : Controller
|
||||
{
|
||||
private readonly ServerService ServerService;
|
||||
private readonly NodeRepository NodeRepository;
|
||||
|
||||
public SftpAuthController(
|
||||
ServerService serverService,
|
||||
NodeRepository nodeRepository)
|
||||
{
|
||||
ServerService = serverService;
|
||||
NodeRepository = nodeRepository;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<SftpLoginResult>> Login(SftpLoginRequest request)
|
||||
{
|
||||
var tokenData = Request.Headers.Authorization.ToString().Replace("Bearer ", "");
|
||||
var tokenId = tokenData.Split(".")[0];
|
||||
var token = tokenData.Split(".")[1];
|
||||
|
||||
var node = NodeRepository.Get().FirstOrDefault(x => x.TokenId == tokenId);
|
||||
|
||||
if (node == null)
|
||||
return NotFound();
|
||||
|
||||
if (token != node.Token)
|
||||
return Unauthorized();
|
||||
|
||||
if (request.Type == "public_key") // Deny public key authentication, because moonlight does not implement that
|
||||
{
|
||||
return StatusCode(403);
|
||||
}
|
||||
|
||||
// Parse the username
|
||||
var parts = request.Username.Split(".");
|
||||
|
||||
if (parts.Length < 2)
|
||||
return BadRequest();
|
||||
|
||||
if (!int.TryParse(parts[0], out int id))
|
||||
return BadRequest();
|
||||
|
||||
if (!int.TryParse(parts[1], out int serverId))
|
||||
return BadRequest();
|
||||
|
||||
try
|
||||
{
|
||||
var server = await ServerService.SftpServerLogin(serverId, id, request.Password);
|
||||
|
||||
return Ok(new SftpLoginResult()
|
||||
{
|
||||
Server = server.Uuid.ToString(),
|
||||
User = "",
|
||||
Permissions = new()
|
||||
{
|
||||
"control.console",
|
||||
"control.start",
|
||||
"control.stop",
|
||||
"control.restart",
|
||||
"websocket.connect",
|
||||
"file.create",
|
||||
"file.read",
|
||||
"file.read-content",
|
||||
"file.update",
|
||||
"file.delete",
|
||||
"file.archive",
|
||||
"file.sftp",
|
||||
"user.create",
|
||||
"user.read",
|
||||
"user.update",
|
||||
"user.delete",
|
||||
"backup.create",
|
||||
"backup.read",
|
||||
"backup.delete",
|
||||
"backup.download",
|
||||
"backup.restore",
|
||||
"allocation.read",
|
||||
"allocation.create",
|
||||
"allocation.update",
|
||||
"allocation.delete",
|
||||
"startup.read",
|
||||
"startup.update",
|
||||
"startup.docker-image",
|
||||
"database.create",
|
||||
"database.read",
|
||||
"database.update",
|
||||
"database.delete",
|
||||
"database.view_password",
|
||||
"schedule.create",
|
||||
"schedule.read",
|
||||
"schedule.update",
|
||||
"schedule.delete",
|
||||
"settings.rename",
|
||||
"settings.reinstall"
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Most of the exception here will be because of stuff like a invalid server id and simular things
|
||||
// so we ignore them and return 403
|
||||
return StatusCode(403);
|
||||
}
|
||||
}
|
||||
}
|
6
Moonlight/App/Models/Misc/QuestionType.cs
Normal file
6
Moonlight/App/Models/Misc/QuestionType.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace Moonlight.App.Models.Misc;
|
||||
|
||||
public enum QuestionType
|
||||
{
|
||||
ServerUrl
|
||||
}
|
44
Moonlight/App/Repositories/SupportMessageRepository.cs
Normal file
44
Moonlight/App/Repositories/SupportMessageRepository.cs
Normal file
|
@ -0,0 +1,44 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Moonlight.App.Database;
|
||||
using Moonlight.App.Database.Entities;
|
||||
|
||||
namespace Moonlight.App.Repositories;
|
||||
|
||||
public class SupportMessageRepository : IDisposable
|
||||
{
|
||||
private readonly DataContext DataContext;
|
||||
|
||||
public SupportMessageRepository(DataContext dataContext)
|
||||
{
|
||||
DataContext = dataContext;
|
||||
}
|
||||
|
||||
public DbSet<SupportMessage> Get()
|
||||
{
|
||||
return DataContext.SupportMessages;
|
||||
}
|
||||
|
||||
public SupportMessage Add(SupportMessage message)
|
||||
{
|
||||
var x = DataContext.SupportMessages.Add(message);
|
||||
DataContext.SaveChanges();
|
||||
return x.Entity;
|
||||
}
|
||||
|
||||
public void Update(SupportMessage message)
|
||||
{
|
||||
DataContext.SupportMessages.Update(message);
|
||||
DataContext.SaveChanges();
|
||||
}
|
||||
|
||||
public void Delete(SupportMessage message)
|
||||
{
|
||||
DataContext.SupportMessages.Remove(message);
|
||||
DataContext.SaveChanges();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DataContext.Dispose();
|
||||
}
|
||||
}
|
23
Moonlight/App/Services/ResourceService.cs
Normal file
23
Moonlight/App/Services/ResourceService.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using Moonlight.App.Database.Entities;
|
||||
|
||||
namespace Moonlight.App.Services;
|
||||
|
||||
public class ResourceService
|
||||
{
|
||||
private readonly string AppUrl;
|
||||
|
||||
public ResourceService(ConfigService configService)
|
||||
{
|
||||
AppUrl = configService.GetSection("Moonlight").GetValue<string>("AppUrl");
|
||||
}
|
||||
|
||||
public string Image(string name)
|
||||
{
|
||||
return $"{AppUrl}/api/moonlight/resources/images/{name}";
|
||||
}
|
||||
|
||||
public string Avatar(User user)
|
||||
{
|
||||
return $"{AppUrl}/api/moonlight/avatar/{user.Id}";
|
||||
}
|
||||
}
|
67
Moonlight/App/Services/Support/SupportAdminServer.cs
Normal file
67
Moonlight/App/Services/Support/SupportAdminServer.cs
Normal file
|
@ -0,0 +1,67 @@
|
|||
using Moonlight.App.Database.Entities;
|
||||
using Moonlight.App.Services.Sessions;
|
||||
|
||||
namespace Moonlight.App.Services.Support;
|
||||
|
||||
public class SupportAdminServer
|
||||
{
|
||||
private readonly SupportServerService SupportServerService;
|
||||
private readonly IdentityService IdentityService;
|
||||
private readonly MessageService MessageService;
|
||||
|
||||
public EventHandler<SupportMessage> OnNewMessage;
|
||||
|
||||
private User Self;
|
||||
private User Recipient;
|
||||
|
||||
public SupportAdminServer(
|
||||
SupportServerService supportServerService,
|
||||
IdentityService identityService,
|
||||
MessageService messageService)
|
||||
{
|
||||
SupportServerService = supportServerService;
|
||||
IdentityService = identityService;
|
||||
MessageService = messageService;
|
||||
}
|
||||
|
||||
public async Task Start(User user)
|
||||
{
|
||||
Self = (await IdentityService.Get())!;
|
||||
Recipient = user;
|
||||
|
||||
MessageService.Subscribe<SupportClientService, SupportMessage>(
|
||||
$"support.{Recipient.Id}.message",
|
||||
this,
|
||||
message =>
|
||||
{
|
||||
OnNewMessage?.Invoke(this, message);
|
||||
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<SupportMessage[]> GetMessages()
|
||||
{
|
||||
return await SupportServerService.GetMessages(Recipient);
|
||||
}
|
||||
|
||||
public async Task SendMessage(string content)
|
||||
{
|
||||
var message = new SupportMessage()
|
||||
{
|
||||
Message = content
|
||||
};
|
||||
|
||||
await SupportServerService.SendMessage(
|
||||
Recipient,
|
||||
message,
|
||||
Self,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
MessageService.Unsubscribe($"support.{Recipient.Id}.message", this);
|
||||
}
|
||||
}
|
64
Moonlight/App/Services/Support/SupportClientService.cs
Normal file
64
Moonlight/App/Services/Support/SupportClientService.cs
Normal file
|
@ -0,0 +1,64 @@
|
|||
using Moonlight.App.Database.Entities;
|
||||
using Moonlight.App.Services.Sessions;
|
||||
|
||||
namespace Moonlight.App.Services.Support;
|
||||
|
||||
public class SupportClientService : IDisposable
|
||||
{
|
||||
private readonly SupportServerService SupportServerService;
|
||||
private readonly IdentityService IdentityService;
|
||||
private readonly MessageService MessageService;
|
||||
|
||||
public EventHandler<SupportMessage> OnNewMessage;
|
||||
|
||||
private User Self;
|
||||
|
||||
public SupportClientService(
|
||||
SupportServerService supportServerService,
|
||||
IdentityService identityService,
|
||||
MessageService messageService)
|
||||
{
|
||||
SupportServerService = supportServerService;
|
||||
IdentityService = identityService;
|
||||
MessageService = messageService;
|
||||
}
|
||||
|
||||
public async Task Start()
|
||||
{
|
||||
Self = (await IdentityService.Get())!;
|
||||
|
||||
MessageService.Subscribe<SupportClientService, SupportMessage>(
|
||||
$"support.{Self.Id}.message",
|
||||
this,
|
||||
message =>
|
||||
{
|
||||
OnNewMessage?.Invoke(this, message);
|
||||
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<SupportMessage[]> GetMessages()
|
||||
{
|
||||
return await SupportServerService.GetMessages(Self);
|
||||
}
|
||||
|
||||
public async Task SendMessage(string content)
|
||||
{
|
||||
var message = new SupportMessage()
|
||||
{
|
||||
Message = content
|
||||
};
|
||||
|
||||
await SupportServerService.SendMessage(
|
||||
Self,
|
||||
message,
|
||||
Self
|
||||
);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
MessageService.Unsubscribe($"support.{Self.Id}.message", this);
|
||||
}
|
||||
}
|
104
Moonlight/App/Services/Support/SupportServerService.cs
Normal file
104
Moonlight/App/Services/Support/SupportServerService.cs
Normal file
|
@ -0,0 +1,104 @@
|
|||
using Logging.Net;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Moonlight.App.Database.Entities;
|
||||
using Moonlight.App.Repositories;
|
||||
|
||||
namespace Moonlight.App.Services.Support;
|
||||
|
||||
public class SupportServerService : IDisposable
|
||||
{
|
||||
private SupportMessageRepository SupportMessageRepository;
|
||||
private MessageService MessageService;
|
||||
private UserRepository UserRepository;
|
||||
private readonly IServiceScopeFactory ServiceScopeFactory;
|
||||
private IServiceScope ServiceScope;
|
||||
|
||||
public SupportServerService(IServiceScopeFactory serviceScopeFactory)
|
||||
{
|
||||
ServiceScopeFactory = serviceScopeFactory;
|
||||
|
||||
Task.Run(Run);
|
||||
}
|
||||
|
||||
public async Task SendMessage(User r, SupportMessage message, User s, bool isSupport = false)
|
||||
{
|
||||
var recipient = UserRepository.Get().First(x => x.Id == r.Id);
|
||||
var sender = UserRepository.Get().First(x => x.Id == s.Id);
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
message.CreatedAt = DateTime.UtcNow;
|
||||
message.Sender = sender;
|
||||
message.Recipient = recipient;
|
||||
message.IsSupport = isSupport;
|
||||
|
||||
SupportMessageRepository.Add(message);
|
||||
|
||||
await MessageService.Emit($"support.{recipient.Id}.message", message);
|
||||
|
||||
if (!recipient.SupportPending)
|
||||
{
|
||||
recipient.SupportPending = true;
|
||||
UserRepository.Update(recipient);
|
||||
|
||||
var systemMessage = new SupportMessage()
|
||||
{
|
||||
Recipient = recipient,
|
||||
Sender = null,
|
||||
IsSystem = true,
|
||||
Message = "The support team has been notified. Please be patient"
|
||||
};
|
||||
|
||||
SupportMessageRepository.Add(systemMessage);
|
||||
|
||||
await MessageService.Emit($"support.{recipient.Id}.message", systemMessage);
|
||||
|
||||
Logger.Info("Support ticket created: " + recipient.Id);
|
||||
//TODO: Ping or so
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Task<SupportMessage[]> GetMessages(User r)
|
||||
{
|
||||
var recipient = UserRepository.Get().First(x => x.Id == r.Id);
|
||||
|
||||
var messages = SupportMessageRepository
|
||||
.Get()
|
||||
.Include(x => x.Recipient)
|
||||
.Include(x => x.Sender)
|
||||
.Where(x => x.Recipient.Id == recipient.Id)
|
||||
.AsEnumerable()
|
||||
.TakeLast(50)
|
||||
.OrderBy(x => x.Id)
|
||||
.ToArray();
|
||||
|
||||
return Task.FromResult(messages);
|
||||
}
|
||||
|
||||
private Task Run()
|
||||
{
|
||||
ServiceScope = ServiceScopeFactory.CreateScope();
|
||||
|
||||
SupportMessageRepository = ServiceScope
|
||||
.ServiceProvider
|
||||
.GetRequiredService<SupportMessageRepository>();
|
||||
|
||||
MessageService = ServiceScope
|
||||
.ServiceProvider
|
||||
.GetRequiredService<MessageService>();
|
||||
|
||||
UserRepository = ServiceScope
|
||||
.ServiceProvider
|
||||
.GetRequiredService<UserRepository>();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
SupportMessageRepository.Dispose();
|
||||
UserRepository.Dispose();
|
||||
ServiceScope.Dispose();
|
||||
}
|
||||
}
|
|
@ -60,6 +60,7 @@
|
|||
<Folder Include="App\Http\Middleware" />
|
||||
<Folder Include="App\Models\AuditLogData" />
|
||||
<Folder Include="resources\lang" />
|
||||
<Folder Include="Shared\Views\Admin\Support" />
|
||||
<Folder Include="wwwroot\assets\media" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ using Moonlight.App.Repositories.Servers;
|
|||
using Moonlight.App.Services;
|
||||
using Moonlight.App.Services.Interop;
|
||||
using Moonlight.App.Services.Sessions;
|
||||
using Moonlight.App.Services.Support;
|
||||
|
||||
namespace Moonlight
|
||||
{
|
||||
|
@ -36,6 +37,7 @@ namespace Moonlight
|
|||
builder.Services.AddScoped<AuditLogRepository>();
|
||||
builder.Services.AddScoped<DatabaseRepository>();
|
||||
builder.Services.AddScoped<ImageRepository>();
|
||||
builder.Services.AddScoped<SupportMessageRepository>();
|
||||
|
||||
// Services
|
||||
builder.Services.AddSingleton<ConfigService>();
|
||||
|
@ -53,10 +55,16 @@ namespace Moonlight
|
|||
builder.Services.AddScoped<ServerService>();
|
||||
builder.Services.AddSingleton<PaperService>();
|
||||
builder.Services.AddScoped<ClipboardService>();
|
||||
builder.Services.AddSingleton<ResourceService>();
|
||||
|
||||
builder.Services.AddScoped<AuditLogService>();
|
||||
builder.Services.AddScoped<SystemAuditLogService>();
|
||||
|
||||
// Support
|
||||
builder.Services.AddSingleton<SupportServerService>();
|
||||
builder.Services.AddScoped<SupportAdminServer>();
|
||||
builder.Services.AddScoped<SupportClientService>();
|
||||
|
||||
// Helpers
|
||||
builder.Services.AddSingleton<SmartTranslateHelper>();
|
||||
builder.Services.AddScoped<WingsApiHelper>();
|
||||
|
@ -89,6 +97,9 @@ namespace Moonlight
|
|||
app.MapBlazorHub();
|
||||
app.MapFallbackToPage("/_Host");
|
||||
|
||||
// Support service
|
||||
var supportServerService = app.Services.GetRequiredService<SupportServerService>();
|
||||
|
||||
app.Run();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,8 +37,7 @@
|
|||
<SidebarMenu></SidebarMenu>
|
||||
|
||||
<div class="app-sidebar-footer flex-column-auto pt-2 pb-6 px-6" id="kt_app_sidebar_footer">
|
||||
<a id="support_ticket_toggle_sidebar"
|
||||
class="btn btn-flex flex-center btn-custom btn-primary overflow-hidden text-nowrap px-0 h-40px w-100 btn-label">
|
||||
<a href="/support" class="btn btn-flex flex-center btn-custom btn-primary overflow-hidden text-nowrap px-0 h-40px w-100 btn-label">
|
||||
<TL>Open support</TL>
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
@using Moonlight.App.Database.Entities
|
||||
@using Moonlight.App.Extensions
|
||||
@using Moonlight.App.Models.Misc
|
||||
@using Moonlight.App.Services
|
||||
@using Moonlight.App.Services.Interop
|
||||
@using Moonlight.App.Services.Sessions
|
||||
|
||||
@layout ThemeInit
|
||||
|
@ -15,6 +17,8 @@
|
|||
@inject IdentityService IdentityService
|
||||
@inject SessionService SessionService
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject MessageService MessageService
|
||||
@inject ToastService ToastService
|
||||
|
||||
<GlobalErrorBoundary>
|
||||
@{
|
||||
|
@ -151,6 +155,17 @@
|
|||
await SessionService.Register();
|
||||
|
||||
NavigationManager.LocationChanged += (sender, args) => { SessionService.Refresh(); };
|
||||
|
||||
MessageService.Subscribe<MainLayout, SupportMessage>(
|
||||
$"support.{User.Id}.message",
|
||||
this,
|
||||
async message =>
|
||||
{
|
||||
if (!NavigationManager.Uri.EndsWith("/support") && (message.IsSupport || message.IsSystem))
|
||||
{
|
||||
await ToastService.Info($"Support: {message.Message}");
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
@ -162,6 +177,11 @@
|
|||
public void Dispose()
|
||||
{
|
||||
SessionService.Close();
|
||||
|
||||
if (User != null)
|
||||
{
|
||||
MessageService.Unsubscribe($"support.{User.Id}.message", this);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddBodyAttribute(string attribute, string value)
|
||||
|
|
135
Moonlight/Shared/Views/Admin/Index.razor
Normal file
135
Moonlight/Shared/Views/Admin/Index.razor
Normal file
|
@ -0,0 +1,135 @@
|
|||
@page "/admin"
|
||||
@using Moonlight.App.Repositories.Servers
|
||||
@using Moonlight.App.Repositories
|
||||
|
||||
@inject ServerRepository ServerRepository
|
||||
@inject DatabaseRepository DatabaseRepository
|
||||
@inject UserRepository UserRepository
|
||||
|
||||
<OnlyAdmin>
|
||||
<LazyLoader Load="Load">
|
||||
<div class="row mb-5">
|
||||
<div class="col-12 col-lg-6 col-xl">
|
||||
<a class="mt-4 card" href="/servers">
|
||||
<div class="card-body">
|
||||
<div class="row align-items-center gx-0">
|
||||
<div class="col">
|
||||
<h6 class="text-uppercase text-muted mb-2">
|
||||
<TL>Servers</TL>
|
||||
</h6>
|
||||
<span class="h2 mb-0">
|
||||
@(ServerCount)
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<span class="h2 text-muted mb-0">
|
||||
<i class="text-primary bx bx-server bx-lg"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6 col-xl">
|
||||
<a class="mt-4 card" href="/websites">
|
||||
<div class="card-body">
|
||||
<div class="row align-items-center gx-0">
|
||||
<div class="col">
|
||||
<h6 class="text-uppercase text-muted mb-2">
|
||||
<TL>Websites</TL>
|
||||
</h6>
|
||||
<span class="h2 mb-0">
|
||||
0
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<span class="h2 text-muted mb-0">
|
||||
<i class="text-primary bx bx-globe bx-lg"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6 col-xl">
|
||||
<div class="mt-4 card" href="/databases">
|
||||
<div class="card-body">
|
||||
<div class="row align-items-center gx-0">
|
||||
<div class="col">
|
||||
<h6 class="text-uppercase text-muted mb-2">
|
||||
<TL>Databases</TL>
|
||||
</h6>
|
||||
<span class="h2 mb-0">
|
||||
@(DatabaseCount)
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<span class="h2 text-muted mb-0">
|
||||
<i class="text-primary bx bx-data bx-lg"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6 col-xl">
|
||||
<a class="mt-4 card" href="/domains">
|
||||
<div class="card-body">
|
||||
<div class="row align-items-center gx-0">
|
||||
<div class="col">
|
||||
<h6 class="text-uppercase text-muted mb-2">
|
||||
<TL>Domains</TL>
|
||||
</h6>
|
||||
<span class="h2 mb-0">
|
||||
0
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<span class="h2 text-muted mb-">
|
||||
<i class="text-primary bx bx-purchase-tag bx-lg"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6 col-xl">
|
||||
<a class="mt-4 card" href="/domains">
|
||||
<div class="card-body">
|
||||
<div class="row align-items-center gx-0">
|
||||
<div class="col">
|
||||
<h6 class="text-uppercase text-muted mb-2">
|
||||
<TL>Users</TL>
|
||||
</h6>
|
||||
<span class="h2 mb-0">
|
||||
@(UserCount)
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<span class="h2 text-muted mb-">
|
||||
<i class="text-primary bx bx-user bx-lg"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</LazyLoader>
|
||||
</OnlyAdmin>
|
||||
|
||||
@code
|
||||
{
|
||||
private int DatabaseCount = 0;
|
||||
private int ServerCount = 0;
|
||||
private int UserCount = 0;
|
||||
|
||||
private Task Load(LazyLoader lazyLoader)
|
||||
{
|
||||
DatabaseCount = DatabaseRepository.Get().Count();
|
||||
ServerCount = ServerRepository.Get().Count();
|
||||
UserCount = UserRepository.Get().Count();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
144
Moonlight/Shared/Views/Support.razor
Normal file
144
Moonlight/Shared/Views/Support.razor
Normal file
|
@ -0,0 +1,144 @@
|
|||
@page "/support"
|
||||
@using Moonlight.App.Services
|
||||
@using Moonlight.App.Services.Sessions
|
||||
@using Moonlight.App.Database.Entities
|
||||
@using Moonlight.App.Helpers
|
||||
@using Moonlight.App.Services.Support
|
||||
|
||||
@inject ResourceService ResourceService
|
||||
@inject IdentityService IdentityService
|
||||
@inject SupportClientService SupportClientService
|
||||
@inject SmartTranslateService SmartTranslateService
|
||||
|
||||
<LazyLoader Load="Load">
|
||||
<div class="row">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="scroll-y me-n5 pe-5" style="max-height: 65vh; display: flex; flex-direction: column-reverse;">
|
||||
@foreach (var message in Messages)
|
||||
{
|
||||
if (message.IsSystem || message.IsSupport)
|
||||
{
|
||||
<div class="d-flex justify-content-start mb-10 ">
|
||||
<div class="d-flex flex-column align-items-start">
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<div class="symbol symbol-35px symbol-circle ">
|
||||
<img alt="Logo" src="@(ResourceService.Image("logo.svg"))">
|
||||
</div>
|
||||
<div class="ms-3">
|
||||
<a class="fs-5 fw-bold text-gray-900 text-hover-primary me-1">
|
||||
@if (message.IsSupport && !message.IsSystem)
|
||||
{
|
||||
<span>@(message.Sender!.FirstName) @(message.Sender!.LastName)</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>System</span>
|
||||
}
|
||||
</a>
|
||||
<span class="text-muted fs-7 mb-1">@(Formatter.FormatAgoFromDateTime(message.CreatedAt, SmartTranslateService))</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-5 rounded bg-light-info text-dark fw-semibold mw-lg-400px text-start">
|
||||
@(message.Message)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="d-flex justify-content-end mb-10 ">
|
||||
<div class="d-flex flex-column align-items-end">
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<div class="me-3">
|
||||
<span class="text-muted fs-7 mb-1">@(Formatter.FormatAgoFromDateTime(message.CreatedAt, SmartTranslateService))</span>
|
||||
<a class="fs-5 fw-bold text-gray-900 text-hover-primary ms-1">
|
||||
<span>@(message.Sender!.FirstName) @(message.Sender!.LastName)</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="symbol symbol-35px symbol-circle ">
|
||||
<img alt="Avatar" src="@(ResourceService.Avatar(message.Sender))">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-5 rounded bg-light-primary text-dark fw-semibold mw-lg-400px text-end">
|
||||
@(message.Message)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
<div class="d-flex justify-content-start mb-10 ">
|
||||
<div class="d-flex flex-column align-items-start">
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<div class="symbol symbol-35px symbol-circle ">
|
||||
<img alt="Logo" src="@(ResourceService.Image("logo.svg"))">
|
||||
</div>
|
||||
<div class="ms-3">
|
||||
<a class="fs-5 fw-bold text-gray-900 text-hover-primary me-1">
|
||||
<span>System</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-5 rounded bg-light-info text-dark fw-semibold mw-lg-400px text-start">
|
||||
<TL>Welcome to the support chat. Ask your question here and we will help you</TL>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<textarea @bind="Content" class="form-control form-control-flush mb-3" rows="1" placeholder="Type a message">
|
||||
</textarea>
|
||||
<div class="d-flex flex-stack">
|
||||
<div class="d-flex align-items-center me-2">
|
||||
<button class="btn btn-sm btn-icon btn-active-light-primary me-1" type="button">
|
||||
<i class="bx bx-upload fs-3"></i>
|
||||
</button>
|
||||
</div>
|
||||
<WButton Text="@(SmartTranslateService.Translate("Send"))"
|
||||
WorkingText="@(SmartTranslateService.Translate("Sending"))"
|
||||
CssClasses="btn-primary"
|
||||
OnClick="Send">
|
||||
</WButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LazyLoader>
|
||||
|
||||
@code
|
||||
{
|
||||
private User User;
|
||||
|
||||
private SupportMessage[] Messages;
|
||||
private string Content = "";
|
||||
|
||||
private async Task Load(LazyLoader lazyLoader)
|
||||
{
|
||||
User = (await IdentityService.Get())!;
|
||||
|
||||
await lazyLoader.SetText("Starting chat client");
|
||||
|
||||
SupportClientService.OnNewMessage += OnNewMessage;
|
||||
|
||||
await SupportClientService.Start();
|
||||
|
||||
Messages = (await SupportClientService.GetMessages()).Reverse().ToArray();
|
||||
}
|
||||
|
||||
private async void OnNewMessage(object? sender, SupportMessage e)
|
||||
{
|
||||
Messages = (await SupportClientService.GetMessages()).Reverse().ToArray();
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private async Task Send()
|
||||
{
|
||||
await SupportClientService.SendMessage(Content);
|
||||
Content = "";
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
|
@ -167,6 +167,10 @@ build_metadata.AdditionalFiles.CssScope =
|
|||
build_metadata.AdditionalFiles.TargetPath = U2hhcmVkXExheW91dHNcVGhlbWVJbml0LnJhem9y
|
||||
build_metadata.AdditionalFiles.CssScope =
|
||||
|
||||
[C:/Users/marce/source/repos/MoonlightPublic/Moonlight/Moonlight/Shared/Views/Admin/Index.razor]
|
||||
build_metadata.AdditionalFiles.TargetPath = U2hhcmVkXFZpZXdzXEFkbWluXEluZGV4LnJhem9y
|
||||
build_metadata.AdditionalFiles.CssScope =
|
||||
|
||||
[C:/Users/marce/source/repos/MoonlightPublic/Moonlight/Moonlight/Shared/Views/Admin/Nodes/Edit.razor]
|
||||
build_metadata.AdditionalFiles.TargetPath = U2hhcmVkXFZpZXdzXEFkbWluXE5vZGVzXEVkaXQucmF6b3I=
|
||||
build_metadata.AdditionalFiles.CssScope =
|
||||
|
@ -223,6 +227,10 @@ build_metadata.AdditionalFiles.CssScope =
|
|||
build_metadata.AdditionalFiles.TargetPath = U2hhcmVkXFZpZXdzXFNldHVwXFVzZXJzLnJhem9y
|
||||
build_metadata.AdditionalFiles.CssScope =
|
||||
|
||||
[C:/Users/marce/source/repos/MoonlightPublic/Moonlight/Moonlight/Shared/Views/Support.razor]
|
||||
build_metadata.AdditionalFiles.TargetPath = U2hhcmVkXFZpZXdzXFN1cHBvcnQucmF6b3I=
|
||||
build_metadata.AdditionalFiles.CssScope =
|
||||
|
||||
[C:/Users/marce/source/repos/MoonlightPublic/Moonlight/Moonlight/_Imports.razor]
|
||||
build_metadata.AdditionalFiles.TargetPath = X0ltcG9ydHMucmF6b3I=
|
||||
build_metadata.AdditionalFiles.CssScope =
|
||||
|
|
|
@ -193,3 +193,25 @@ Running;Running
|
|||
Loading backups;Loading backups
|
||||
Started backup creation;Started backup creation
|
||||
Backup is going to be created;Backup is going to be created
|
||||
Rename;Rename
|
||||
Move;Move
|
||||
Archive;Archive
|
||||
Unarchive;Unarchive
|
||||
Download;Download
|
||||
Starting download;Starting download
|
||||
Backup successfully created;Backup successfully created
|
||||
Restore;Restore
|
||||
Copy url;Copy url
|
||||
Backup deletion started;Backup deletion started
|
||||
Backup successfully deleted;Backup successfully deleted
|
||||
Primary;Primary
|
||||
This feature is currently not available;This feature is currently not available
|
||||
Send;Send
|
||||
Sending;Sending
|
||||
Welcome to the support chat. Ask your question here and we will help you;Welcome to the support chat. Ask your question here and we will help you
|
||||
minutes ago; minutes ago
|
||||
just now;just now
|
||||
less than a minute ago;less than a minute ago
|
||||
1 hour ago;1 hour ago
|
||||
1 minute ago;1 minute ago
|
||||
Failed;Failed
|
||||
|
|
Loading…
Reference in a new issue