@@ -1,83 +0,0 @@
|
|||||||
using Moonlight.App.Database.Entities;
|
|
||||||
using Moonlight.App.Models.Plesk.Resources;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using RestSharp;
|
|
||||||
|
|
||||||
namespace Moonlight.App.ApiClients.CloudPanel;
|
|
||||||
|
|
||||||
public class CloudPanelApiHelper
|
|
||||||
{
|
|
||||||
private readonly RestClient Client;
|
|
||||||
|
|
||||||
public CloudPanelApiHelper()
|
|
||||||
{
|
|
||||||
Client = new();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Post(Database.Entities.CloudPanel cloudPanel, string resource, object? body)
|
|
||||||
{
|
|
||||||
var request = CreateRequest(cloudPanel, resource);
|
|
||||||
|
|
||||||
request.Method = Method.Post;
|
|
||||||
|
|
||||||
if(body != null)
|
|
||||||
request.AddParameter("text/plain", JsonConvert.SerializeObject(body), ParameterType.RequestBody);
|
|
||||||
|
|
||||||
var response = await Client.ExecuteAsync(request);
|
|
||||||
|
|
||||||
if (!response.IsSuccessful)
|
|
||||||
{
|
|
||||||
if (response.StatusCode != 0)
|
|
||||||
{
|
|
||||||
throw new CloudPanelException(
|
|
||||||
$"An error occured: ({response.StatusCode}) {response.Content}",
|
|
||||||
(int)response.StatusCode
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new Exception($"An internal error occured: {response.ErrorMessage}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Delete(Database.Entities.CloudPanel cloudPanel, string resource, object? body)
|
|
||||||
{
|
|
||||||
var request = CreateRequest(cloudPanel, resource);
|
|
||||||
|
|
||||||
request.Method = Method.Delete;
|
|
||||||
|
|
||||||
if(body != null)
|
|
||||||
request.AddParameter("text/plain", JsonConvert.SerializeObject(body), ParameterType.RequestBody);
|
|
||||||
|
|
||||||
var response = await Client.ExecuteAsync(request);
|
|
||||||
|
|
||||||
if (!response.IsSuccessful)
|
|
||||||
{
|
|
||||||
if (response.StatusCode != 0)
|
|
||||||
{
|
|
||||||
throw new CloudPanelException(
|
|
||||||
$"An error occured: ({response.StatusCode}) {response.Content}",
|
|
||||||
(int)response.StatusCode
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new Exception($"An internal error occured: {response.ErrorMessage}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private RestRequest CreateRequest(Database.Entities.CloudPanel cloudPanel, string resource)
|
|
||||||
{
|
|
||||||
var url = $"{cloudPanel.ApiUrl}/" + resource;
|
|
||||||
|
|
||||||
var request = new RestRequest(url);
|
|
||||||
|
|
||||||
request.AddHeader("Content-Type", "application/json");
|
|
||||||
request.AddHeader("Accept", "application/json");
|
|
||||||
request.AddHeader("Authorization", "Bearer " + cloudPanel.ApiKey);
|
|
||||||
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
using System.Runtime.Serialization;
|
|
||||||
|
|
||||||
namespace Moonlight.App.ApiClients.CloudPanel;
|
|
||||||
|
|
||||||
[Serializable]
|
|
||||||
public class CloudPanelException : Exception
|
|
||||||
{
|
|
||||||
public int StatusCode { get; set; }
|
|
||||||
|
|
||||||
public CloudPanelException()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public CloudPanelException(string message, int statusCode) : base(message)
|
|
||||||
{
|
|
||||||
StatusCode = statusCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CloudPanelException(string message) : base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public CloudPanelException(string message, Exception inner) : base(message, inner)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected CloudPanelException(
|
|
||||||
SerializationInfo info,
|
|
||||||
StreamingContext context) : base(info, context)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Moonlight.App.ApiClients.CloudPanel.Requests;
|
|
||||||
|
|
||||||
public class AddDatabase
|
|
||||||
{
|
|
||||||
[JsonProperty("domainName")]
|
|
||||||
public string DomainName { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("databaseName")]
|
|
||||||
public string DatabaseName { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("databaseUserName")]
|
|
||||||
public string DatabaseUserName { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("databaseUserPassword")]
|
|
||||||
public string DatabaseUserPassword { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Moonlight.App.ApiClients.CloudPanel.Requests;
|
|
||||||
|
|
||||||
public class AddPhpSite
|
|
||||||
{
|
|
||||||
[JsonProperty("domainName")] public string DomainName { get; set; } = "";
|
|
||||||
|
|
||||||
[JsonProperty("siteUser")] public string SiteUser { get; set; } = "";
|
|
||||||
|
|
||||||
[JsonProperty("siteUserPassword")] public string SiteUserPassword { get; set; } = "";
|
|
||||||
|
|
||||||
[JsonProperty("vHostTemplate")] public string VHostTemplate { get; set; } = "";
|
|
||||||
|
|
||||||
[JsonProperty("phpVersion")] public string PhpVersion { get; set; } = "";
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Moonlight.App.ApiClients.CloudPanel.Requests;
|
|
||||||
|
|
||||||
public class InstallLetsEncrypt
|
|
||||||
{
|
|
||||||
[JsonProperty("domainName")]
|
|
||||||
public string DomainName { get; set; }
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
using Moonlight.App.Database.Entities;
|
using Moonlight.App.Database.Entities;
|
||||||
using Moonlight.App.Database.Entities.LogsEntries;
|
using Moonlight.App.Database.Entities.LogsEntries;
|
||||||
using Moonlight.App.Database.Entities.Notification;
|
using Moonlight.App.Database.Entities.Notification;
|
||||||
using Moonlight.App.Database.Interceptors;
|
|
||||||
using Moonlight.App.Models.Misc;
|
using Moonlight.App.Models.Misc;
|
||||||
using Moonlight.App.Services;
|
using Moonlight.App.Services;
|
||||||
|
|
||||||
@@ -30,7 +29,8 @@ public class DataContext : DbContext
|
|||||||
public DbSet<AuditLogEntry> AuditLog { get; set; }
|
public DbSet<AuditLogEntry> AuditLog { get; set; }
|
||||||
public DbSet<ErrorLogEntry> ErrorLog { get; set; }
|
public DbSet<ErrorLogEntry> ErrorLog { get; set; }
|
||||||
public DbSet<SecurityLogEntry> SecurityLog { get; set; }
|
public DbSet<SecurityLogEntry> SecurityLog { get; set; }
|
||||||
|
public DbSet<SupportMessage> SupportMessages { get; set; }
|
||||||
|
|
||||||
public DbSet<SharedDomain> SharedDomains { get; set; }
|
public DbSet<SharedDomain> SharedDomains { get; set; }
|
||||||
public DbSet<Domain> Domains { get; set; }
|
public DbSet<Domain> Domains { get; set; }
|
||||||
public DbSet<Revoke> Revokes { get; set; }
|
public DbSet<Revoke> Revokes { get; set; }
|
||||||
@@ -38,13 +38,10 @@ public class DataContext : DbContext
|
|||||||
public DbSet<NotificationAction> NotificationActions { get; set; }
|
public DbSet<NotificationAction> NotificationActions { get; set; }
|
||||||
public DbSet<DdosAttack> DdosAttacks { get; set; }
|
public DbSet<DdosAttack> DdosAttacks { get; set; }
|
||||||
public DbSet<Subscription> Subscriptions { get; set; }
|
public DbSet<Subscription> Subscriptions { get; set; }
|
||||||
|
public DbSet<PleskServer> PleskServers { get; set; }
|
||||||
|
public DbSet<Website> Websites { get; set; }
|
||||||
public DbSet<StatisticsData> Statistics { get; set; }
|
public DbSet<StatisticsData> Statistics { get; set; }
|
||||||
public DbSet<NewsEntry> NewsEntries { get; set; }
|
public DbSet<NewsEntry> NewsEntries { get; set; }
|
||||||
|
|
||||||
public DbSet<CloudPanel> CloudPanels { get; set; }
|
|
||||||
public DbSet<MySqlDatabase> Databases { get; set; }
|
|
||||||
public DbSet<WebSpace> WebSpaces { get; set; }
|
|
||||||
public DbSet<SupportChatMessage> SupportChatMessages { get; set; }
|
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
{
|
{
|
||||||
@@ -68,9 +65,6 @@ public class DataContext : DbContext
|
|||||||
builder.EnableRetryOnFailure(5);
|
builder.EnableRetryOnFailure(5);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if(ConfigService.SqlDebugMode)
|
|
||||||
optionsBuilder.AddInterceptors(new SqlLoggingInterceptor());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
using Newtonsoft.Json;
|
namespace Moonlight.App.Database.Entities;
|
||||||
|
|
||||||
namespace Moonlight.App.Database.Entities;
|
|
||||||
|
|
||||||
public class DockerImage
|
public class DockerImage
|
||||||
{
|
{
|
||||||
[JsonIgnore]
|
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public bool Default { get; set; } = false;
|
public bool Default { get; set; } = false;
|
||||||
public string Name { get; set; } = "";
|
public string Name { get; set; } = "";
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
using Newtonsoft.Json;
|
namespace Moonlight.App.Database.Entities;
|
||||||
|
|
||||||
namespace Moonlight.App.Database.Entities;
|
|
||||||
|
|
||||||
public class Image
|
public class Image
|
||||||
{
|
{
|
||||||
[JsonIgnore]
|
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public Guid Uuid { get; set; }
|
public Guid Uuid { get; set; }
|
||||||
public string Name { get; set; } = "";
|
public string Name { get; set; } = "";
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
using Newtonsoft.Json;
|
namespace Moonlight.App.Database.Entities;
|
||||||
|
|
||||||
namespace Moonlight.App.Database.Entities;
|
|
||||||
|
|
||||||
public class ImageVariable
|
public class ImageVariable
|
||||||
{
|
{
|
||||||
[JsonIgnore]
|
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public string Key { get; set; } = "";
|
public string Key { get; set; } = "";
|
||||||
public string DefaultValue { get; set; } = "";
|
public string DefaultValue { get; set; } = "";
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
namespace Moonlight.App.Database.Entities;
|
|
||||||
|
|
||||||
public class MySqlDatabase
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
public WebSpace WebSpace { get; set; }
|
|
||||||
public string UserName { get; set; } = "";
|
|
||||||
public string Password { get; set; } = "";
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
namespace Moonlight.App.Database.Entities;
|
namespace Moonlight.App.Database.Entities;
|
||||||
|
|
||||||
public class CloudPanel
|
public class PleskServer
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public string Name { get; set; } = "";
|
public string Name { get; set; } = "";
|
||||||
public string ApiUrl { get; set; } = "";
|
public string ApiUrl { get; set; } = "";
|
||||||
public string ApiKey { get; set; } = "";
|
public string ApiKey { get; set; } = "";
|
||||||
public string Host { get; set; } = "";
|
|
||||||
}
|
}
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
using Moonlight.App.Models.Misc;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Database.Entities;
|
|
||||||
|
|
||||||
public class SupportChatMessage
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
public string Content { get; set; } = "";
|
|
||||||
public string Attachment { get; set; } = "";
|
|
||||||
|
|
||||||
public User? Sender { get; set; }
|
|
||||||
public User Recipient { get; set; }
|
|
||||||
|
|
||||||
public bool IsQuestion { get; set; } = false;
|
|
||||||
public QuestionType QuestionType { get; set; }
|
|
||||||
public string Answer { get; set; } = "";
|
|
||||||
|
|
||||||
public DateTime CreatedAt { get; set; }
|
|
||||||
public DateTime UpdatedAt { get; set; }
|
|
||||||
}
|
|
||||||
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;
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Moonlight.App.Models.Misc;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Moonlight.App.Models.Misc;
|
||||||
|
|
||||||
namespace Moonlight.App.Database.Entities;
|
namespace Moonlight.App.Database.Entities;
|
||||||
|
|
||||||
@@ -33,7 +34,7 @@ public class User
|
|||||||
// Security
|
// Security
|
||||||
public bool TotpEnabled { get; set; } = false;
|
public bool TotpEnabled { get; set; } = false;
|
||||||
public string TotpSecret { get; set; } = "";
|
public string TotpSecret { get; set; } = "";
|
||||||
public DateTime TokenValidTime { get; set; } = DateTime.UtcNow;
|
public DateTime TokenValidTime { get; set; } = DateTime.Now;
|
||||||
|
|
||||||
// Discord
|
// Discord
|
||||||
public ulong DiscordId { get; set; }
|
public ulong DiscordId { get; set; }
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
namespace Moonlight.App.Database.Entities;
|
|
||||||
|
|
||||||
public class WebSpace
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
public string Domain { get; set; } = "";
|
|
||||||
public string UserName { get; set; } = "";
|
|
||||||
public string Password { get; set; } = "";
|
|
||||||
public string VHostTemplate { get; set; } = "";
|
|
||||||
public User Owner { get; set; }
|
|
||||||
public List<MySqlDatabase> Databases { get; set; } = new();
|
|
||||||
public CloudPanel CloudPanel { get; set; }
|
|
||||||
}
|
|
||||||
12
Moonlight/App/Database/Entities/Website.cs
Normal file
12
Moonlight/App/Database/Entities/Website.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Moonlight.App.Database.Entities;
|
||||||
|
|
||||||
|
public class Website
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string BaseDomain { get; set; } = "";
|
||||||
|
public int PleskId { get; set; }
|
||||||
|
public PleskServer PleskServer { get; set; }
|
||||||
|
public User Owner { get; set; }
|
||||||
|
public string FtpLogin { get; set; } = "";
|
||||||
|
public string FtpPassword { get; set; } = "";
|
||||||
|
}
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
using System.Data.Common;
|
|
||||||
using Logging.Net;
|
|
||||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Database.Interceptors;
|
|
||||||
|
|
||||||
public class SqlLoggingInterceptor : DbCommandInterceptor
|
|
||||||
{
|
|
||||||
public override InterceptionResult<DbDataReader> ReaderExecuting(
|
|
||||||
DbCommand command,
|
|
||||||
CommandEventData eventData,
|
|
||||||
InterceptionResult<DbDataReader> result)
|
|
||||||
{
|
|
||||||
LogSql(command.CommandText);
|
|
||||||
return base.ReaderExecuting(command, eventData, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override InterceptionResult<object> ScalarExecuting(
|
|
||||||
DbCommand command,
|
|
||||||
CommandEventData eventData,
|
|
||||||
InterceptionResult<object> result)
|
|
||||||
{
|
|
||||||
LogSql(command.CommandText);
|
|
||||||
return base.ScalarExecuting(command, eventData, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override InterceptionResult<int> NonQueryExecuting(
|
|
||||||
DbCommand command,
|
|
||||||
CommandEventData eventData,
|
|
||||||
InterceptionResult<int> result)
|
|
||||||
{
|
|
||||||
LogSql(command.CommandText);
|
|
||||||
return base.NonQueryExecuting(command, eventData, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LogSql(string sql)
|
|
||||||
{
|
|
||||||
Logger.Info($"[SQL DEBUG] {sql.Replace("\n", "")}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,121 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.App.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddedCloudPanelModels : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "CloudPanels",
|
|
||||||
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"),
|
|
||||||
ApiUrl = table.Column<string>(type: "longtext", nullable: false)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
ApiKey = table.Column<string>(type: "longtext", nullable: false)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4")
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_CloudPanels", x => x.Id);
|
|
||||||
})
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4");
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "WebSpaces",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "int", nullable: false)
|
|
||||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
|
||||||
Domain = table.Column<string>(type: "longtext", nullable: false)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
UserName = table.Column<string>(type: "longtext", nullable: false)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
Password = table.Column<string>(type: "longtext", nullable: false)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
VHostTemplate = table.Column<string>(type: "longtext", nullable: false)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
OwnerId = table.Column<int>(type: "int", nullable: false),
|
|
||||||
CloudPanelId = table.Column<int>(type: "int", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_WebSpaces", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_WebSpaces_CloudPanels_CloudPanelId",
|
|
||||||
column: x => x.CloudPanelId,
|
|
||||||
principalTable: "CloudPanels",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_WebSpaces_Users_OwnerId",
|
|
||||||
column: x => x.OwnerId,
|
|
||||||
principalTable: "Users",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
})
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4");
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Databases",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "int", nullable: false)
|
|
||||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
|
||||||
WebSpaceId = table.Column<int>(type: "int", nullable: false),
|
|
||||||
UserName = table.Column<string>(type: "longtext", nullable: false)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
Password = table.Column<string>(type: "longtext", nullable: false)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4")
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Databases", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Databases_WebSpaces_WebSpaceId",
|
|
||||||
column: x => x.WebSpaceId,
|
|
||||||
principalTable: "WebSpaces",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
})
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Databases_WebSpaceId",
|
|
||||||
table: "Databases",
|
|
||||||
column: "WebSpaceId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_WebSpaces_CloudPanelId",
|
|
||||||
table: "WebSpaces",
|
|
||||||
column: "CloudPanelId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_WebSpaces_OwnerId",
|
|
||||||
table: "WebSpaces",
|
|
||||||
column: "OwnerId");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Databases");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "WebSpaces");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "CloudPanels");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,29 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.App.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddedHostFieldToCloudPanelModel : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.AddColumn<string>(
|
|
||||||
name: "Host",
|
|
||||||
table: "CloudPanels",
|
|
||||||
type: "longtext",
|
|
||||||
nullable: false)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "Host",
|
|
||||||
table: "CloudPanels");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,138 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.App.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddedNewSupportChatModels : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Websites");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "PleskServers");
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "SupportChatMessages",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "int", nullable: false)
|
|
||||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
|
||||||
Content = table.Column<string>(type: "longtext", nullable: false)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
Attachment = table.Column<string>(type: "longtext", nullable: false)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
SenderId = table.Column<int>(type: "int", nullable: true),
|
|
||||||
RecipientId = table.Column<int>(type: "int", nullable: false),
|
|
||||||
IsQuestion = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
|
||||||
QuestionType = table.Column<int>(type: "int", nullable: false),
|
|
||||||
Answer = table.Column<string>(type: "longtext", nullable: false)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
|
||||||
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_SupportChatMessages", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_SupportChatMessages_Users_RecipientId",
|
|
||||||
column: x => x.RecipientId,
|
|
||||||
principalTable: "Users",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_SupportChatMessages_Users_SenderId",
|
|
||||||
column: x => x.SenderId,
|
|
||||||
principalTable: "Users",
|
|
||||||
principalColumn: "Id");
|
|
||||||
})
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_SupportChatMessages_RecipientId",
|
|
||||||
table: "SupportChatMessages",
|
|
||||||
column: "RecipientId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_SupportChatMessages_SenderId",
|
|
||||||
table: "SupportChatMessages",
|
|
||||||
column: "SenderId");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "SupportChatMessages");
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "PleskServers",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "int", nullable: false)
|
|
||||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
|
||||||
ApiKey = table.Column<string>(type: "longtext", nullable: false)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
ApiUrl = table.Column<string>(type: "longtext", nullable: false)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
Name = table.Column<string>(type: "longtext", nullable: false)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4")
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_PleskServers", x => x.Id);
|
|
||||||
})
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4");
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Websites",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "int", nullable: false)
|
|
||||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
|
||||||
OwnerId = table.Column<int>(type: "int", nullable: false),
|
|
||||||
PleskServerId = table.Column<int>(type: "int", nullable: false),
|
|
||||||
BaseDomain = table.Column<string>(type: "longtext", nullable: false)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
FtpLogin = table.Column<string>(type: "longtext", nullable: false)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
FtpPassword = table.Column<string>(type: "longtext", nullable: false)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
PleskId = table.Column<int>(type: "int", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Websites", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Websites_PleskServers_PleskServerId",
|
|
||||||
column: x => x.PleskServerId,
|
|
||||||
principalTable: "PleskServers",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_Websites_Users_OwnerId",
|
|
||||||
column: x => x.OwnerId,
|
|
||||||
principalTable: "Users",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
})
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Websites_OwnerId",
|
|
||||||
table: "Websites",
|
|
||||||
column: "OwnerId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Websites_PleskServerId",
|
|
||||||
table: "Websites",
|
|
||||||
column: "PleskServerId");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,67 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.App.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class RemovedOldSupportChatModel : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "SupportMessages");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "SupportMessages",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "int", nullable: false)
|
|
||||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
|
||||||
RecipientId = table.Column<int>(type: "int", nullable: true),
|
|
||||||
SenderId = table.Column<int>(type: "int", nullable: true),
|
|
||||||
Answer = table.Column<string>(type: "longtext", nullable: false)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
|
||||||
IsQuestion = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
|
||||||
IsSupport = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
|
||||||
IsSystem = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
|
||||||
Message = table.Column<string>(type: "longtext", nullable: false)
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
|
||||||
Type = table.Column<int>(type: "int", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_SupportMessages", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_SupportMessages_Users_RecipientId",
|
|
||||||
column: x => x.RecipientId,
|
|
||||||
principalTable: "Users",
|
|
||||||
principalColumn: "Id");
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_SupportMessages_Users_SenderId",
|
|
||||||
column: x => x.SenderId,
|
|
||||||
principalTable: "Users",
|
|
||||||
principalColumn: "Id");
|
|
||||||
})
|
|
||||||
.Annotation("MySql:CharSet", "utf8mb4");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_SupportMessages_RecipientId",
|
|
||||||
table: "SupportMessages",
|
|
||||||
column: "RecipientId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_SupportMessages_SenderId",
|
|
||||||
table: "SupportMessages",
|
|
||||||
column: "SenderId");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,33 +19,6 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
.HasAnnotation("ProductVersion", "7.0.3")
|
.HasAnnotation("ProductVersion", "7.0.3")
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.App.Database.Entities.CloudPanel", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("ApiKey")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("ApiUrl")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("Host")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("CloudPanels");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.App.Database.Entities.DdosAttack", b =>
|
modelBuilder.Entity("Moonlight.App.Database.Entities.DdosAttack", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -308,30 +281,6 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
b.ToTable("SecurityLog");
|
b.ToTable("SecurityLog");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.App.Database.Entities.MySqlDatabase", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.Property<string>("Password")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("UserName")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<int>("WebSpaceId")
|
|
||||||
.HasColumnType("int");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("WebSpaceId");
|
|
||||||
|
|
||||||
b.ToTable("Databases");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.App.Database.Entities.NewsEntry", b =>
|
modelBuilder.Entity("Moonlight.App.Database.Entities.NewsEntry", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -453,6 +402,29 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
b.ToTable("NotificationClients");
|
b.ToTable("NotificationClients");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.App.Database.Entities.PleskServer", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ApiKey")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("ApiUrl")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("PleskServers");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.App.Database.Entities.Revoke", b =>
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Revoke", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -650,7 +622,7 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
b.ToTable("Subscriptions");
|
b.ToTable("Subscriptions");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.App.Database.Entities.SupportChatMessage", b =>
|
modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -660,31 +632,30 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
b.Property<string>("Attachment")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("Content")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
b.Property<DateTime>("CreatedAt")
|
||||||
.HasColumnType("datetime(6)");
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
b.Property<bool>("IsQuestion")
|
b.Property<bool>("IsQuestion")
|
||||||
.HasColumnType("tinyint(1)");
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
b.Property<int>("QuestionType")
|
b.Property<bool>("IsSupport")
|
||||||
.HasColumnType("int");
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
b.Property<int>("RecipientId")
|
b.Property<bool>("IsSystem")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<string>("Message")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int?>("RecipientId")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<int?>("SenderId")
|
b.Property<int?>("SenderId")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<DateTime>("UpdatedAt")
|
b.Property<int>("Type")
|
||||||
.HasColumnType("datetime(6)");
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
@@ -692,7 +663,7 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("SenderId");
|
b.HasIndex("SenderId");
|
||||||
|
|
||||||
b.ToTable("SupportChatMessages");
|
b.ToTable("SupportMessages");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
|
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
|
||||||
@@ -777,41 +748,40 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
b.ToTable("Users");
|
b.ToTable("Users");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b =>
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<int>("CloudPanelId")
|
b.Property<string>("BaseDomain")
|
||||||
.HasColumnType("int");
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
b.Property<string>("Domain")
|
b.Property<string>("FtpLogin")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("FtpPassword")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
b.Property<int>("OwnerId")
|
b.Property<int>("OwnerId")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<string>("Password")
|
b.Property<int>("PleskId")
|
||||||
.IsRequired()
|
.HasColumnType("int");
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("UserName")
|
b.Property<int>("PleskServerId")
|
||||||
.IsRequired()
|
.HasColumnType("int");
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.Property<string>("VHostTemplate")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("longtext");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("CloudPanelId");
|
|
||||||
|
|
||||||
b.HasIndex("OwnerId");
|
b.HasIndex("OwnerId");
|
||||||
|
|
||||||
b.ToTable("WebSpaces");
|
b.HasIndex("PleskServerId");
|
||||||
|
|
||||||
|
b.ToTable("Websites");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.App.Database.Entities.DdosAttack", b =>
|
modelBuilder.Entity("Moonlight.App.Database.Entities.DdosAttack", b =>
|
||||||
@@ -858,17 +828,6 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
.HasForeignKey("ImageId");
|
.HasForeignKey("ImageId");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.App.Database.Entities.MySqlDatabase", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Moonlight.App.Database.Entities.WebSpace", "WebSpace")
|
|
||||||
.WithMany("Databases")
|
|
||||||
.HasForeignKey("WebSpaceId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("WebSpace");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.App.Database.Entities.NodeAllocation", b =>
|
modelBuilder.Entity("Moonlight.App.Database.Entities.NodeAllocation", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Moonlight.App.Database.Entities.Node", null)
|
b.HasOne("Moonlight.App.Database.Entities.Node", null)
|
||||||
@@ -949,13 +908,11 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
.HasForeignKey("ServerId");
|
.HasForeignKey("ServerId");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.App.Database.Entities.SupportChatMessage", b =>
|
modelBuilder.Entity("Moonlight.App.Database.Entities.SupportMessage", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Moonlight.App.Database.Entities.User", "Recipient")
|
b.HasOne("Moonlight.App.Database.Entities.User", "Recipient")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("RecipientId")
|
.HasForeignKey("RecipientId");
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.HasOne("Moonlight.App.Database.Entities.User", "Sender")
|
b.HasOne("Moonlight.App.Database.Entities.User", "Sender")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
@@ -975,23 +932,23 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
b.Navigation("CurrentSubscription");
|
b.Navigation("CurrentSubscription");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b =>
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Moonlight.App.Database.Entities.CloudPanel", "CloudPanel")
|
|
||||||
.WithMany()
|
|
||||||
.HasForeignKey("CloudPanelId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.HasOne("Moonlight.App.Database.Entities.User", "Owner")
|
b.HasOne("Moonlight.App.Database.Entities.User", "Owner")
|
||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("OwnerId")
|
.HasForeignKey("OwnerId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
|
|
||||||
b.Navigation("CloudPanel");
|
b.HasOne("Moonlight.App.Database.Entities.PleskServer", "PleskServer")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PleskServerId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
b.Navigation("Owner");
|
b.Navigation("Owner");
|
||||||
|
|
||||||
|
b.Navigation("PleskServer");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b =>
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b =>
|
||||||
@@ -1014,11 +971,6 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
|
|
||||||
b.Navigation("Variables");
|
b.Navigation("Variables");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Databases");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,142 +0,0 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using Logging.Net;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Events;
|
|
||||||
|
|
||||||
public class EventSystem
|
|
||||||
{
|
|
||||||
private Dictionary<int, object> Storage = new();
|
|
||||||
private List<Subscriber> Subscribers = new();
|
|
||||||
|
|
||||||
private readonly bool Debug = false;
|
|
||||||
private readonly bool DisableWarning = false;
|
|
||||||
private readonly TimeSpan TookToLongTime = TimeSpan.FromSeconds(1);
|
|
||||||
|
|
||||||
public Task On<T>(string id, object handle, Func<T, Task> action)
|
|
||||||
{
|
|
||||||
if(Debug)
|
|
||||||
Logger.Debug($"{handle} subscribed to '{id}'");
|
|
||||||
|
|
||||||
lock (Subscribers)
|
|
||||||
{
|
|
||||||
if (!Subscribers.Any(x => x.Id == id && x.Handle == handle))
|
|
||||||
{
|
|
||||||
Subscribers.Add(new ()
|
|
||||||
{
|
|
||||||
Action = action,
|
|
||||||
Handle = handle,
|
|
||||||
Id = id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task Emit(string id, object? d = null)
|
|
||||||
{
|
|
||||||
int hashCode = -1;
|
|
||||||
|
|
||||||
if (d != null)
|
|
||||||
{
|
|
||||||
hashCode = d.GetHashCode();
|
|
||||||
Storage.TryAdd(hashCode, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
Subscriber[] subscribers;
|
|
||||||
|
|
||||||
lock (Subscribers)
|
|
||||||
{
|
|
||||||
subscribers = Subscribers
|
|
||||||
.Where(x => x.Id == id)
|
|
||||||
.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
var tasks = new List<Task>();
|
|
||||||
|
|
||||||
foreach (var subscriber in subscribers)
|
|
||||||
{
|
|
||||||
tasks.Add(new Task(() =>
|
|
||||||
{
|
|
||||||
int storageId = hashCode + 0; // To create a copy of the hash code
|
|
||||||
|
|
||||||
object? data = null;
|
|
||||||
|
|
||||||
if (storageId != -1)
|
|
||||||
{
|
|
||||||
if (Storage.TryGetValue(storageId, out var value))
|
|
||||||
{
|
|
||||||
data = value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.Warn($"Object with the hash '{storageId}' was not present in the storage");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var stopWatch = new Stopwatch();
|
|
||||||
stopWatch.Start();
|
|
||||||
|
|
||||||
var del = (Delegate)subscriber.Action;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
((Task)del.DynamicInvoke(data)!).Wait();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Logger.Warn($"Error emitting '{subscriber.Id} on {subscriber.Handle}'");
|
|
||||||
Logger.Warn(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
stopWatch.Stop();
|
|
||||||
|
|
||||||
if (!DisableWarning)
|
|
||||||
{
|
|
||||||
if (stopWatch.Elapsed.TotalMilliseconds > TookToLongTime.TotalMilliseconds)
|
|
||||||
{
|
|
||||||
Logger.Warn($"Subscriber {subscriber.Handle} for event '{subscriber.Id}' took long to process. {stopWatch.Elapsed.TotalMilliseconds}ms");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Debug)
|
|
||||||
{
|
|
||||||
Logger.Debug($"Subscriber {subscriber.Handle} for event '{subscriber.Id}' took {stopWatch.Elapsed.TotalMilliseconds}ms");
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var task in tasks)
|
|
||||||
{
|
|
||||||
task.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
Task.Run(() =>
|
|
||||||
{
|
|
||||||
Task.WaitAll(tasks.ToArray());
|
|
||||||
Storage.Remove(hashCode);
|
|
||||||
|
|
||||||
if(Debug)
|
|
||||||
Logger.Debug($"Completed all event tasks for '{id}' and removed object from storage");
|
|
||||||
});
|
|
||||||
|
|
||||||
if(Debug)
|
|
||||||
Logger.Debug($"Completed event emit '{id}'");
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task Off(string id, object handle)
|
|
||||||
{
|
|
||||||
if(Debug)
|
|
||||||
Logger.Debug($"{handle} unsubscribed to '{id}'");
|
|
||||||
|
|
||||||
lock (Subscribers)
|
|
||||||
{
|
|
||||||
Subscribers.RemoveAll(x => x.Id == id && x.Handle == handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace Moonlight.App.Events;
|
|
||||||
|
|
||||||
public class Subscriber
|
|
||||||
{
|
|
||||||
public string Id { get; set; }
|
|
||||||
public object Action { get; set; }
|
|
||||||
public object Handle { get; set; }
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,7 @@ namespace Moonlight.App.Exceptions;
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
public class DaemonException : Exception
|
public class DaemonException : Exception
|
||||||
{
|
{
|
||||||
public int StatusCode { get; set; }
|
public int StatusCode { private get; set; }
|
||||||
|
|
||||||
public DaemonException()
|
public DaemonException()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace Moonlight.App.Exceptions;
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
public class PleskException : Exception
|
public class PleskException : Exception
|
||||||
{
|
{
|
||||||
public int StatusCode { get; set; }
|
public int StatusCode { private get; set; }
|
||||||
|
|
||||||
public PleskException()
|
public PleskException()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace Moonlight.App.Exceptions;
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
public class WingsException : Exception
|
public class WingsException : Exception
|
||||||
{
|
{
|
||||||
public int StatusCode { get; set; }
|
public int StatusCode { private get; set; }
|
||||||
|
|
||||||
public WingsException()
|
public WingsException()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Extensions;
|
|
||||||
|
|
||||||
public static class DbSetExtensions
|
|
||||||
{
|
|
||||||
public static T Random<T>(this DbSet<T> repo) where T : class
|
|
||||||
{
|
|
||||||
Random rand = new Random();
|
|
||||||
int toSkip = rand.Next(0, repo.Count());
|
|
||||||
|
|
||||||
return repo.Skip(toSkip).Take(1).First();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,206 +0,0 @@
|
|||||||
using Logging.Net;
|
|
||||||
using Renci.SshNet;
|
|
||||||
using ConnectionInfo = Renci.SshNet.ConnectionInfo;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Helpers.Files;
|
|
||||||
|
|
||||||
public class SftpFileAccess : FileAccess
|
|
||||||
{
|
|
||||||
private readonly string SftpHost;
|
|
||||||
private readonly string SftpUser;
|
|
||||||
private readonly string SftpPassword;
|
|
||||||
private readonly int SftpPort;
|
|
||||||
private readonly bool ForceUserDir;
|
|
||||||
|
|
||||||
private readonly SftpClient Client;
|
|
||||||
|
|
||||||
private string InternalPath
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (ForceUserDir)
|
|
||||||
return $"/home/{SftpUser}{CurrentPath}";
|
|
||||||
|
|
||||||
return InternalPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public SftpFileAccess(string sftpHost, string sftpUser, string sftpPassword, int sftpPort,
|
|
||||||
bool forceUserDir = false)
|
|
||||||
{
|
|
||||||
SftpHost = sftpHost;
|
|
||||||
SftpUser = sftpUser;
|
|
||||||
SftpPassword = sftpPassword;
|
|
||||||
SftpPort = sftpPort;
|
|
||||||
ForceUserDir = forceUserDir;
|
|
||||||
|
|
||||||
Client = new(
|
|
||||||
new ConnectionInfo(
|
|
||||||
SftpHost,
|
|
||||||
SftpPort,
|
|
||||||
SftpUser,
|
|
||||||
new PasswordAuthenticationMethod(
|
|
||||||
SftpUser,
|
|
||||||
SftpPassword
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EnsureConnect()
|
|
||||||
{
|
|
||||||
if (!Client.IsConnected)
|
|
||||||
Client.Connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public override Task<FileData[]> Ls()
|
|
||||||
{
|
|
||||||
EnsureConnect();
|
|
||||||
|
|
||||||
var x = new List<FileData>();
|
|
||||||
|
|
||||||
foreach (var file in Client.ListDirectory(InternalPath))
|
|
||||||
{
|
|
||||||
if (file.Name != "." && file.Name != "..")
|
|
||||||
{
|
|
||||||
x.Add(new()
|
|
||||||
{
|
|
||||||
Name = file.Name,
|
|
||||||
Size = file.Attributes.Size,
|
|
||||||
IsFile = !file.IsDirectory
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.FromResult(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 Task<string> Read(FileData fileData)
|
|
||||||
{
|
|
||||||
EnsureConnect();
|
|
||||||
|
|
||||||
var textStream = Client.Open(InternalPath.TrimEnd('/') + "/" + fileData.Name, FileMode.Open);
|
|
||||||
|
|
||||||
if (textStream == null)
|
|
||||||
return Task.FromResult("");
|
|
||||||
|
|
||||||
var streamReader = new StreamReader(textStream);
|
|
||||||
|
|
||||||
var text = streamReader.ReadToEnd();
|
|
||||||
|
|
||||||
streamReader.Close();
|
|
||||||
textStream.Close();
|
|
||||||
|
|
||||||
return Task.FromResult(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task Write(FileData fileData, string content)
|
|
||||||
{
|
|
||||||
EnsureConnect();
|
|
||||||
|
|
||||||
var textStream = Client.Open(InternalPath.TrimEnd('/') + "/" + fileData.Name, FileMode.Create);
|
|
||||||
|
|
||||||
var streamWriter = new StreamWriter(textStream);
|
|
||||||
streamWriter.Write(content);
|
|
||||||
|
|
||||||
streamWriter.Flush();
|
|
||||||
textStream.Flush();
|
|
||||||
|
|
||||||
streamWriter.Close();
|
|
||||||
textStream.Close();
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task Upload(string name, Stream stream, Action<int>? progressUpdated = null)
|
|
||||||
{
|
|
||||||
var dataStream = new SyncStreamAdapter(stream);
|
|
||||||
|
|
||||||
await Task.Factory.FromAsync((x, _) => Client.BeginUploadFile(dataStream, InternalPath + name, x, null, u =>
|
|
||||||
{
|
|
||||||
progressUpdated?.Invoke((int)((long)u / stream.Length));
|
|
||||||
}),
|
|
||||||
Client.EndUploadFile, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task MkDir(string name)
|
|
||||||
{
|
|
||||||
Client.CreateDirectory(InternalPath + name);
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task<string> Pwd()
|
|
||||||
{
|
|
||||||
return Task.FromResult(CurrentPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task<string> DownloadUrl(FileData fileData)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task<Stream> DownloadStream(FileData fileData)
|
|
||||||
{
|
|
||||||
var stream = new MemoryStream(100 * 1024 * 1024);
|
|
||||||
Client.DownloadFile(InternalPath + fileData.Name, stream);
|
|
||||||
|
|
||||||
return Task.FromResult<Stream>(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task Delete(FileData fileData)
|
|
||||||
{
|
|
||||||
Client.Delete(InternalPath + fileData.Name);
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task Move(FileData fileData, string newPath)
|
|
||||||
{
|
|
||||||
Client.RenameFile(InternalPath + fileData.Name, InternalPath + newPath);
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task Compress(params FileData[] files)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task Decompress(FileData fileData)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task<string> GetLaunchUrl()
|
|
||||||
{
|
|
||||||
return Task.FromResult($"sftp://{SftpUser}@{SftpHost}:{SftpPort}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object Clone()
|
|
||||||
{
|
|
||||||
return new SftpFileAccess(SftpHost, SftpUser, SftpPassword, SftpPort, ForceUserDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +1,15 @@
|
|||||||
using Logging.Net;
|
namespace Moonlight.App.Helpers;
|
||||||
|
|
||||||
namespace Moonlight.App.Helpers;
|
|
||||||
|
|
||||||
public static class ParseHelper
|
public static class ParseHelper
|
||||||
{
|
{
|
||||||
public static int MinecraftToInt(string raw)
|
public static int MinecraftToInt(string raw)
|
||||||
{
|
{
|
||||||
var versionWithoutPre = raw.Split("_")[0];
|
var versionWithoutPre = raw.Split("-")[0];
|
||||||
versionWithoutPre = versionWithoutPre.Split("-")[0];
|
|
||||||
|
|
||||||
// Fuck you 1.7.10 ;)
|
|
||||||
versionWithoutPre = versionWithoutPre.Replace("1.7.10", "1.7");
|
|
||||||
|
|
||||||
if (versionWithoutPre.Count(x => x == "."[0]) == 1)
|
if (versionWithoutPre.Count(x => x == "."[0]) == 1)
|
||||||
versionWithoutPre += ".0";
|
versionWithoutPre += ".0";
|
||||||
|
|
||||||
var x = versionWithoutPre.Replace(".", "");
|
return int.Parse(versionWithoutPre.Replace(".", ""));
|
||||||
|
|
||||||
return int.Parse(x);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string FirstPartStartingWithNumber(string raw)
|
public static string FirstPartStartingWithNumber(string raw)
|
||||||
@@ -37,61 +29,4 @@ public static class ParseHelper
|
|||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetHighestVersion(string[] versions)
|
|
||||||
{
|
|
||||||
// Initialize the highest version to the first version in the array
|
|
||||||
string highestVersion = versions[0];
|
|
||||||
|
|
||||||
// Loop through the remaining versions in the array
|
|
||||||
for (int i = 1; i < versions.Length; i++)
|
|
||||||
{
|
|
||||||
// Compare the current version to the highest version
|
|
||||||
if (CompareVersions(versions[i], highestVersion) > 0)
|
|
||||||
{
|
|
||||||
// If the current version is higher, update the highest version
|
|
||||||
highestVersion = versions[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return highestVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int CompareVersions(string version1, string version2)
|
|
||||||
{
|
|
||||||
// Split the versions into their component parts
|
|
||||||
string[] version1Parts = version1.Split('.');
|
|
||||||
string[] version2Parts = version2.Split('.');
|
|
||||||
|
|
||||||
// Compare each component part in turn
|
|
||||||
for (int i = 0; i < version1Parts.Length && i < version2Parts.Length; i++)
|
|
||||||
{
|
|
||||||
int part1 = int.Parse(version1Parts[i]);
|
|
||||||
int part2 = int.Parse(version2Parts[i]);
|
|
||||||
|
|
||||||
if (part1 < part2)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else if (part1 > part2)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we get here, the versions are equal up to the length of the shorter one.
|
|
||||||
// If one version has more parts than the other, the longer one is considered higher.
|
|
||||||
if (version1Parts.Length < version2Parts.Length)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else if (version1Parts.Length > version2Parts.Length)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
220
Moonlight/App/Helpers/PleskApiHelper.cs
Normal file
220
Moonlight/App/Helpers/PleskApiHelper.cs
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Moonlight.App.Database.Entities;
|
||||||
|
using Moonlight.App.Exceptions;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using RestSharp;
|
||||||
|
|
||||||
|
namespace Moonlight.App.Helpers;
|
||||||
|
|
||||||
|
public class PleskApiHelper
|
||||||
|
{
|
||||||
|
private readonly RestClient Client;
|
||||||
|
|
||||||
|
public PleskApiHelper()
|
||||||
|
{
|
||||||
|
Client = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<T> Get<T>(PleskServer server, string resource)
|
||||||
|
{
|
||||||
|
var request = CreateRequest(server, resource);
|
||||||
|
|
||||||
|
request.Method = Method.Get;
|
||||||
|
|
||||||
|
var response = await Client.ExecuteAsync(request);
|
||||||
|
|
||||||
|
if (!response.IsSuccessful)
|
||||||
|
{
|
||||||
|
if (response.StatusCode != 0)
|
||||||
|
{
|
||||||
|
throw new PleskException(
|
||||||
|
$"An error occured: ({response.StatusCode}) {response.Content}",
|
||||||
|
(int)response.StatusCode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception($"An internal error occured: {response.ErrorMessage}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonConvert.DeserializeObject<T>(response.Content!)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> GetRaw(PleskServer server, string resource)
|
||||||
|
{
|
||||||
|
var request = CreateRequest(server, resource);
|
||||||
|
|
||||||
|
request.Method = Method.Get;
|
||||||
|
|
||||||
|
var response = await Client.ExecuteAsync(request);
|
||||||
|
|
||||||
|
if (!response.IsSuccessful)
|
||||||
|
{
|
||||||
|
if (response.StatusCode != 0)
|
||||||
|
{
|
||||||
|
throw new PleskException(
|
||||||
|
$"An error occured: ({response.StatusCode}) {response.Content}",
|
||||||
|
(int)response.StatusCode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception($"An internal error occured: {response.ErrorMessage}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Content!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<T> Post<T>(PleskServer server, string resource, object? body)
|
||||||
|
{
|
||||||
|
var request = CreateRequest(server, resource);
|
||||||
|
|
||||||
|
request.Method = Method.Post;
|
||||||
|
|
||||||
|
request.AddParameter("text/plain",
|
||||||
|
JsonConvert.SerializeObject(body),
|
||||||
|
ParameterType.RequestBody
|
||||||
|
);
|
||||||
|
|
||||||
|
var response = await Client.ExecuteAsync(request);
|
||||||
|
|
||||||
|
if (!response.IsSuccessful)
|
||||||
|
{
|
||||||
|
if (response.StatusCode != 0)
|
||||||
|
{
|
||||||
|
throw new PleskException(
|
||||||
|
$"An error occured: ({response.StatusCode}) {response.Content}",
|
||||||
|
(int)response.StatusCode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception($"An internal error occured: {response.ErrorMessage}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return JsonConvert.DeserializeObject<T>(response.Content!)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Post(PleskServer server, string resource, object? body)
|
||||||
|
{
|
||||||
|
var request = CreateRequest(server, resource);
|
||||||
|
|
||||||
|
request.Method = Method.Post;
|
||||||
|
|
||||||
|
if(body != null)
|
||||||
|
request.AddParameter("text/plain", JsonConvert.SerializeObject(body), ParameterType.RequestBody);
|
||||||
|
|
||||||
|
var response = await Client.ExecuteAsync(request);
|
||||||
|
|
||||||
|
if (!response.IsSuccessful)
|
||||||
|
{
|
||||||
|
if (response.StatusCode != 0)
|
||||||
|
{
|
||||||
|
throw new PleskException(
|
||||||
|
$"An error occured: ({response.StatusCode}) {response.Content}",
|
||||||
|
(int)response.StatusCode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception($"An internal error occured: {response.ErrorMessage}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task PostRaw(PleskServer server, string resource, object body)
|
||||||
|
{
|
||||||
|
var request = CreateRequest(server, resource);
|
||||||
|
|
||||||
|
request.Method = Method.Post;
|
||||||
|
|
||||||
|
request.AddParameter("text/plain", body, ParameterType.RequestBody);
|
||||||
|
|
||||||
|
var response = await Client.ExecuteAsync(request);
|
||||||
|
|
||||||
|
if (!response.IsSuccessful)
|
||||||
|
{
|
||||||
|
if (response.StatusCode != 0)
|
||||||
|
{
|
||||||
|
throw new PleskException(
|
||||||
|
$"An error occured: ({response.StatusCode}) {response.Content}",
|
||||||
|
(int)response.StatusCode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception($"An internal error occured: {response.ErrorMessage}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Delete(PleskServer server, string resource, object? body)
|
||||||
|
{
|
||||||
|
var request = CreateRequest(server, resource);
|
||||||
|
|
||||||
|
request.Method = Method.Delete;
|
||||||
|
|
||||||
|
if(body != null)
|
||||||
|
request.AddParameter("text/plain", JsonConvert.SerializeObject(body), ParameterType.RequestBody);
|
||||||
|
|
||||||
|
var response = await Client.ExecuteAsync(request);
|
||||||
|
|
||||||
|
if (!response.IsSuccessful)
|
||||||
|
{
|
||||||
|
if (response.StatusCode != 0)
|
||||||
|
{
|
||||||
|
throw new PleskException(
|
||||||
|
$"An error occured: ({response.StatusCode}) {response.Content}",
|
||||||
|
(int)response.StatusCode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception($"An internal error occured: {response.ErrorMessage}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Put(PleskServer server, string resource, object? body)
|
||||||
|
{
|
||||||
|
var request = CreateRequest(server, resource);
|
||||||
|
|
||||||
|
request.Method = Method.Put;
|
||||||
|
|
||||||
|
request.AddParameter("text/plain", JsonConvert.SerializeObject(body), ParameterType.RequestBody);
|
||||||
|
|
||||||
|
var response = await Client.ExecuteAsync(request);
|
||||||
|
|
||||||
|
if (!response.IsSuccessful)
|
||||||
|
{
|
||||||
|
if (response.StatusCode != 0)
|
||||||
|
{
|
||||||
|
throw new PleskException(
|
||||||
|
$"An error occured: ({response.StatusCode}) {response.Content}",
|
||||||
|
(int)response.StatusCode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception($"An internal error occured: {response.ErrorMessage}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private RestRequest CreateRequest(PleskServer pleskServer, string resource)
|
||||||
|
{
|
||||||
|
var url = $"{pleskServer.ApiUrl}/" + resource;
|
||||||
|
|
||||||
|
var request = new RestRequest(url);
|
||||||
|
var ba = Convert.ToBase64String(Encoding.UTF8.GetBytes(pleskServer.ApiKey));
|
||||||
|
|
||||||
|
request.AddHeader("Content-Type", "application/json");
|
||||||
|
request.AddHeader("Accept", "application/json");
|
||||||
|
request.AddHeader("Authorization", "Basic " + ba);
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
namespace Moonlight.App.Helpers;
|
|
||||||
|
|
||||||
public class SyncStreamAdapter : Stream
|
|
||||||
{
|
|
||||||
private readonly Stream _stream;
|
|
||||||
|
|
||||||
public SyncStreamAdapter(Stream stream)
|
|
||||||
{
|
|
||||||
_stream = stream ?? throw new ArgumentNullException(nameof(stream));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CanRead => _stream.CanRead;
|
|
||||||
public override bool CanSeek => _stream.CanSeek;
|
|
||||||
public override bool CanWrite => _stream.CanWrite;
|
|
||||||
public override long Length => _stream.Length;
|
|
||||||
|
|
||||||
public override long Position
|
|
||||||
{
|
|
||||||
get => _stream.Position;
|
|
||||||
set => _stream.Position = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Flush()
|
|
||||||
{
|
|
||||||
_stream.Flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int Read(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
var task = Task.Run(() => _stream.ReadAsync(buffer, offset, count));
|
|
||||||
return task.GetAwaiter().GetResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override long Seek(long offset, SeekOrigin origin)
|
|
||||||
{
|
|
||||||
return _stream.Seek(offset, origin);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetLength(long value)
|
|
||||||
{
|
|
||||||
_stream.SetLength(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
var task = Task.Run(() => _stream.WriteAsync(buffer, offset, count));
|
|
||||||
task.GetAwaiter().GetResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
_stream?.Dispose();
|
|
||||||
}
|
|
||||||
base.Dispose(disposing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -18,19 +18,17 @@ public class OAuth2Controller : Controller
|
|||||||
private readonly DiscordOAuth2Service DiscordOAuth2Service;
|
private readonly DiscordOAuth2Service DiscordOAuth2Service;
|
||||||
private readonly UserRepository UserRepository;
|
private readonly UserRepository UserRepository;
|
||||||
private readonly UserService UserService;
|
private readonly UserService UserService;
|
||||||
private readonly DateTimeService DateTimeService;
|
|
||||||
|
|
||||||
public OAuth2Controller(
|
public OAuth2Controller(
|
||||||
GoogleOAuth2Service googleOAuth2Service,
|
GoogleOAuth2Service googleOAuth2Service,
|
||||||
UserRepository userRepository,
|
UserRepository userRepository,
|
||||||
UserService userService,
|
UserService userService,
|
||||||
DiscordOAuth2Service discordOAuth2Service, DateTimeService dateTimeService)
|
DiscordOAuth2Service discordOAuth2Service)
|
||||||
{
|
{
|
||||||
GoogleOAuth2Service = googleOAuth2Service;
|
GoogleOAuth2Service = googleOAuth2Service;
|
||||||
UserRepository = userRepository;
|
UserRepository = userRepository;
|
||||||
UserService = userService;
|
UserService = userService;
|
||||||
DiscordOAuth2Service = discordOAuth2Service;
|
DiscordOAuth2Service = discordOAuth2Service;
|
||||||
DateTimeService = dateTimeService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("google")]
|
[HttpGet("google")]
|
||||||
@@ -65,7 +63,7 @@ public class OAuth2Controller : Controller
|
|||||||
|
|
||||||
Response.Cookies.Append("token", token, new ()
|
Response.Cookies.Append("token", token, new ()
|
||||||
{
|
{
|
||||||
Expires = new DateTimeOffset(DateTimeService.GetCurrent().AddDays(10))
|
Expires = new DateTimeOffset(DateTime.UtcNow.AddDays(10))
|
||||||
});
|
});
|
||||||
|
|
||||||
return Redirect("/");
|
return Redirect("/");
|
||||||
@@ -123,7 +121,7 @@ public class OAuth2Controller : Controller
|
|||||||
|
|
||||||
Response.Cookies.Append("token", token, new ()
|
Response.Cookies.Append("token", token, new ()
|
||||||
{
|
{
|
||||||
Expires = new DateTimeOffset(DateTimeService.GetCurrent().AddDays(10))
|
Expires = new DateTimeOffset(DateTime.UtcNow.AddDays(10))
|
||||||
});
|
});
|
||||||
|
|
||||||
return Redirect("/");
|
return Redirect("/");
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Moonlight.App.Events;
|
|
||||||
using Moonlight.App.Http.Requests.Wings;
|
using Moonlight.App.Http.Requests.Wings;
|
||||||
using Moonlight.App.Repositories;
|
using Moonlight.App.Repositories;
|
||||||
using Moonlight.App.Repositories.Servers;
|
using Moonlight.App.Repositories.Servers;
|
||||||
@@ -12,17 +11,17 @@ namespace Moonlight.App.Http.Controllers.Api.Remote;
|
|||||||
public class BackupController : Controller
|
public class BackupController : Controller
|
||||||
{
|
{
|
||||||
private readonly ServerBackupRepository ServerBackupRepository;
|
private readonly ServerBackupRepository ServerBackupRepository;
|
||||||
private readonly EventSystem Event;
|
private readonly MessageService MessageService;
|
||||||
private readonly NodeRepository NodeRepository;
|
private readonly NodeRepository NodeRepository;
|
||||||
|
|
||||||
public BackupController(
|
public BackupController(
|
||||||
ServerBackupRepository serverBackupRepository,
|
ServerBackupRepository serverBackupRepository,
|
||||||
NodeRepository nodeRepository,
|
NodeRepository nodeRepository,
|
||||||
EventSystem eventSystem)
|
MessageService messageService)
|
||||||
{
|
{
|
||||||
ServerBackupRepository = serverBackupRepository;
|
ServerBackupRepository = serverBackupRepository;
|
||||||
NodeRepository = nodeRepository;
|
NodeRepository = nodeRepository;
|
||||||
Event = eventSystem;
|
MessageService = messageService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{uuid}")]
|
[HttpGet("{uuid}")]
|
||||||
@@ -58,11 +57,11 @@ public class BackupController : Controller
|
|||||||
|
|
||||||
ServerBackupRepository.Update(backup);
|
ServerBackupRepository.Update(backup);
|
||||||
|
|
||||||
await Event.Emit($"wings.backups.create", backup);
|
await MessageService.Emit($"wings.backups.create", backup);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await Event.Emit($"wings.backups.createFailed", backup);
|
await MessageService.Emit($"wings.backups.createfailed", backup);
|
||||||
ServerBackupRepository.Delete(backup);
|
ServerBackupRepository.Delete(backup);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +88,7 @@ public class BackupController : Controller
|
|||||||
if (backup == null)
|
if (backup == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
|
||||||
await Event.Emit($"wings.backups.restore", backup);
|
await MessageService.Emit($"wings.backups.restore", backup);
|
||||||
|
|
||||||
return NoContent();
|
return NoContent();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Logging.Net;
|
using Logging.Net;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Moonlight.App.Database.Entities;
|
using Moonlight.App.Database.Entities;
|
||||||
using Moonlight.App.Events;
|
|
||||||
using Moonlight.App.Http.Requests.Daemon;
|
using Moonlight.App.Http.Requests.Daemon;
|
||||||
using Moonlight.App.Repositories;
|
using Moonlight.App.Repositories;
|
||||||
using Moonlight.App.Services;
|
using Moonlight.App.Services;
|
||||||
@@ -13,13 +12,13 @@ namespace Moonlight.App.Http.Controllers.Api.Remote;
|
|||||||
public class DdosController : Controller
|
public class DdosController : Controller
|
||||||
{
|
{
|
||||||
private readonly NodeRepository NodeRepository;
|
private readonly NodeRepository NodeRepository;
|
||||||
private readonly EventSystem Event;
|
private readonly MessageService MessageService;
|
||||||
private readonly DdosAttackRepository DdosAttackRepository;
|
private readonly DdosAttackRepository DdosAttackRepository;
|
||||||
|
|
||||||
public DdosController(NodeRepository nodeRepository, EventSystem eventSystem, DdosAttackRepository ddosAttackRepository)
|
public DdosController(NodeRepository nodeRepository, MessageService messageService, DdosAttackRepository ddosAttackRepository)
|
||||||
{
|
{
|
||||||
NodeRepository = nodeRepository;
|
NodeRepository = nodeRepository;
|
||||||
Event = eventSystem;
|
MessageService = messageService;
|
||||||
DdosAttackRepository = ddosAttackRepository;
|
DdosAttackRepository = ddosAttackRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +47,7 @@ public class DdosController : Controller
|
|||||||
|
|
||||||
ddosAttack = DdosAttackRepository.Add(ddosAttack);
|
ddosAttack = DdosAttackRepository.Add(ddosAttack);
|
||||||
|
|
||||||
await Event.Emit("node.ddos", ddosAttack);
|
await MessageService.Emit("node.ddos", ddosAttack);
|
||||||
|
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Moonlight.App.Events;
|
|
||||||
using Moonlight.App.Helpers;
|
using Moonlight.App.Helpers;
|
||||||
using Moonlight.App.Http.Resources.Wings;
|
using Moonlight.App.Http.Resources.Wings;
|
||||||
using Moonlight.App.Repositories;
|
using Moonlight.App.Repositories;
|
||||||
@@ -16,18 +15,18 @@ public class ServersController : Controller
|
|||||||
private readonly WingsServerConverter Converter;
|
private readonly WingsServerConverter Converter;
|
||||||
private readonly ServerRepository ServerRepository;
|
private readonly ServerRepository ServerRepository;
|
||||||
private readonly NodeRepository NodeRepository;
|
private readonly NodeRepository NodeRepository;
|
||||||
private readonly EventSystem Event;
|
private readonly MessageService MessageService;
|
||||||
|
|
||||||
public ServersController(
|
public ServersController(
|
||||||
WingsServerConverter converter,
|
WingsServerConverter converter,
|
||||||
ServerRepository serverRepository,
|
ServerRepository serverRepository,
|
||||||
NodeRepository nodeRepository,
|
NodeRepository nodeRepository,
|
||||||
EventSystem eventSystem)
|
MessageService messageService)
|
||||||
{
|
{
|
||||||
Converter = converter;
|
Converter = converter;
|
||||||
ServerRepository = serverRepository;
|
ServerRepository = serverRepository;
|
||||||
NodeRepository = nodeRepository;
|
NodeRepository = nodeRepository;
|
||||||
Event = eventSystem;
|
MessageService = messageService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
@@ -69,7 +68,7 @@ public class ServersController : Controller
|
|||||||
totalPages = slice.Length - 1;
|
totalPages = slice.Length - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
await Event.Emit($"wings.{node.Id}.serverList", node);
|
await MessageService.Emit($"wings.{node.Id}.serverlist", node);
|
||||||
|
|
||||||
//Logger.Debug($"[BRIDGE] Node '{node.Name}' is requesting server list page {page} with {perPage} items per page");
|
//Logger.Debug($"[BRIDGE] Node '{node.Name}' is requesting server list page {page} with {perPage} items per page");
|
||||||
|
|
||||||
@@ -98,7 +97,7 @@ public class ServersController : Controller
|
|||||||
if (token != node.Token)
|
if (token != node.Token)
|
||||||
return Unauthorized();
|
return Unauthorized();
|
||||||
|
|
||||||
await Event.Emit($"wings.{node.Id}.stateReset", node);
|
await MessageService.Emit($"wings.{node.Id}.statereset", node);
|
||||||
|
|
||||||
foreach (var server in ServerRepository
|
foreach (var server in ServerRepository
|
||||||
.Get()
|
.Get()
|
||||||
@@ -137,7 +136,7 @@ public class ServersController : Controller
|
|||||||
if (server == null)
|
if (server == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
|
||||||
await Event.Emit($"wings.{node.Id}.serverFetch", server);
|
await MessageService.Emit($"wings.{node.Id}.serverfetch", server);
|
||||||
|
|
||||||
try //TODO: Remove
|
try //TODO: Remove
|
||||||
{
|
{
|
||||||
@@ -170,7 +169,7 @@ public class ServersController : Controller
|
|||||||
if (server == null)
|
if (server == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
|
||||||
await Event.Emit($"wings.{node.Id}.serverInstallFetch", server);
|
await MessageService.Emit($"wings.{node.Id}.serverinstallfetch", server);
|
||||||
|
|
||||||
return new WingsServerInstall()
|
return new WingsServerInstall()
|
||||||
{
|
{
|
||||||
@@ -203,8 +202,8 @@ public class ServersController : Controller
|
|||||||
server.Installing = false;
|
server.Installing = false;
|
||||||
ServerRepository.Update(server);
|
ServerRepository.Update(server);
|
||||||
|
|
||||||
await Event.Emit($"wings.{node.Id}.serverInstallComplete", server);
|
await MessageService.Emit($"wings.{node.Id}.serverinstallcomplete", server);
|
||||||
await Event.Emit($"server.{server.Uuid}.installComplete", server);
|
await MessageService.Emit($"server.{server.Uuid}.installcomplete", server);
|
||||||
|
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|||||||
83
Moonlight/App/MessageSystem/MessageSender.cs
Normal file
83
Moonlight/App/MessageSystem/MessageSender.cs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using Logging.Net;
|
||||||
|
|
||||||
|
namespace Moonlight.App.MessageSystem;
|
||||||
|
|
||||||
|
public class MessageSender
|
||||||
|
{
|
||||||
|
private readonly List<MessageSubscriber> Subscribers;
|
||||||
|
|
||||||
|
public bool Debug { get; set; }
|
||||||
|
public TimeSpan TookToLongTime { get; set; } = TimeSpan.FromSeconds(1);
|
||||||
|
|
||||||
|
public MessageSender()
|
||||||
|
{
|
||||||
|
Subscribers = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Subscribe<T, K>(string name, object bind, Func<K, Task> method)
|
||||||
|
{
|
||||||
|
lock (Subscribers)
|
||||||
|
{
|
||||||
|
Subscribers.Add(new ()
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
Action = method,
|
||||||
|
Type = typeof(T),
|
||||||
|
Bind = bind
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Debug)
|
||||||
|
Logger.Debug($"{bind} subscribed to '{name}'");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unsubscribe(string name, object bind)
|
||||||
|
{
|
||||||
|
lock (Subscribers)
|
||||||
|
{
|
||||||
|
Subscribers.RemoveAll(x => x.Bind == bind);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Debug)
|
||||||
|
Logger.Debug($"{bind} unsubscribed from '{name}'");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task Emit(string name, object? value, bool disableWarning = false)
|
||||||
|
{
|
||||||
|
lock (Subscribers)
|
||||||
|
{
|
||||||
|
foreach (var subscriber in Subscribers)
|
||||||
|
{
|
||||||
|
if (subscriber.Name == name)
|
||||||
|
{
|
||||||
|
var stopWatch = new Stopwatch();
|
||||||
|
stopWatch.Start();
|
||||||
|
|
||||||
|
var del = (Delegate)subscriber.Action;
|
||||||
|
|
||||||
|
((Task)del.DynamicInvoke(value)!).Wait();
|
||||||
|
|
||||||
|
stopWatch.Stop();
|
||||||
|
|
||||||
|
if (!disableWarning)
|
||||||
|
{
|
||||||
|
if (stopWatch.Elapsed.TotalMilliseconds > TookToLongTime.TotalMilliseconds)
|
||||||
|
{
|
||||||
|
Logger.Warn(
|
||||||
|
$"Subscriber {subscriber.Type.Name} for event '{name}' took long to process. {stopWatch.Elapsed.TotalMilliseconds}ms");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Debug)
|
||||||
|
{
|
||||||
|
Logger.Debug(
|
||||||
|
$"Subscriber {subscriber.Type.Name} for event '{name}' took {stopWatch.Elapsed.TotalMilliseconds}ms");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Moonlight/App/MessageSystem/MessageSubscriber.cs
Normal file
9
Moonlight/App/MessageSystem/MessageSubscriber.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Moonlight.App.MessageSystem;
|
||||||
|
|
||||||
|
public class MessageSubscriber
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public object Action { get; set; }
|
||||||
|
public Type Type { get; set; }
|
||||||
|
public object Bind { get; set; }
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Models.Forms;
|
|
||||||
|
|
||||||
public class CloudPanelDataModel
|
|
||||||
{
|
|
||||||
[Required(ErrorMessage = "You have to enter a name")]
|
|
||||||
[MaxLength(32, ErrorMessage = "The name should not be longer than 32 characters")]
|
|
||||||
public string Name { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "You need to specify the host")]
|
|
||||||
public string Host { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "You need to enter an api url")]
|
|
||||||
public string ApiUrl { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "You need to enter an api key")]
|
|
||||||
public string ApiKey { get; set; }
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,7 @@ public class DomainOrderDataModel
|
|||||||
{
|
{
|
||||||
[Required(ErrorMessage = "You need to specify a name")]
|
[Required(ErrorMessage = "You need to specify a name")]
|
||||||
[MaxLength(32, ErrorMessage = "The max lenght for the name is 32 characters")]
|
[MaxLength(32, ErrorMessage = "The max lenght for the name is 32 characters")]
|
||||||
[RegularExpression(@"^[a-z0-9]+$", ErrorMessage = "The name should only consist of lower case characters or numbers")]
|
[RegularExpression(@"^[a-z]+$", ErrorMessage = "The name should only consist of lower case characters")]
|
||||||
public string Name { get; set; } = "";
|
public string Name { get; set; } = "";
|
||||||
|
|
||||||
[Required(ErrorMessage = "You need to specify a shared domain")]
|
[Required(ErrorMessage = "You need to specify a shared domain")]
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using Moonlight.App.Database.Entities;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Models.Forms;
|
|
||||||
|
|
||||||
public class ServerEditDataModel
|
|
||||||
{
|
|
||||||
[Required(ErrorMessage = "You need to enter a name")]
|
|
||||||
[MaxLength(32, ErrorMessage = "The name cannot be longer that 32 characters")]
|
|
||||||
public string Name { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "You need to specify a user")]
|
|
||||||
public User Owner { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "You need to specify the cpu cores")]
|
|
||||||
public int Cpu { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "You need to specify the memory")]
|
|
||||||
public long Memory { get; set; }
|
|
||||||
|
|
||||||
[Required(ErrorMessage = "You need to specify the disk")]
|
|
||||||
public long Disk { get; set; }
|
|
||||||
|
|
||||||
public string OverrideStartup { get; set; }
|
|
||||||
|
|
||||||
public int DockerImageIndex { get; set; }
|
|
||||||
|
|
||||||
public bool IsCleanupException { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Moonlight.App.Database;
|
|
||||||
using Moonlight.App.Database.Entities;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Repositories;
|
|
||||||
|
|
||||||
public class NodeAllocationRepository : IDisposable
|
|
||||||
{
|
|
||||||
// This repository is ONLY for the server creation service, so allocations can be found
|
|
||||||
// using raw sql. DO NOT use this in any other component
|
|
||||||
|
|
||||||
private readonly DataContext DataContext;
|
|
||||||
|
|
||||||
public NodeAllocationRepository(DataContext dataContext)
|
|
||||||
{
|
|
||||||
DataContext = dataContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DbSet<NodeAllocation> Get()
|
|
||||||
{
|
|
||||||
return DataContext.NodeAllocations;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
DataContext.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
44
Moonlight/App/Repositories/PleskServerRepository.cs
Normal file
44
Moonlight/App/Repositories/PleskServerRepository.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 PleskServerRepository : IDisposable
|
||||||
|
{
|
||||||
|
private readonly DataContext DataContext;
|
||||||
|
|
||||||
|
public PleskServerRepository(DataContext dataContext)
|
||||||
|
{
|
||||||
|
DataContext = dataContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DbSet<PleskServer> Get()
|
||||||
|
{
|
||||||
|
return DataContext.PleskServers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PleskServer Add(PleskServer pleskServer)
|
||||||
|
{
|
||||||
|
var x = DataContext.PleskServers.Add(pleskServer);
|
||||||
|
DataContext.SaveChanges();
|
||||||
|
return x.Entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(PleskServer pleskServer)
|
||||||
|
{
|
||||||
|
DataContext.PleskServers.Update(pleskServer);
|
||||||
|
DataContext.SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Delete(PleskServer pleskServer)
|
||||||
|
{
|
||||||
|
DataContext.PleskServers.Remove(pleskServer);
|
||||||
|
DataContext.SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
DataContext.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Moonlight.App.Database;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Repositories;
|
|
||||||
|
|
||||||
public class Repository<TEntity> where TEntity : class
|
|
||||||
{
|
|
||||||
private readonly DataContext DataContext;
|
|
||||||
private readonly DbSet<TEntity> DbSet;
|
|
||||||
|
|
||||||
public Repository(DataContext dbContext)
|
|
||||||
{
|
|
||||||
DataContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
|
|
||||||
DbSet = DataContext.Set<TEntity>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DbSet<TEntity> Get()
|
|
||||||
{
|
|
||||||
return DbSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TEntity Add(TEntity entity)
|
|
||||||
{
|
|
||||||
var x = DbSet.Add(entity);
|
|
||||||
DataContext.SaveChanges();
|
|
||||||
return x.Entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update(TEntity entity)
|
|
||||||
{
|
|
||||||
DbSet.Update(entity);
|
|
||||||
DataContext.SaveChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Delete(TEntity entity)
|
|
||||||
{
|
|
||||||
DbSet.Remove(entity);
|
|
||||||
DataContext.SaveChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +1,16 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Moonlight.App.Database;
|
using Moonlight.App.Database;
|
||||||
using Moonlight.App.Database.Entities;
|
using Moonlight.App.Database.Entities;
|
||||||
using Moonlight.App.Services;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Repositories;
|
namespace Moonlight.App.Repositories;
|
||||||
|
|
||||||
public class StatisticsRepository : IDisposable
|
public class StatisticsRepository : IDisposable
|
||||||
{
|
{
|
||||||
private readonly DataContext DataContext;
|
private readonly DataContext DataContext;
|
||||||
private readonly DateTimeService DateTimeService;
|
|
||||||
|
|
||||||
public StatisticsRepository(DataContext dataContext, DateTimeService dateTimeService)
|
public StatisticsRepository(DataContext dataContext)
|
||||||
{
|
{
|
||||||
DataContext = dataContext;
|
DataContext = dataContext;
|
||||||
DateTimeService = dateTimeService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DbSet<StatisticsData> Get()
|
public DbSet<StatisticsData> Get()
|
||||||
@@ -30,7 +27,7 @@ public class StatisticsRepository : IDisposable
|
|||||||
|
|
||||||
public StatisticsData Add(string chart, double value)
|
public StatisticsData Add(string chart, double value)
|
||||||
{
|
{
|
||||||
return Add(new StatisticsData() {Chart = chart, Value = value, Date = DateTimeService.GetCurrent()});
|
return Add(new StatisticsData() {Chart = chart, Value = value, Date = DateTime.Now});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
44
Moonlight/App/Repositories/WebsiteRepository.cs
Normal file
44
Moonlight/App/Repositories/WebsiteRepository.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 WebsiteRepository : IDisposable
|
||||||
|
{
|
||||||
|
private readonly DataContext DataContext;
|
||||||
|
|
||||||
|
public WebsiteRepository(DataContext dataContext)
|
||||||
|
{
|
||||||
|
DataContext = dataContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DbSet<Website> Get()
|
||||||
|
{
|
||||||
|
return DataContext.Websites;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Website Add(Website website)
|
||||||
|
{
|
||||||
|
var x = DataContext.Websites.Add(website);
|
||||||
|
DataContext.SaveChanges();
|
||||||
|
return x.Entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(Website website)
|
||||||
|
{
|
||||||
|
DataContext.Websites.Update(website);
|
||||||
|
DataContext.SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Delete(Website website)
|
||||||
|
{
|
||||||
|
DataContext.Websites.Remove(website);
|
||||||
|
DataContext.SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
DataContext.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,6 @@ using Moonlight.App.Models.Wings;
|
|||||||
using Moonlight.App.Repositories;
|
using Moonlight.App.Repositories;
|
||||||
using Moonlight.App.Repositories.Servers;
|
using Moonlight.App.Repositories.Servers;
|
||||||
using Logging.Net;
|
using Logging.Net;
|
||||||
using Moonlight.App.Events;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Moonlight.App.Services;
|
namespace Moonlight.App.Services;
|
||||||
@@ -24,24 +23,21 @@ public class CleanupService
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private readonly ConfigService ConfigService;
|
private readonly ConfigService ConfigService;
|
||||||
private readonly DateTimeService DateTimeService;
|
private readonly MessageService MessageService;
|
||||||
private readonly EventSystem Event;
|
|
||||||
private readonly IServiceScopeFactory ServiceScopeFactory;
|
private readonly IServiceScopeFactory ServiceScopeFactory;
|
||||||
private readonly PeriodicTimer Timer;
|
private readonly PeriodicTimer Timer;
|
||||||
|
|
||||||
public CleanupService(
|
public CleanupService(
|
||||||
ConfigService configService,
|
ConfigService configService,
|
||||||
IServiceScopeFactory serviceScopeFactory,
|
IServiceScopeFactory serviceScopeFactory,
|
||||||
DateTimeService dateTimeService,
|
MessageService messageService)
|
||||||
EventSystem eventSystem)
|
|
||||||
{
|
{
|
||||||
ServiceScopeFactory = serviceScopeFactory;
|
ServiceScopeFactory = serviceScopeFactory;
|
||||||
DateTimeService = dateTimeService;
|
MessageService = messageService;
|
||||||
ConfigService = configService;
|
ConfigService = configService;
|
||||||
Event = eventSystem;
|
|
||||||
|
|
||||||
StartedAt = DateTimeService.GetCurrent();
|
StartedAt = DateTime.Now;
|
||||||
CompletedAt = DateTimeService.GetCurrent();
|
CompletedAt = DateTime.Now;
|
||||||
IsRunning = false;
|
IsRunning = false;
|
||||||
|
|
||||||
var config = ConfigService.GetSection("Moonlight").GetSection("Cleanup");
|
var config = ConfigService.GetSection("Moonlight").GetSection("Cleanup");
|
||||||
@@ -149,7 +145,7 @@ public class CleanupService
|
|||||||
ServersRunning++;
|
ServersRunning++;
|
||||||
}
|
}
|
||||||
|
|
||||||
await Event.Emit("cleanup.updated");
|
await MessageService.Emit("cleanup.updated", null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -179,7 +175,7 @@ public class CleanupService
|
|||||||
ServersRunning++;
|
ServersRunning++;
|
||||||
}
|
}
|
||||||
|
|
||||||
await Event.Emit("cleanup.updated");
|
await MessageService.Emit("cleanup.updated", null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,7 +196,7 @@ public class CleanupService
|
|||||||
|
|
||||||
IsRunning = false;
|
IsRunning = false;
|
||||||
CleanupsPerformed++;
|
CleanupsPerformed++;
|
||||||
await Event.Emit("cleanup.updated");
|
await MessageService.Emit("cleanup.updated", null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ public class ConfigService : IConfiguration
|
|||||||
private IConfiguration Configuration;
|
private IConfiguration Configuration;
|
||||||
|
|
||||||
public bool DebugMode { get; private set; } = false;
|
public bool DebugMode { get; private set; } = false;
|
||||||
public bool SqlDebugMode { get; private set; } = false;
|
|
||||||
|
|
||||||
public ConfigService(StorageService storageService)
|
public ConfigService(StorageService storageService)
|
||||||
{
|
{
|
||||||
@@ -29,14 +28,6 @@ public class ConfigService : IConfiguration
|
|||||||
|
|
||||||
if (DebugMode)
|
if (DebugMode)
|
||||||
Logger.Debug("Debug mode enabled");
|
Logger.Debug("Debug mode enabled");
|
||||||
|
|
||||||
var sqlDebugVar = Environment.GetEnvironmentVariable("ML_SQL_DEBUG");
|
|
||||||
|
|
||||||
if (sqlDebugVar != null)
|
|
||||||
SqlDebugMode = bool.Parse(sqlDebugVar);
|
|
||||||
|
|
||||||
if (SqlDebugMode)
|
|
||||||
Logger.Debug("Sql debug mode enabled");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reload()
|
public void Reload()
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
using Moonlight.App.Helpers;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Services;
|
|
||||||
|
|
||||||
public class DateTimeService
|
|
||||||
{
|
|
||||||
public long GetCurrentUnix()
|
|
||||||
{
|
|
||||||
return new DateTimeOffset(GetCurrent()).ToUnixTimeMilliseconds();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long GetCurrentUnixSeconds()
|
|
||||||
{
|
|
||||||
return new DateTimeOffset(GetCurrent()).ToUnixTimeSeconds();
|
|
||||||
}
|
|
||||||
|
|
||||||
public DateTime GetCurrent()
|
|
||||||
{
|
|
||||||
return DateTime.UtcNow;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetDate()
|
|
||||||
{
|
|
||||||
return Formatter.FormatDateOnly(GetCurrent());
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetDateTime()
|
|
||||||
{
|
|
||||||
return Formatter.FormatDate(GetCurrent());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
using Discord;
|
|
||||||
using Discord.Webhook;
|
|
||||||
using Logging.Net;
|
|
||||||
using Moonlight.App.Database.Entities;
|
|
||||||
using Moonlight.App.Events;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Services;
|
|
||||||
|
|
||||||
public class DiscordNotificationService
|
|
||||||
{
|
|
||||||
private readonly EventSystem Event;
|
|
||||||
private readonly ResourceService ResourceService;
|
|
||||||
private readonly DiscordWebhookClient Client;
|
|
||||||
private readonly string AppUrl;
|
|
||||||
|
|
||||||
public DiscordNotificationService(
|
|
||||||
EventSystem eventSystem,
|
|
||||||
ConfigService configService,
|
|
||||||
ResourceService resourceService)
|
|
||||||
{
|
|
||||||
Event = eventSystem;
|
|
||||||
ResourceService = resourceService;
|
|
||||||
|
|
||||||
var config = configService.GetSection("Moonlight").GetSection("DiscordNotifications");
|
|
||||||
|
|
||||||
if (config.GetValue<bool>("Enable"))
|
|
||||||
{
|
|
||||||
Logger.Info("Discord notifications enabled");
|
|
||||||
|
|
||||||
Client = new(config.GetValue<string>("WebHook"));
|
|
||||||
AppUrl = configService.GetSection("Moonlight").GetValue<string>("AppUrl");
|
|
||||||
|
|
||||||
Event.On<User>("supportChat.new", this, OnNewSupportChat);
|
|
||||||
Event.On<SupportChatMessage>("supportChat.message", this, OnSupportChatMessage);
|
|
||||||
Event.On<User>("supportChat.close", this, OnSupportChatClose);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.Info("Discord notifications disabled");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task OnSupportChatClose(User user)
|
|
||||||
{
|
|
||||||
await SendNotification("", builder =>
|
|
||||||
{
|
|
||||||
builder.Title = "A new support chat has been marked as closed";
|
|
||||||
builder.Color = Color.Red;
|
|
||||||
builder.AddField("Email", user.Email);
|
|
||||||
builder.AddField("Firstname", user.FirstName);
|
|
||||||
builder.AddField("Lastname", user.LastName);
|
|
||||||
builder.Url = $"{AppUrl}/admin/support/view/{user.Id}";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task OnSupportChatMessage(SupportChatMessage message)
|
|
||||||
{
|
|
||||||
if(message.Sender == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
await SendNotification("", builder =>
|
|
||||||
{
|
|
||||||
builder.Title = "New message in support chat";
|
|
||||||
builder.Color = Color.Blue;
|
|
||||||
builder.AddField("Message", message.Content);
|
|
||||||
builder.Author = new EmbedAuthorBuilder()
|
|
||||||
.WithName($"{message.Sender.FirstName} {message.Sender.LastName}")
|
|
||||||
.WithIconUrl(ResourceService.Avatar(message.Sender));
|
|
||||||
builder.Url = $"{AppUrl}/admin/support/view/{message.Recipient.Id}";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task OnNewSupportChat(User user)
|
|
||||||
{
|
|
||||||
await SendNotification("", builder =>
|
|
||||||
{
|
|
||||||
builder.Title = "A new support chat has been marked as active";
|
|
||||||
builder.Color = Color.Green;
|
|
||||||
builder.AddField("Email", user.Email);
|
|
||||||
builder.AddField("Firstname", user.FirstName);
|
|
||||||
builder.AddField("Lastname", user.LastName);
|
|
||||||
builder.Url = $"{AppUrl}/admin/support/view/{user.Id}";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SendNotification(string content, Action<EmbedBuilder>? embed = null)
|
|
||||||
{
|
|
||||||
var e = new EmbedBuilder();
|
|
||||||
embed?.Invoke(e);
|
|
||||||
|
|
||||||
await Client.SendMessageAsync(
|
|
||||||
content,
|
|
||||||
false,
|
|
||||||
new []{e.Build()},
|
|
||||||
"Moonlight Notification",
|
|
||||||
ResourceService.Image("logo.svg")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
using System.Text;
|
|
||||||
using Moonlight.App.Helpers;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Services;
|
|
||||||
|
|
||||||
public class FabricService
|
|
||||||
{
|
|
||||||
private readonly HttpClient Client;
|
|
||||||
|
|
||||||
public FabricService()
|
|
||||||
{
|
|
||||||
Client = new();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> GetLatestInstallerVersion()
|
|
||||||
{
|
|
||||||
var data = await Client
|
|
||||||
.GetStringAsync("https://meta.fabricmc.net/v2/versions/installer");
|
|
||||||
|
|
||||||
var x = JsonConvert.DeserializeObject<JObject[]>(data) ?? Array.Empty<JObject>();
|
|
||||||
|
|
||||||
var stableVersions = new List<string>();
|
|
||||||
|
|
||||||
foreach (var y in x)
|
|
||||||
{
|
|
||||||
var section = new ConfigurationBuilder().AddJsonStream(
|
|
||||||
new MemoryStream(Encoding.ASCII.GetBytes(
|
|
||||||
y.Root.ToString()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).Build();
|
|
||||||
|
|
||||||
if (section.GetValue<bool>("stable"))
|
|
||||||
{
|
|
||||||
stableVersions.Add(section.GetValue<string>("version"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ParseHelper.GetHighestVersion(stableVersions.ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> GetLatestLoaderVersion()
|
|
||||||
{
|
|
||||||
var data = await Client
|
|
||||||
.GetStringAsync("https://meta.fabricmc.net/v2/versions/loader");
|
|
||||||
|
|
||||||
var x = JsonConvert.DeserializeObject<JObject[]>(data) ?? Array.Empty<JObject>();
|
|
||||||
|
|
||||||
var stableVersions = new List<string>();
|
|
||||||
|
|
||||||
foreach (var y in x)
|
|
||||||
{
|
|
||||||
var section = new ConfigurationBuilder().AddJsonStream(
|
|
||||||
new MemoryStream(Encoding.ASCII.GetBytes(
|
|
||||||
y.Root.ToString()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).Build();
|
|
||||||
|
|
||||||
if (section.GetValue<bool>("stable"))
|
|
||||||
{
|
|
||||||
stableVersions.Add(section.GetValue<string>("version"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ParseHelper.GetHighestVersion(stableVersions.ToArray());
|
|
||||||
}
|
|
||||||
public async Task<string[]> GetGameVersions()
|
|
||||||
{
|
|
||||||
var data = await Client
|
|
||||||
.GetStringAsync("https://meta.fabricmc.net/v2/versions/game");
|
|
||||||
|
|
||||||
var x = JsonConvert.DeserializeObject<JObject[]>(data) ?? Array.Empty<JObject>();
|
|
||||||
|
|
||||||
var stableVersions = new List<string>();
|
|
||||||
|
|
||||||
foreach (var y in x)
|
|
||||||
{
|
|
||||||
var section = new ConfigurationBuilder().AddJsonStream(
|
|
||||||
new MemoryStream(Encoding.ASCII.GetBytes(
|
|
||||||
y.Root.ToString()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).Build();
|
|
||||||
|
|
||||||
if (section.GetValue<bool>("stable"))
|
|
||||||
{
|
|
||||||
stableVersions.Add(section.GetValue<string>("version"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return stableVersions.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
using System.Text;
|
|
||||||
using Microsoft.JSInterop;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Services;
|
|
||||||
|
|
||||||
public class FileDownloadService
|
|
||||||
{
|
|
||||||
private readonly IJSRuntime JSRuntime;
|
|
||||||
|
|
||||||
public FileDownloadService(IJSRuntime jsRuntime)
|
|
||||||
{
|
|
||||||
JSRuntime = jsRuntime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DownloadStream(string fileName, Stream stream)
|
|
||||||
{
|
|
||||||
using var streamRef = new DotNetStreamReference(stream);
|
|
||||||
|
|
||||||
await JSRuntime.InvokeVoidAsync("moonlight.downloads.downloadStream", fileName, streamRef);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DownloadBytes(string fileName, byte[] bytes)
|
|
||||||
{
|
|
||||||
var ms = new MemoryStream(bytes);
|
|
||||||
|
|
||||||
await DownloadStream(fileName, ms);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DownloadString(string fileName, string content)
|
|
||||||
{
|
|
||||||
await DownloadBytes(fileName, Encoding.UTF8.GetBytes(content));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
using System.Text;
|
|
||||||
using Moonlight.App.Helpers;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Services;
|
|
||||||
|
|
||||||
public class ForgeService
|
|
||||||
{
|
|
||||||
private readonly HttpClient Client;
|
|
||||||
|
|
||||||
public ForgeService()
|
|
||||||
{
|
|
||||||
Client = new();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Key: 1.9.4-recommended Value: 12.17.0.2317
|
|
||||||
public async Task<Dictionary<string, string>> GetVersions()
|
|
||||||
{
|
|
||||||
var data = await Client.GetStringAsync(
|
|
||||||
"https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json");
|
|
||||||
|
|
||||||
var json = new ConfigurationBuilder().AddJsonStream(
|
|
||||||
new MemoryStream(Encoding.ASCII.GetBytes(
|
|
||||||
data
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).Build();
|
|
||||||
|
|
||||||
var d = new Dictionary<string, string>();
|
|
||||||
|
|
||||||
foreach (var section in json.GetSection("promos").GetChildren())
|
|
||||||
{
|
|
||||||
d.Add(section.Key, section.Value!);
|
|
||||||
}
|
|
||||||
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,9 +10,14 @@ public class ClipboardService
|
|||||||
{
|
{
|
||||||
JsRuntime = jsRuntime;
|
JsRuntime = jsRuntime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task CopyToClipboard(string data)
|
||||||
|
{
|
||||||
|
await JsRuntime.InvokeVoidAsync("copyTextToClipboard", data);
|
||||||
|
}
|
||||||
public async Task Copy(string data)
|
public async Task Copy(string data)
|
||||||
{
|
{
|
||||||
await JsRuntime.InvokeVoidAsync("moonlight.clipboard.copy", data);
|
await JsRuntime.InvokeVoidAsync("copyTextToClipboard", data);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -13,36 +13,36 @@ public class ToastService
|
|||||||
|
|
||||||
public async Task Info(string message)
|
public async Task Info(string message)
|
||||||
{
|
{
|
||||||
await JsRuntime.InvokeVoidAsync("moonlight.toasts.info", message);
|
await JsRuntime.InvokeVoidAsync("showInfoToast", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Error(string message)
|
public async Task Error(string message)
|
||||||
{
|
{
|
||||||
await JsRuntime.InvokeVoidAsync("moonlight.toasts.error", message);
|
await JsRuntime.InvokeVoidAsync("showErrorToast", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Warning(string message)
|
public async Task Warning(string message)
|
||||||
{
|
{
|
||||||
await JsRuntime.InvokeVoidAsync("moonlight.toasts.warning", message);
|
await JsRuntime.InvokeVoidAsync("showWarningToast", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Success(string message)
|
public async Task Success(string message)
|
||||||
{
|
{
|
||||||
await JsRuntime.InvokeVoidAsync("moonlight.toasts.success", message);
|
await JsRuntime.InvokeVoidAsync("showSuccessToast", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CreateProcessToast(string id, string text)
|
public async Task CreateProcessToast(string id, string text)
|
||||||
{
|
{
|
||||||
await JsRuntime.InvokeVoidAsync("moonlight.toasts.create", id, text);
|
await JsRuntime.InvokeVoidAsync("createToast", id, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateProcessToast(string id, string text)
|
public async Task UpdateProcessToast(string id, string text)
|
||||||
{
|
{
|
||||||
await JsRuntime.InvokeVoidAsync("moonlight.toasts.modify", id, text);
|
await JsRuntime.InvokeVoidAsync("modifyToast", id, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RemoveProcessToast(string id)
|
public async Task RemoveProcessToast(string id)
|
||||||
{
|
{
|
||||||
await JsRuntime.InvokeVoidAsync("moonlight.toasts.remove", id);
|
await JsRuntime.InvokeVoidAsync("removeToast", id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Mail;
|
using System.Net.Mail;
|
||||||
using Logging.Net;
|
using Logging.Net;
|
||||||
using MimeKit;
|
|
||||||
using Moonlight.App.Database.Entities;
|
using Moonlight.App.Database.Entities;
|
||||||
using Moonlight.App.Exceptions;
|
using Moonlight.App.Exceptions;
|
||||||
using Moonlight.App.Helpers;
|
using Moonlight.App.Helpers;
|
||||||
using SmtpClient = MailKit.Net.Smtp.SmtpClient;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Services;
|
namespace Moonlight.App.Services;
|
||||||
|
|
||||||
@@ -15,7 +13,6 @@ public class MailService
|
|||||||
private readonly string Password;
|
private readonly string Password;
|
||||||
private readonly string Email;
|
private readonly string Email;
|
||||||
private readonly int Port;
|
private readonly int Port;
|
||||||
private readonly bool Ssl;
|
|
||||||
|
|
||||||
public MailService(ConfigService configService)
|
public MailService(ConfigService configService)
|
||||||
{
|
{
|
||||||
@@ -27,7 +24,6 @@ public class MailService
|
|||||||
Password = mailConfig.GetValue<string>("Password");
|
Password = mailConfig.GetValue<string>("Password");
|
||||||
Email = mailConfig.GetValue<string>("Email");
|
Email = mailConfig.GetValue<string>("Email");
|
||||||
Port = mailConfig.GetValue<int>("Port");
|
Port = mailConfig.GetValue<int>("Port");
|
||||||
Ssl = mailConfig.GetValue<bool>("Ssl");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendMail(
|
public async Task SendMail(
|
||||||
@@ -58,24 +54,20 @@ public class MailService
|
|||||||
{
|
{
|
||||||
using var client = new SmtpClient();
|
using var client = new SmtpClient();
|
||||||
|
|
||||||
var mailMessage = new MimeMessage();
|
client.Host = Server;
|
||||||
mailMessage.From.Add(new MailboxAddress(Email, Email));
|
client.Port = Port;
|
||||||
mailMessage.To.Add(new MailboxAddress(user.Email, user.Email));
|
client.EnableSsl = true;
|
||||||
mailMessage.Subject = $"Hey {user.FirstName}, there are news from moonlight";
|
client.Credentials = new NetworkCredential(Email, Password);
|
||||||
|
|
||||||
var body = new BodyBuilder
|
|
||||||
{
|
|
||||||
HtmlBody = parsed
|
|
||||||
};
|
|
||||||
mailMessage.Body = body.ToMessageBody();
|
|
||||||
|
|
||||||
using (var smtpClient = new SmtpClient())
|
await client.SendMailAsync(new MailMessage()
|
||||||
{
|
{
|
||||||
await smtpClient.ConnectAsync(Server, Port, Ssl);
|
From = new MailAddress(Email),
|
||||||
await smtpClient.AuthenticateAsync(Email, Password);
|
Sender = new MailAddress(Email),
|
||||||
await smtpClient.SendAsync(mailMessage);
|
Body = parsed,
|
||||||
await smtpClient.DisconnectAsync(true);
|
IsBodyHtml = true,
|
||||||
}
|
Subject = $"Hey {user.FirstName}, there are news from moonlight",
|
||||||
|
To = { new MailAddress(user.Email) }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|||||||
11
Moonlight/App/Services/MessageService.cs
Normal file
11
Moonlight/App/Services/MessageService.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using Moonlight.App.MessageSystem;
|
||||||
|
|
||||||
|
namespace Moonlight.App.Services;
|
||||||
|
|
||||||
|
public class MessageService : MessageSender
|
||||||
|
{
|
||||||
|
public MessageService()
|
||||||
|
{
|
||||||
|
Debug = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Moonlight.App.Database;
|
using Moonlight.App.Database;
|
||||||
using Moonlight.App.Database.Entities;
|
using Moonlight.App.Database.Entities;
|
||||||
using Moonlight.App.Events;
|
|
||||||
using Moonlight.App.Exceptions;
|
using Moonlight.App.Exceptions;
|
||||||
using Moonlight.App.Helpers;
|
using Moonlight.App.Helpers;
|
||||||
using Moonlight.App.Helpers.Files;
|
using Moonlight.App.Helpers.Files;
|
||||||
@@ -22,8 +21,8 @@ public class ServerService
|
|||||||
private readonly UserRepository UserRepository;
|
private readonly UserRepository UserRepository;
|
||||||
private readonly ImageRepository ImageRepository;
|
private readonly ImageRepository ImageRepository;
|
||||||
private readonly NodeRepository NodeRepository;
|
private readonly NodeRepository NodeRepository;
|
||||||
private readonly NodeAllocationRepository NodeAllocationRepository;
|
|
||||||
private readonly WingsApiHelper WingsApiHelper;
|
private readonly WingsApiHelper WingsApiHelper;
|
||||||
|
private readonly MessageService MessageService;
|
||||||
private readonly UserService UserService;
|
private readonly UserService UserService;
|
||||||
private readonly ConfigService ConfigService;
|
private readonly ConfigService ConfigService;
|
||||||
private readonly WingsJwtHelper WingsJwtHelper;
|
private readonly WingsJwtHelper WingsJwtHelper;
|
||||||
@@ -31,8 +30,6 @@ public class ServerService
|
|||||||
private readonly AuditLogService AuditLogService;
|
private readonly AuditLogService AuditLogService;
|
||||||
private readonly ErrorLogService ErrorLogService;
|
private readonly ErrorLogService ErrorLogService;
|
||||||
private readonly NodeService NodeService;
|
private readonly NodeService NodeService;
|
||||||
private readonly DateTimeService DateTimeService;
|
|
||||||
private readonly EventSystem Event;
|
|
||||||
|
|
||||||
public ServerService(
|
public ServerService(
|
||||||
ServerRepository serverRepository,
|
ServerRepository serverRepository,
|
||||||
@@ -40,22 +37,21 @@ public class ServerService
|
|||||||
UserRepository userRepository,
|
UserRepository userRepository,
|
||||||
ImageRepository imageRepository,
|
ImageRepository imageRepository,
|
||||||
NodeRepository nodeRepository,
|
NodeRepository nodeRepository,
|
||||||
|
MessageService messageService,
|
||||||
UserService userService,
|
UserService userService,
|
||||||
ConfigService configService,
|
ConfigService configService,
|
||||||
WingsJwtHelper wingsJwtHelper,
|
WingsJwtHelper wingsJwtHelper,
|
||||||
SecurityLogService securityLogService,
|
SecurityLogService securityLogService,
|
||||||
AuditLogService auditLogService,
|
AuditLogService auditLogService,
|
||||||
ErrorLogService errorLogService,
|
ErrorLogService errorLogService,
|
||||||
NodeService nodeService,
|
NodeService nodeService)
|
||||||
NodeAllocationRepository nodeAllocationRepository,
|
|
||||||
DateTimeService dateTimeService,
|
|
||||||
EventSystem eventSystem)
|
|
||||||
{
|
{
|
||||||
ServerRepository = serverRepository;
|
ServerRepository = serverRepository;
|
||||||
WingsApiHelper = wingsApiHelper;
|
WingsApiHelper = wingsApiHelper;
|
||||||
UserRepository = userRepository;
|
UserRepository = userRepository;
|
||||||
ImageRepository = imageRepository;
|
ImageRepository = imageRepository;
|
||||||
NodeRepository = nodeRepository;
|
NodeRepository = nodeRepository;
|
||||||
|
MessageService = messageService;
|
||||||
UserService = userService;
|
UserService = userService;
|
||||||
ConfigService = configService;
|
ConfigService = configService;
|
||||||
WingsJwtHelper = wingsJwtHelper;
|
WingsJwtHelper = wingsJwtHelper;
|
||||||
@@ -63,9 +59,6 @@ public class ServerService
|
|||||||
AuditLogService = auditLogService;
|
AuditLogService = auditLogService;
|
||||||
ErrorLogService = errorLogService;
|
ErrorLogService = errorLogService;
|
||||||
NodeService = nodeService;
|
NodeService = nodeService;
|
||||||
NodeAllocationRepository = nodeAllocationRepository;
|
|
||||||
DateTimeService = dateTimeService;
|
|
||||||
Event = eventSystem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Server EnsureNodeData(Server s)
|
private Server EnsureNodeData(Server s)
|
||||||
@@ -119,9 +112,9 @@ public class ServerService
|
|||||||
|
|
||||||
var backup = new ServerBackup()
|
var backup = new ServerBackup()
|
||||||
{
|
{
|
||||||
Name = $"Created at {DateTimeService.GetCurrent().ToShortDateString()} {DateTimeService.GetCurrent().ToShortTimeString()}",
|
Name = $"Created at {DateTime.Now.ToShortDateString()} {DateTime.Now.ToShortTimeString()}",
|
||||||
Uuid = Guid.NewGuid(),
|
Uuid = Guid.NewGuid(),
|
||||||
CreatedAt = DateTimeService.GetCurrent(),
|
CreatedAt = DateTime.Now,
|
||||||
Created = false
|
Created = false
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -193,27 +186,15 @@ public class ServerService
|
|||||||
.Include(x => x.Backups)
|
.Include(x => x.Backups)
|
||||||
.First(x => x.Id == server.Id);
|
.First(x => x.Id == server.Id);
|
||||||
|
|
||||||
try
|
await WingsApiHelper.Delete(serverData.Node, $"api/servers/{serverData.Uuid}/backup/{serverBackup.Uuid}",
|
||||||
{
|
null);
|
||||||
await WingsApiHelper.Delete(serverData.Node, $"api/servers/{serverData.Uuid}/backup/{serverBackup.Uuid}",
|
|
||||||
null);
|
|
||||||
}
|
|
||||||
catch (WingsException e)
|
|
||||||
{
|
|
||||||
// when a backup is not longer there we can
|
|
||||||
// safely delete the backup so we ignore this error
|
|
||||||
if (e.StatusCode != 404)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var backup = serverData.Backups.First(x => x.Uuid == serverBackup.Uuid);
|
var backup = serverData.Backups.First(x => x.Uuid == serverBackup.Uuid);
|
||||||
serverData.Backups.Remove(backup);
|
serverData.Backups.Remove(backup);
|
||||||
|
|
||||||
ServerRepository.Update(serverData);
|
ServerRepository.Update(serverData);
|
||||||
|
|
||||||
await Event.Emit("wings.backups.delete", backup);
|
await MessageService.Emit("wings.backups.delete", backup);
|
||||||
|
|
||||||
await AuditLogService.Log(AuditLogType.DeleteBackup,
|
await AuditLogService.Log(AuditLogType.DeleteBackup,
|
||||||
x =>
|
x =>
|
||||||
@@ -263,7 +244,7 @@ public class ServerService
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Server> Create(string name, int cpu, long memory, long disk, User u, Image i, Node? n = null,
|
public async Task<Server> Create(string name, int cpu, long memory, long disk, User u, Image i, Node? n = null,
|
||||||
Action<Server>? modifyDetails = null)
|
Action<Server>? modifyDetails = null, int allocations = 1)
|
||||||
{
|
{
|
||||||
var user = UserRepository
|
var user = UserRepository
|
||||||
.Get()
|
.Get()
|
||||||
@@ -275,21 +256,32 @@ public class ServerService
|
|||||||
.Include(x => x.DockerImages)
|
.Include(x => x.DockerImages)
|
||||||
.First(x => x.Id == i.Id);
|
.First(x => x.Id == i.Id);
|
||||||
|
|
||||||
var allocations = image.Allocations;
|
Node node;
|
||||||
|
|
||||||
Node node = n ?? NodeRepository.Get().First();
|
if (n == null)
|
||||||
|
{
|
||||||
|
node = NodeRepository
|
||||||
|
.Get()
|
||||||
|
.Include(x => x.Allocations)
|
||||||
|
.First(); //TODO: Add smart deploy maybe
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
node = NodeRepository
|
||||||
|
.Get()
|
||||||
|
.Include(x => x.Allocations)
|
||||||
|
.First(x => x.Id == n.Id);
|
||||||
|
}
|
||||||
|
|
||||||
NodeAllocation[] freeAllocations;
|
NodeAllocation[] freeAllocations;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// We have sadly no choice to use entity framework to do what the sql call does, there
|
freeAllocations = node.Allocations
|
||||||
// are only slower ways, so we will use a raw sql call as a exception
|
.Where(a => !ServerRepository.Get()
|
||||||
|
.SelectMany(s => s.Allocations)
|
||||||
freeAllocations = NodeAllocationRepository
|
.Any(b => b.Id == a.Id))
|
||||||
.Get()
|
.Take(allocations).ToArray();
|
||||||
.FromSqlRaw($"SELECT * FROM `NodeAllocations` WHERE ServerId IS NULL AND NodeId={node.Id} LIMIT {allocations}")
|
|
||||||
.ToArray();
|
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
@@ -380,7 +372,7 @@ public class ServerService
|
|||||||
|
|
||||||
var user = await UserService.SftpLogin(id, password);
|
var user = await UserService.SftpLogin(id, password);
|
||||||
|
|
||||||
if (server.Owner.Id == user.Id || user.Admin)
|
if (server.Owner.Id == user.Id)
|
||||||
{
|
{
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ using JWT.Builder;
|
|||||||
using JWT.Exceptions;
|
using JWT.Exceptions;
|
||||||
using Logging.Net;
|
using Logging.Net;
|
||||||
using Moonlight.App.Database.Entities;
|
using Moonlight.App.Database.Entities;
|
||||||
using Moonlight.App.Helpers;
|
|
||||||
using Moonlight.App.Models.Misc;
|
using Moonlight.App.Models.Misc;
|
||||||
using Moonlight.App.Repositories;
|
using Moonlight.App.Repositories;
|
||||||
using Moonlight.App.Services.LogServices;
|
using Moonlight.App.Services.LogServices;
|
||||||
@@ -124,9 +123,9 @@ public class IdentityService
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var iatD = DateTimeOffset.FromUnixTimeSeconds(iat).ToUniversalTime().DateTime;
|
var issuedAt = DateTimeOffset.FromUnixTimeSeconds(iat).DateTime;
|
||||||
|
|
||||||
if (iatD < user.TokenValidTime)
|
if (issuedAt < user.TokenValidTime.ToUniversalTime())
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
UserCache = user;
|
UserCache = user;
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ public class SessionService
|
|||||||
private readonly IdentityService IdentityService;
|
private readonly IdentityService IdentityService;
|
||||||
private readonly NavigationManager NavigationManager;
|
private readonly NavigationManager NavigationManager;
|
||||||
private readonly AlertService AlertService;
|
private readonly AlertService AlertService;
|
||||||
private readonly DateTimeService DateTimeService;
|
|
||||||
|
|
||||||
private Session? OwnSession;
|
private Session? OwnSession;
|
||||||
|
|
||||||
@@ -20,14 +19,12 @@ public class SessionService
|
|||||||
SessionRepository sessionRepository,
|
SessionRepository sessionRepository,
|
||||||
IdentityService identityService,
|
IdentityService identityService,
|
||||||
NavigationManager navigationManager,
|
NavigationManager navigationManager,
|
||||||
AlertService alertService,
|
AlertService alertService)
|
||||||
DateTimeService dateTimeService)
|
|
||||||
{
|
{
|
||||||
SessionRepository = sessionRepository;
|
SessionRepository = sessionRepository;
|
||||||
IdentityService = identityService;
|
IdentityService = identityService;
|
||||||
NavigationManager = navigationManager;
|
NavigationManager = navigationManager;
|
||||||
AlertService = alertService;
|
AlertService = alertService;
|
||||||
DateTimeService = dateTimeService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Register()
|
public async Task Register()
|
||||||
@@ -39,7 +36,7 @@ public class SessionService
|
|||||||
Ip = IdentityService.GetIp(),
|
Ip = IdentityService.GetIp(),
|
||||||
Url = NavigationManager.Uri,
|
Url = NavigationManager.Uri,
|
||||||
Device = IdentityService.GetDevice(),
|
Device = IdentityService.GetDevice(),
|
||||||
CreatedAt = DateTimeService.GetCurrent(),
|
CreatedAt = DateTime.Now,
|
||||||
User = user,
|
User = user,
|
||||||
Navigation = NavigationManager,
|
Navigation = NavigationManager,
|
||||||
AlertService = AlertService
|
AlertService = AlertService
|
||||||
@@ -67,8 +64,10 @@ public class SessionService
|
|||||||
{
|
{
|
||||||
foreach (var session in SessionRepository.Get())
|
foreach (var session in SessionRepository.Get())
|
||||||
{
|
{
|
||||||
if(session.User != null && session.User.Id == user.Id)
|
if (session.User.Id == user.Id)
|
||||||
|
{
|
||||||
session.Navigation.NavigateTo(session.Navigation.Uri, true);
|
session.Navigation.NavigateTo(session.Navigation.Uri, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,20 +6,18 @@ namespace Moonlight.App.Services;
|
|||||||
public class SmartDeployService
|
public class SmartDeployService
|
||||||
{
|
{
|
||||||
private readonly NodeRepository NodeRepository;
|
private readonly NodeRepository NodeRepository;
|
||||||
private readonly Repository<CloudPanel> CloudPanelRepository;
|
private readonly PleskServerRepository PleskServerRepository;
|
||||||
private readonly WebSpaceService WebSpaceService;
|
private readonly WebsiteService WebsiteService;
|
||||||
private readonly NodeService NodeService;
|
private readonly NodeService NodeService;
|
||||||
|
|
||||||
public SmartDeployService(
|
public SmartDeployService(
|
||||||
NodeRepository nodeRepository,
|
NodeRepository nodeRepository,
|
||||||
NodeService nodeService,
|
NodeService nodeService, PleskServerRepository pleskServerRepository, WebsiteService websiteService)
|
||||||
WebSpaceService webSpaceService,
|
|
||||||
Repository<CloudPanel> cloudPanelRepository)
|
|
||||||
{
|
{
|
||||||
NodeRepository = nodeRepository;
|
NodeRepository = nodeRepository;
|
||||||
NodeService = nodeService;
|
NodeService = nodeService;
|
||||||
WebSpaceService = webSpaceService;
|
PleskServerRepository = pleskServerRepository;
|
||||||
CloudPanelRepository = cloudPanelRepository;
|
WebsiteService = websiteService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Node?> GetNode()
|
public async Task<Node?> GetNode()
|
||||||
@@ -40,14 +38,16 @@ public class SmartDeployService
|
|||||||
return data.MaxBy(x => x.Value).Key;
|
return data.MaxBy(x => x.Value).Key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<CloudPanel?> GetCloudPanel()
|
public async Task<PleskServer?> GetPleskServer()
|
||||||
{
|
{
|
||||||
var result = new List<CloudPanel>();
|
var result = new List<PleskServer>();
|
||||||
|
|
||||||
foreach (var cloudPanel in CloudPanelRepository.Get().ToArray())
|
foreach (var pleskServer in PleskServerRepository.Get().ToArray())
|
||||||
{
|
{
|
||||||
if (await WebSpaceService.IsHostUp(cloudPanel))
|
if (await WebsiteService.IsHostUp(pleskServer))
|
||||||
result.Add(cloudPanel);
|
{
|
||||||
|
result.Add(pleskServer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.FirstOrDefault();
|
return result.FirstOrDefault();
|
||||||
|
|||||||
@@ -1,78 +1,57 @@
|
|||||||
using Logging.Net;
|
using Moonlight.App.Database;
|
||||||
using Moonlight.App.Database;
|
|
||||||
using Moonlight.App.Database.Entities;
|
|
||||||
using Moonlight.App.Repositories;
|
using Moonlight.App.Repositories;
|
||||||
using Moonlight.App.Services.Sessions;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Services.Statistics;
|
namespace Moonlight.App.Services.Statistics;
|
||||||
|
|
||||||
public class StatisticsCaptureService
|
public class StatisticsCaptureService
|
||||||
{
|
{
|
||||||
|
private readonly DataContext DataContext;
|
||||||
|
private readonly ConfigService ConfigService;
|
||||||
|
private readonly StatisticsRepository StatisticsRepository;
|
||||||
private readonly IServiceScopeFactory ServiceScopeFactory;
|
private readonly IServiceScopeFactory ServiceScopeFactory;
|
||||||
private readonly DateTimeService DateTimeService;
|
private readonly WebsiteService WebsiteService;
|
||||||
private readonly PeriodicTimer Timer;
|
private readonly PleskServerRepository PleskServerRepository;
|
||||||
|
private PeriodicTimer Timer;
|
||||||
|
|
||||||
public StatisticsCaptureService(IServiceScopeFactory serviceScopeFactory, ConfigService configService, DateTimeService dateTimeService)
|
public StatisticsCaptureService(IServiceScopeFactory serviceScopeFactory, ConfigService configService)
|
||||||
{
|
{
|
||||||
ServiceScopeFactory = serviceScopeFactory;
|
ServiceScopeFactory = serviceScopeFactory;
|
||||||
DateTimeService = dateTimeService;
|
var provider = ServiceScopeFactory.CreateScope().ServiceProvider;
|
||||||
|
|
||||||
|
DataContext = provider.GetRequiredService<DataContext>();
|
||||||
|
ConfigService = configService;
|
||||||
|
StatisticsRepository = provider.GetRequiredService<StatisticsRepository>();
|
||||||
|
WebsiteService = provider.GetRequiredService<WebsiteService>();
|
||||||
|
PleskServerRepository = provider.GetRequiredService<PleskServerRepository>();
|
||||||
|
|
||||||
var config = configService
|
var config = ConfigService.GetSection("Moonlight").GetSection("Statistics");
|
||||||
.GetSection("Moonlight")
|
|
||||||
.GetSection("Statistics");
|
|
||||||
|
|
||||||
if(!config.GetValue<bool>("Enabled"))
|
if(!config.GetValue<bool>("Enabled"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var period = TimeSpan.FromMinutes(config.GetValue<int>("Wait"));
|
var _period = config.GetValue<int>("Wait");
|
||||||
|
var period = TimeSpan.FromMinutes(_period);
|
||||||
Timer = new(period);
|
Timer = new(period);
|
||||||
|
|
||||||
Logger.Info("Starting statistics system");
|
|
||||||
Task.Run(Run);
|
Task.Run(Run);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Run()
|
private async Task Run()
|
||||||
{
|
{
|
||||||
try
|
while (await Timer.WaitForNextTickAsync())
|
||||||
{
|
{
|
||||||
while (await Timer.WaitForNextTickAsync())
|
StatisticsRepository.Add("statistics.usersCount", DataContext.Users.Count());
|
||||||
|
StatisticsRepository.Add("statistics.serversCount", DataContext.Servers.Count());
|
||||||
|
StatisticsRepository.Add("statistics.domainsCount", DataContext.Domains.Count());
|
||||||
|
StatisticsRepository.Add("statistics.websitesCount", DataContext.Websites.Count());
|
||||||
|
|
||||||
|
int databases = 0;
|
||||||
|
|
||||||
|
await foreach (var pleskServer in PleskServerRepository.Get())
|
||||||
{
|
{
|
||||||
Logger.Warn("Creating statistics");
|
databases += (await WebsiteService.GetDefaultDatabaseServer(pleskServer)).DbCount;
|
||||||
|
|
||||||
using var scope = ServiceScopeFactory.CreateScope();
|
|
||||||
|
|
||||||
var statisticsRepo = scope.ServiceProvider.GetRequiredService<Repository<StatisticsData>>();
|
|
||||||
var usersRepo = scope.ServiceProvider.GetRequiredService<Repository<User>>();
|
|
||||||
var serversRepo = scope.ServiceProvider.GetRequiredService<Repository<Server>>();
|
|
||||||
var domainsRepo = scope.ServiceProvider.GetRequiredService<Repository<Domain>>();
|
|
||||||
var webspacesRepo = scope.ServiceProvider.GetRequiredService<Repository<WebSpace>>();
|
|
||||||
var databasesRepo = scope.ServiceProvider.GetRequiredService<Repository<MySqlDatabase>>();
|
|
||||||
var sessionService = scope.ServiceProvider.GetRequiredService<SessionService>();
|
|
||||||
|
|
||||||
void AddEntry(string chart, int value)
|
|
||||||
{
|
|
||||||
statisticsRepo!.Add(new StatisticsData()
|
|
||||||
{
|
|
||||||
Chart = chart,
|
|
||||||
Value = value,
|
|
||||||
Date = DateTimeService.GetCurrent()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
AddEntry("usersCount", usersRepo.Get().Count());
|
|
||||||
AddEntry("serversCount", serversRepo.Get().Count());
|
|
||||||
AddEntry("domainsCount", domainsRepo.Get().Count());
|
|
||||||
AddEntry("webspacesCount", webspacesRepo.Get().Count());
|
|
||||||
AddEntry("databasesCount", databasesRepo.Get().Count());
|
|
||||||
AddEntry("sessionsCount", sessionService.GetAll().Length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Log("Statistics are weird");
|
StatisticsRepository.Add("statistics.databasesCount", databases);
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Logger.Error("An unexpected error occured while capturing statistics");
|
|
||||||
Logger.Error(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,17 +7,15 @@ namespace Moonlight.App.Services.Statistics;
|
|||||||
public class StatisticsViewService
|
public class StatisticsViewService
|
||||||
{
|
{
|
||||||
private readonly StatisticsRepository StatisticsRepository;
|
private readonly StatisticsRepository StatisticsRepository;
|
||||||
private readonly DateTimeService DateTimeService;
|
|
||||||
|
|
||||||
public StatisticsViewService(StatisticsRepository statisticsRepository, DateTimeService dateTimeService)
|
public StatisticsViewService(StatisticsRepository statisticsRepository)
|
||||||
{
|
{
|
||||||
StatisticsRepository = statisticsRepository;
|
StatisticsRepository = statisticsRepository;
|
||||||
DateTimeService = dateTimeService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public StatisticsData[] GetData(string chart, StatisticsTimeSpan timeSpan)
|
public StatisticsData[] GetData(string chart, StatisticsTimeSpan timeSpan)
|
||||||
{
|
{
|
||||||
var startDate = DateTimeService.GetCurrent() - TimeSpan.FromHours((int)timeSpan);
|
var startDate = DateTime.Now - TimeSpan.FromHours((int)timeSpan);
|
||||||
|
|
||||||
var objs = StatisticsRepository.Get().Where(x => x.Date > startDate && x.Chart == chart);
|
var objs = StatisticsRepository.Get().Where(x => x.Date > startDate && x.Chart == chart);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Moonlight.App.Database.Entities;
|
using Moonlight.App.Database.Entities;
|
||||||
using Moonlight.App.Exceptions;
|
using Moonlight.App.Exceptions;
|
||||||
using Moonlight.App.Helpers;
|
|
||||||
using Moonlight.App.Models.Misc;
|
using Moonlight.App.Models.Misc;
|
||||||
using Moonlight.App.Repositories;
|
using Moonlight.App.Repositories;
|
||||||
using Moonlight.App.Services.Sessions;
|
using Moonlight.App.Services.Sessions;
|
||||||
@@ -15,18 +14,20 @@ public class SubscriptionService
|
|||||||
private readonly OneTimeJwtService OneTimeJwtService;
|
private readonly OneTimeJwtService OneTimeJwtService;
|
||||||
private readonly IdentityService IdentityService;
|
private readonly IdentityService IdentityService;
|
||||||
private readonly UserRepository UserRepository;
|
private readonly UserRepository UserRepository;
|
||||||
|
private readonly ConfigService ConfigService;
|
||||||
|
|
||||||
public SubscriptionService(
|
public SubscriptionService(
|
||||||
SubscriptionRepository subscriptionRepository,
|
SubscriptionRepository subscriptionRepository,
|
||||||
OneTimeJwtService oneTimeJwtService,
|
OneTimeJwtService oneTimeJwtService,
|
||||||
IdentityService identityService,
|
IdentityService identityService,
|
||||||
UserRepository userRepository
|
UserRepository userRepository,
|
||||||
)
|
ConfigService configService)
|
||||||
{
|
{
|
||||||
SubscriptionRepository = subscriptionRepository;
|
SubscriptionRepository = subscriptionRepository;
|
||||||
OneTimeJwtService = oneTimeJwtService;
|
OneTimeJwtService = oneTimeJwtService;
|
||||||
IdentityService = identityService;
|
IdentityService = identityService;
|
||||||
UserRepository = userRepository;
|
UserRepository = userRepository;
|
||||||
|
ConfigService = configService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Subscription?> GetCurrent()
|
public async Task<Subscription?> GetCurrent()
|
||||||
@@ -89,15 +90,13 @@ public class SubscriptionService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SubscriptionLimit> GetLimit(string identifier) // Cache, optimize sql code
|
public async Task<SubscriptionLimit> GetLimit(string identifier)
|
||||||
{
|
{
|
||||||
var subscription = await GetCurrent();
|
var subscription = await GetCurrent();
|
||||||
var defaultLimits = await GetDefaultLimits();
|
|
||||||
|
|
||||||
if (subscription == null)
|
if (subscription == null)
|
||||||
{
|
{
|
||||||
// If the default subscription limit with identifier is found, return it. if not, return empty
|
return new()
|
||||||
return defaultLimits.FirstOrDefault(x => x.Identifier == identifier) ?? new()
|
|
||||||
{
|
{
|
||||||
Identifier = identifier,
|
Identifier = identifier,
|
||||||
Amount = 0
|
Amount = 0
|
||||||
@@ -112,9 +111,8 @@ public class SubscriptionService
|
|||||||
|
|
||||||
if (foundLimit != null)
|
if (foundLimit != null)
|
||||||
return foundLimit;
|
return foundLimit;
|
||||||
|
|
||||||
// If the default subscription limit with identifier is found, return it. if not, return empty
|
return new()
|
||||||
return defaultLimits.FirstOrDefault(x => x.Identifier == identifier) ?? new()
|
|
||||||
{
|
{
|
||||||
Identifier = identifier,
|
Identifier = identifier,
|
||||||
Amount = 0
|
Amount = 0
|
||||||
@@ -135,17 +133,4 @@ public class SubscriptionService
|
|||||||
|
|
||||||
return userWithData;
|
return userWithData;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<SubscriptionLimit[]> GetDefaultLimits() // Add cache and reload option
|
|
||||||
{
|
|
||||||
var defaultSubscriptionJson = "[]";
|
|
||||||
|
|
||||||
if (File.Exists(PathBuilder.File("storage", "configs", "default_subscription.json")))
|
|
||||||
{
|
|
||||||
defaultSubscriptionJson =
|
|
||||||
await File.ReadAllTextAsync(PathBuilder.File("storage", "configs", "default_subscription.json"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return JsonConvert.DeserializeObject<SubscriptionLimit[]>(defaultSubscriptionJson) ?? Array.Empty<SubscriptionLimit>();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
132
Moonlight/App/Services/Support/SupportAdminService.cs
Normal file
132
Moonlight/App/Services/Support/SupportAdminService.cs
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
using Moonlight.App.Database.Entities;
|
||||||
|
using Moonlight.App.Services.Sessions;
|
||||||
|
|
||||||
|
namespace Moonlight.App.Services.Support;
|
||||||
|
|
||||||
|
public class SupportAdminService
|
||||||
|
{
|
||||||
|
private readonly SupportServerService SupportServerService;
|
||||||
|
private readonly IdentityService IdentityService;
|
||||||
|
private readonly MessageService MessageService;
|
||||||
|
|
||||||
|
public EventHandler<SupportMessage> OnNewMessage;
|
||||||
|
|
||||||
|
public EventHandler OnUpdateTyping;
|
||||||
|
private List<string> TypingUsers = new();
|
||||||
|
|
||||||
|
private User Self;
|
||||||
|
private User Recipient;
|
||||||
|
|
||||||
|
public SupportAdminService(
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
|
||||||
|
MessageService.Subscribe<SupportClientService, User>(
|
||||||
|
$"support.{Self.Id}.typing",
|
||||||
|
this,
|
||||||
|
user =>
|
||||||
|
{
|
||||||
|
HandleTyping(user);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Typing
|
||||||
|
|
||||||
|
private void HandleTyping(User user)
|
||||||
|
{
|
||||||
|
var name = $"{user.FirstName} {user.LastName}";
|
||||||
|
|
||||||
|
lock (TypingUsers)
|
||||||
|
{
|
||||||
|
if (!TypingUsers.Contains(name))
|
||||||
|
{
|
||||||
|
TypingUsers.Add(name);
|
||||||
|
OnUpdateTyping!.Invoke(this, null!);
|
||||||
|
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||||
|
|
||||||
|
if (TypingUsers.Contains(name))
|
||||||
|
{
|
||||||
|
TypingUsers.Remove(name);
|
||||||
|
OnUpdateTyping!.Invoke(this, null!);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string[] GetTypingUsers()
|
||||||
|
{
|
||||||
|
lock (TypingUsers)
|
||||||
|
{
|
||||||
|
return TypingUsers.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task TriggerTyping()
|
||||||
|
{
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await MessageService.Emit($"support.{Recipient.Id}.admintyping", Self);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
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 async Task Close()
|
||||||
|
{
|
||||||
|
await SupportServerService.Close(Recipient);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
MessageService.Unsubscribe($"support.{Recipient.Id}.message", this);
|
||||||
|
MessageService.Unsubscribe($"support.{Recipient.Id}.typing", this);
|
||||||
|
}
|
||||||
|
}
|
||||||
124
Moonlight/App/Services/Support/SupportClientService.cs
Normal file
124
Moonlight/App/Services/Support/SupportClientService.cs
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
public EventHandler OnUpdateTyping;
|
||||||
|
private List<string> TypingUsers = new();
|
||||||
|
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
|
||||||
|
MessageService.Subscribe<SupportClientService, User>(
|
||||||
|
$"support.{Self.Id}.admintyping",
|
||||||
|
this,
|
||||||
|
user =>
|
||||||
|
{
|
||||||
|
HandleTyping(user);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Typing
|
||||||
|
|
||||||
|
private void HandleTyping(User user)
|
||||||
|
{
|
||||||
|
var name = $"{user.FirstName} {user.LastName}";
|
||||||
|
|
||||||
|
lock (TypingUsers)
|
||||||
|
{
|
||||||
|
if (!TypingUsers.Contains(name))
|
||||||
|
{
|
||||||
|
TypingUsers.Add(name);
|
||||||
|
OnUpdateTyping!.Invoke(this, null!);
|
||||||
|
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||||
|
|
||||||
|
if (TypingUsers.Contains(name))
|
||||||
|
{
|
||||||
|
TypingUsers.Remove(name);
|
||||||
|
OnUpdateTyping!.Invoke(this, null!);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string[] GetTypingUsers()
|
||||||
|
{
|
||||||
|
lock (TypingUsers)
|
||||||
|
{
|
||||||
|
return TypingUsers.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task TriggerTyping()
|
||||||
|
{
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await MessageService.Emit($"support.{Self.Id}.typing", Self);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
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);
|
||||||
|
MessageService.Unsubscribe($"support.{Self.Id}.admintyping", this);
|
||||||
|
}
|
||||||
|
}
|
||||||
138
Moonlight/App/Services/Support/SupportServerService.cs
Normal file
138
Moonlight/App/Services/Support/SupportServerService.cs
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
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 () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (!message.IsSupport)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
await MessageService.Emit($"support.new", recipient);
|
||||||
|
|
||||||
|
Logger.Info("Support ticket created: " + recipient.Id);
|
||||||
|
//TODO: Ping or so
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Error("Error sending message");
|
||||||
|
Logger.Error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Close(User user)
|
||||||
|
{
|
||||||
|
var recipient = UserRepository.Get().First(x => x.Id == user.Id);
|
||||||
|
|
||||||
|
recipient.SupportPending = false;
|
||||||
|
UserRepository.Update(recipient);
|
||||||
|
|
||||||
|
var systemMessage = new SupportMessage()
|
||||||
|
{
|
||||||
|
Recipient = recipient,
|
||||||
|
Sender = null,
|
||||||
|
IsSystem = true,
|
||||||
|
Message = "The ticket is now closed. Type a message to open it again"
|
||||||
|
};
|
||||||
|
|
||||||
|
SupportMessageRepository.Add(systemMessage);
|
||||||
|
|
||||||
|
await MessageService.Emit($"support.{recipient.Id}.message", systemMessage);
|
||||||
|
await MessageService.Emit($"support.close", recipient);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
using Moonlight.App.Database.Entities;
|
|
||||||
using Moonlight.App.Events;
|
|
||||||
using Moonlight.App.Services.Sessions;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Services.SupportChat;
|
|
||||||
|
|
||||||
public class SupportChatAdminService
|
|
||||||
{
|
|
||||||
private readonly EventSystem Event;
|
|
||||||
private readonly IdentityService IdentityService;
|
|
||||||
private readonly SupportChatServerService ServerService;
|
|
||||||
|
|
||||||
public Func<SupportChatMessage, Task>? OnMessage { get; set; }
|
|
||||||
public Func<string[], Task>? OnTypingChanged { get; set; }
|
|
||||||
|
|
||||||
private User? User;
|
|
||||||
private User Recipient = null!;
|
|
||||||
private readonly List<User> TypingUsers = new();
|
|
||||||
|
|
||||||
public SupportChatAdminService(
|
|
||||||
EventSystem eventSystem,
|
|
||||||
SupportChatServerService serverService,
|
|
||||||
IdentityService identityService)
|
|
||||||
{
|
|
||||||
Event = eventSystem;
|
|
||||||
ServerService = serverService;
|
|
||||||
IdentityService = identityService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Start(User recipient)
|
|
||||||
{
|
|
||||||
User = await IdentityService.Get();
|
|
||||||
Recipient = recipient;
|
|
||||||
|
|
||||||
if (User != null)
|
|
||||||
{
|
|
||||||
await Event.On<SupportChatMessage>($"supportChat.{Recipient.Id}.message", this, async message =>
|
|
||||||
{
|
|
||||||
if (OnMessage != null)
|
|
||||||
{
|
|
||||||
if(message.Sender != null && message.Sender.Id == User.Id)
|
|
||||||
return;
|
|
||||||
|
|
||||||
await OnMessage.Invoke(message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await Event.On<User>($"supportChat.{Recipient.Id}.typing", this, async user =>
|
|
||||||
{
|
|
||||||
await HandleTyping(user);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<SupportChatMessage[]> GetMessages()
|
|
||||||
{
|
|
||||||
if (User == null)
|
|
||||||
return Array.Empty<SupportChatMessage>();
|
|
||||||
|
|
||||||
return await ServerService.GetMessages(Recipient);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<SupportChatMessage> SendMessage(string content)
|
|
||||||
{
|
|
||||||
if (User != null)
|
|
||||||
{
|
|
||||||
return await ServerService.SendMessage(Recipient, content, User);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task HandleTyping(User user)
|
|
||||||
{
|
|
||||||
lock (TypingUsers)
|
|
||||||
{
|
|
||||||
if (!TypingUsers.Contains(user))
|
|
||||||
{
|
|
||||||
TypingUsers.Add(user);
|
|
||||||
|
|
||||||
if (OnTypingChanged != null)
|
|
||||||
{
|
|
||||||
OnTypingChanged.Invoke(
|
|
||||||
TypingUsers
|
|
||||||
.Where(x => x.Id != User!.Id)
|
|
||||||
.Select(x => $"{x.FirstName} {x.LastName}")
|
|
||||||
.ToArray()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
|
||||||
|
|
||||||
if (TypingUsers.Contains(user))
|
|
||||||
{
|
|
||||||
TypingUsers.Remove(user);
|
|
||||||
|
|
||||||
if (OnTypingChanged != null)
|
|
||||||
{
|
|
||||||
await OnTypingChanged.Invoke(
|
|
||||||
TypingUsers
|
|
||||||
.Where(x => x.Id != User!.Id)
|
|
||||||
.Select(x => $"{x.FirstName} {x.LastName}")
|
|
||||||
.ToArray()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SendTyping()
|
|
||||||
{
|
|
||||||
await Event.Emit($"supportChat.{Recipient.Id}.typing", User);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Close()
|
|
||||||
{
|
|
||||||
await ServerService.CloseChat(Recipient);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void Dispose()
|
|
||||||
{
|
|
||||||
if (User != null)
|
|
||||||
{
|
|
||||||
await Event.Off($"supportChat.{Recipient.Id}.message", this);
|
|
||||||
await Event.Off($"supportChat.{Recipient.Id}.typing", this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
using Logging.Net;
|
|
||||||
using Moonlight.App.Database.Entities;
|
|
||||||
using Moonlight.App.Events;
|
|
||||||
using Moonlight.App.Services.Sessions;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Services.SupportChat;
|
|
||||||
|
|
||||||
public class SupportChatClientService : IDisposable
|
|
||||||
{
|
|
||||||
private readonly EventSystem Event;
|
|
||||||
private readonly IdentityService IdentityService;
|
|
||||||
private readonly SupportChatServerService ServerService;
|
|
||||||
|
|
||||||
public Func<SupportChatMessage, Task>? OnMessage { get; set; }
|
|
||||||
public Func<string[], Task>? OnTypingChanged { get; set; }
|
|
||||||
|
|
||||||
private User? User;
|
|
||||||
private readonly List<User> TypingUsers = new();
|
|
||||||
|
|
||||||
public SupportChatClientService(
|
|
||||||
EventSystem eventSystem,
|
|
||||||
SupportChatServerService serverService,
|
|
||||||
IdentityService identityService)
|
|
||||||
{
|
|
||||||
Event = eventSystem;
|
|
||||||
ServerService = serverService;
|
|
||||||
IdentityService = identityService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Start()
|
|
||||||
{
|
|
||||||
User = await IdentityService.Get();
|
|
||||||
|
|
||||||
if (User != null)
|
|
||||||
{
|
|
||||||
await Event.On<SupportChatMessage>($"supportChat.{User.Id}.message", this, async message =>
|
|
||||||
{
|
|
||||||
if (OnMessage != null)
|
|
||||||
{
|
|
||||||
if(message.Sender != null && message.Sender.Id == User.Id)
|
|
||||||
return;
|
|
||||||
|
|
||||||
await OnMessage.Invoke(message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await Event.On<User>($"supportChat.{User.Id}.typing", this, async user =>
|
|
||||||
{
|
|
||||||
await HandleTyping(user);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<SupportChatMessage[]> GetMessages()
|
|
||||||
{
|
|
||||||
if (User == null)
|
|
||||||
return Array.Empty<SupportChatMessage>();
|
|
||||||
|
|
||||||
return await ServerService.GetMessages(User);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<SupportChatMessage> SendMessage(string content)
|
|
||||||
{
|
|
||||||
if (User != null)
|
|
||||||
{
|
|
||||||
return await ServerService.SendMessage(User, content, User);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null!;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task HandleTyping(User user)
|
|
||||||
{
|
|
||||||
lock (TypingUsers)
|
|
||||||
{
|
|
||||||
if (!TypingUsers.Contains(user))
|
|
||||||
{
|
|
||||||
TypingUsers.Add(user);
|
|
||||||
|
|
||||||
if (OnTypingChanged != null)
|
|
||||||
{
|
|
||||||
OnTypingChanged.Invoke(
|
|
||||||
TypingUsers
|
|
||||||
.Where(x => x.Id != User!.Id)
|
|
||||||
.Select(x => $"{x.FirstName} {x.LastName}")
|
|
||||||
.ToArray()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
|
||||||
|
|
||||||
if (TypingUsers.Contains(user))
|
|
||||||
{
|
|
||||||
TypingUsers.Remove(user);
|
|
||||||
|
|
||||||
if (OnTypingChanged != null)
|
|
||||||
{
|
|
||||||
await OnTypingChanged.Invoke(
|
|
||||||
TypingUsers
|
|
||||||
.Where(x => x.Id != User!.Id)
|
|
||||||
.Select(x => $"{x.FirstName} {x.LastName}")
|
|
||||||
.ToArray()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SendTyping()
|
|
||||||
{
|
|
||||||
await Event.Emit($"supportChat.{User!.Id}.typing", User);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void Dispose()
|
|
||||||
{
|
|
||||||
if (User != null)
|
|
||||||
{
|
|
||||||
await Event.Off($"supportChat.{User.Id}.message", this);
|
|
||||||
await Event.Off($"supportChat.{User.Id}.typing", this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Moonlight.App.Database.Entities;
|
|
||||||
using Moonlight.App.Events;
|
|
||||||
using Moonlight.App.Repositories;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Services.SupportChat;
|
|
||||||
|
|
||||||
public class SupportChatServerService
|
|
||||||
{
|
|
||||||
private readonly IServiceScopeFactory ServiceScopeFactory;
|
|
||||||
private readonly DateTimeService DateTimeService;
|
|
||||||
private readonly EventSystem Event;
|
|
||||||
|
|
||||||
public SupportChatServerService(
|
|
||||||
IServiceScopeFactory serviceScopeFactory,
|
|
||||||
DateTimeService dateTimeService,
|
|
||||||
EventSystem eventSystem)
|
|
||||||
{
|
|
||||||
ServiceScopeFactory = serviceScopeFactory;
|
|
||||||
DateTimeService = dateTimeService;
|
|
||||||
Event = eventSystem;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<SupportChatMessage[]> GetMessages(User recipient)
|
|
||||||
{
|
|
||||||
using var scope = ServiceScopeFactory.CreateScope();
|
|
||||||
var msgRepo = scope.ServiceProvider.GetRequiredService<Repository<SupportChatMessage>>();
|
|
||||||
|
|
||||||
var messages = msgRepo
|
|
||||||
.Get()
|
|
||||||
.Include(x => x.Recipient)
|
|
||||||
.Include(x => x.Sender)
|
|
||||||
.Where(x => x.Recipient.Id == recipient.Id)
|
|
||||||
.OrderByDescending(x => x.CreatedAt)
|
|
||||||
.AsEnumerable()
|
|
||||||
.Take(50)
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
return Task.FromResult(messages);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<SupportChatMessage> SendMessage(User recipient, string content, User? sender, string? attachment = null)
|
|
||||||
{
|
|
||||||
using var scope = ServiceScopeFactory.CreateScope();
|
|
||||||
var msgRepo = scope.ServiceProvider.GetRequiredService<Repository<SupportChatMessage>>();
|
|
||||||
var userRepo = scope.ServiceProvider.GetRequiredService<Repository<User>>();
|
|
||||||
|
|
||||||
var message = new SupportChatMessage()
|
|
||||||
{
|
|
||||||
CreatedAt = DateTimeService.GetCurrent(),
|
|
||||||
IsQuestion = false,
|
|
||||||
Sender = sender == null ? null : userRepo.Get().First(x => x.Id == sender.Id),
|
|
||||||
Recipient = userRepo.Get().First(x => x.Id == recipient.Id),
|
|
||||||
Answer = "",
|
|
||||||
Attachment = attachment ?? "",
|
|
||||||
Content = content,
|
|
||||||
UpdatedAt = DateTimeService.GetCurrent()
|
|
||||||
};
|
|
||||||
|
|
||||||
var finalMessage = msgRepo.Add(message);
|
|
||||||
|
|
||||||
await Event.Emit($"supportChat.{recipient.Id}.message", finalMessage);
|
|
||||||
await Event.Emit("supportChat.message", finalMessage);
|
|
||||||
|
|
||||||
if (!userRepo.Get().First(x => x.Id == recipient.Id).SupportPending)
|
|
||||||
{
|
|
||||||
var ticketStart = new SupportChatMessage()
|
|
||||||
{
|
|
||||||
CreatedAt = DateTimeService.GetCurrent(),
|
|
||||||
IsQuestion = false,
|
|
||||||
Sender = null,
|
|
||||||
Recipient = userRepo.Get().First(x => x.Id == recipient.Id),
|
|
||||||
Answer = "",
|
|
||||||
Attachment = "",
|
|
||||||
Content = "Support ticket open", //TODO: Config
|
|
||||||
UpdatedAt = DateTimeService.GetCurrent()
|
|
||||||
};
|
|
||||||
|
|
||||||
var ticketStartFinal = msgRepo.Add(ticketStart);
|
|
||||||
|
|
||||||
var user = userRepo.Get().First(x => x.Id == recipient.Id);
|
|
||||||
user.SupportPending = true;
|
|
||||||
userRepo.Update(user);
|
|
||||||
|
|
||||||
await Event.Emit($"supportChat.{recipient.Id}.message", ticketStartFinal);
|
|
||||||
await Event.Emit("supportChat.message", ticketStartFinal);
|
|
||||||
await Event.Emit("supportChat.new", recipient);
|
|
||||||
}
|
|
||||||
|
|
||||||
return finalMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<Dictionary<User, SupportChatMessage?>> GetOpenChats()
|
|
||||||
{
|
|
||||||
var result = new Dictionary<User, SupportChatMessage?>();
|
|
||||||
|
|
||||||
using var scope = ServiceScopeFactory.CreateScope();
|
|
||||||
var userRepo = scope.ServiceProvider.GetRequiredService<Repository<User>>();
|
|
||||||
var msgRepo = scope.ServiceProvider.GetRequiredService<Repository<SupportChatMessage>>();
|
|
||||||
|
|
||||||
foreach (var user in userRepo.Get().Where(x => x.SupportPending).ToArray())
|
|
||||||
{
|
|
||||||
var lastMessage = msgRepo
|
|
||||||
.Get()
|
|
||||||
.Include(x => x.Recipient)
|
|
||||||
.Include(x => x.Sender)
|
|
||||||
.Where(x => x.Recipient.Id == user.Id)
|
|
||||||
.OrderByDescending(x => x.CreatedAt)
|
|
||||||
.AsEnumerable()
|
|
||||||
.FirstOrDefault();
|
|
||||||
|
|
||||||
result.Add(user, lastMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.FromResult(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task CloseChat(User recipient)
|
|
||||||
{
|
|
||||||
using var scope = ServiceScopeFactory.CreateScope();
|
|
||||||
var msgRepo = scope.ServiceProvider.GetRequiredService<Repository<SupportChatMessage>>();
|
|
||||||
var userRepo = scope.ServiceProvider.GetRequiredService<Repository<User>>();
|
|
||||||
|
|
||||||
var ticketEnd = new SupportChatMessage()
|
|
||||||
{
|
|
||||||
CreatedAt = DateTimeService.GetCurrent(),
|
|
||||||
IsQuestion = false,
|
|
||||||
Sender = null,
|
|
||||||
Recipient = userRepo.Get().First(x => x.Id == recipient.Id),
|
|
||||||
Answer = "",
|
|
||||||
Attachment = "",
|
|
||||||
Content = "Support ticket closed", //TODO: Config
|
|
||||||
UpdatedAt = DateTimeService.GetCurrent()
|
|
||||||
};
|
|
||||||
|
|
||||||
var ticketEndFinal = msgRepo.Add(ticketEnd);
|
|
||||||
|
|
||||||
var user = userRepo.Get().First(x => x.Id == recipient.Id);
|
|
||||||
user.SupportPending = false;
|
|
||||||
userRepo.Update(user);
|
|
||||||
|
|
||||||
await Event.Emit($"supportChat.{recipient.Id}.message", ticketEndFinal);
|
|
||||||
await Event.Emit("supportChat.message", ticketEndFinal);
|
|
||||||
await Event.Emit("supportChat.close", recipient);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,7 +19,6 @@ public class UserService
|
|||||||
private readonly MailService MailService;
|
private readonly MailService MailService;
|
||||||
private readonly IdentityService IdentityService;
|
private readonly IdentityService IdentityService;
|
||||||
private readonly IpLocateService IpLocateService;
|
private readonly IpLocateService IpLocateService;
|
||||||
private readonly DateTimeService DateTimeService;
|
|
||||||
|
|
||||||
private readonly string JwtSecret;
|
private readonly string JwtSecret;
|
||||||
|
|
||||||
@@ -30,9 +29,7 @@ public class UserService
|
|||||||
SecurityLogService securityLogService,
|
SecurityLogService securityLogService,
|
||||||
AuditLogService auditLogService,
|
AuditLogService auditLogService,
|
||||||
MailService mailService,
|
MailService mailService,
|
||||||
IdentityService identityService,
|
IdentityService identityService, IpLocateService ipLocateService)
|
||||||
IpLocateService ipLocateService,
|
|
||||||
DateTimeService dateTimeService)
|
|
||||||
{
|
{
|
||||||
UserRepository = userRepository;
|
UserRepository = userRepository;
|
||||||
TotpService = totpService;
|
TotpService = totpService;
|
||||||
@@ -41,7 +38,6 @@ public class UserService
|
|||||||
MailService = mailService;
|
MailService = mailService;
|
||||||
IdentityService = identityService;
|
IdentityService = identityService;
|
||||||
IpLocateService = ipLocateService;
|
IpLocateService = ipLocateService;
|
||||||
DateTimeService = dateTimeService;
|
|
||||||
|
|
||||||
JwtSecret = configService
|
JwtSecret = configService
|
||||||
.GetSection("Moonlight")
|
.GetSection("Moonlight")
|
||||||
@@ -74,12 +70,12 @@ public class UserService
|
|||||||
LastName = lastname,
|
LastName = lastname,
|
||||||
State = "",
|
State = "",
|
||||||
Status = UserStatus.Unverified,
|
Status = UserStatus.Unverified,
|
||||||
CreatedAt = DateTimeService.GetCurrent(),
|
CreatedAt = DateTime.UtcNow,
|
||||||
DiscordId = 0,
|
DiscordId = 0,
|
||||||
TotpEnabled = false,
|
TotpEnabled = false,
|
||||||
TotpSecret = "",
|
TotpSecret = "",
|
||||||
UpdatedAt = DateTimeService.GetCurrent(),
|
UpdatedAt = DateTime.UtcNow,
|
||||||
TokenValidTime = DateTimeService.GetCurrent().AddDays(-5)
|
TokenValidTime = DateTime.Now.AddDays(-5)
|
||||||
});
|
});
|
||||||
|
|
||||||
await MailService.SendMail(user!, "register", values => {});
|
await MailService.SendMail(user!, "register", values => {});
|
||||||
@@ -172,7 +168,7 @@ public class UserService
|
|||||||
public async Task ChangePassword(User user, string password, bool isSystemAction = false)
|
public async Task ChangePassword(User user, string password, bool isSystemAction = false)
|
||||||
{
|
{
|
||||||
user.Password = BCrypt.Net.BCrypt.HashPassword(password);
|
user.Password = BCrypt.Net.BCrypt.HashPassword(password);
|
||||||
user.TokenValidTime = DateTimeService.GetCurrent();
|
user.TokenValidTime = DateTime.Now;
|
||||||
UserRepository.Update(user);
|
UserRepository.Update(user);
|
||||||
|
|
||||||
if (isSystemAction)
|
if (isSystemAction)
|
||||||
@@ -248,8 +244,8 @@ public class UserService
|
|||||||
var token = JwtBuilder.Create()
|
var token = JwtBuilder.Create()
|
||||||
.WithAlgorithm(new HMACSHA256Algorithm())
|
.WithAlgorithm(new HMACSHA256Algorithm())
|
||||||
.WithSecret(JwtSecret)
|
.WithSecret(JwtSecret)
|
||||||
.AddClaim("exp", new DateTimeOffset(DateTimeService.GetCurrent().AddDays(10)).ToUnixTimeSeconds())
|
.AddClaim("exp", DateTimeOffset.UtcNow.AddDays(10).ToUnixTimeSeconds())
|
||||||
.AddClaim("iat", DateTimeService.GetCurrentUnixSeconds())
|
.AddClaim("iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds())
|
||||||
.AddClaim("userid", user.Id)
|
.AddClaim("userid", user.Id)
|
||||||
.Encode();
|
.Encode();
|
||||||
|
|
||||||
|
|||||||
@@ -1,191 +0,0 @@
|
|||||||
using Logging.Net;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Moonlight.App.ApiClients.CloudPanel;
|
|
||||||
using Moonlight.App.ApiClients.CloudPanel.Requests;
|
|
||||||
using Moonlight.App.Database.Entities;
|
|
||||||
using Moonlight.App.Exceptions;
|
|
||||||
using Moonlight.App.Helpers;
|
|
||||||
using Moonlight.App.Helpers.Files;
|
|
||||||
using Moonlight.App.Models.Plesk.Requests;
|
|
||||||
using Moonlight.App.Models.Plesk.Resources;
|
|
||||||
using Moonlight.App.Repositories;
|
|
||||||
using FileAccess = Moonlight.App.Helpers.Files.FileAccess;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Services;
|
|
||||||
|
|
||||||
public class WebSpaceService
|
|
||||||
{
|
|
||||||
private readonly Repository<CloudPanel> CloudPanelRepository;
|
|
||||||
private readonly Repository<WebSpace> WebSpaceRepository;
|
|
||||||
private readonly Repository<MySqlDatabase> DatabaseRepository;
|
|
||||||
|
|
||||||
private readonly CloudPanelApiHelper CloudPanelApiHelper;
|
|
||||||
|
|
||||||
public WebSpaceService(Repository<CloudPanel> cloudPanelRepository, Repository<WebSpace> webSpaceRepository, CloudPanelApiHelper cloudPanelApiHelper, Repository<MySqlDatabase> databaseRepository)
|
|
||||||
{
|
|
||||||
CloudPanelRepository = cloudPanelRepository;
|
|
||||||
WebSpaceRepository = webSpaceRepository;
|
|
||||||
CloudPanelApiHelper = cloudPanelApiHelper;
|
|
||||||
DatabaseRepository = databaseRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<WebSpace> Create(string domain, User owner, CloudPanel? ps = null)
|
|
||||||
{
|
|
||||||
if (WebSpaceRepository.Get().Any(x => x.Domain == domain))
|
|
||||||
throw new DisplayException("A website with this domain does already exist");
|
|
||||||
|
|
||||||
var cloudPanel = ps ?? CloudPanelRepository.Get().First();
|
|
||||||
|
|
||||||
var ftpLogin = domain.Replace(".", "_");
|
|
||||||
var ftpPassword = StringHelper.GenerateString(16);
|
|
||||||
|
|
||||||
var phpVersion = "8.1"; // TODO: Add config option or smth
|
|
||||||
|
|
||||||
var w = new WebSpace()
|
|
||||||
{
|
|
||||||
CloudPanel = cloudPanel,
|
|
||||||
Owner = owner,
|
|
||||||
Domain = domain,
|
|
||||||
UserName = ftpLogin,
|
|
||||||
Password = ftpPassword,
|
|
||||||
VHostTemplate = "Generic" //TODO: Implement as select option
|
|
||||||
};
|
|
||||||
|
|
||||||
var webSpace = WebSpaceRepository.Add(w);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await CloudPanelApiHelper.Post(cloudPanel, "site/php", new AddPhpSite()
|
|
||||||
{
|
|
||||||
VHostTemplate = w.VHostTemplate,
|
|
||||||
DomainName = w.Domain,
|
|
||||||
PhpVersion = phpVersion,
|
|
||||||
SiteUser = w.UserName,
|
|
||||||
SiteUserPassword = w.Password
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
WebSpaceRepository.Delete(webSpace);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
return webSpace;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Delete(WebSpace w)
|
|
||||||
{
|
|
||||||
var website = EnsureData(w);
|
|
||||||
|
|
||||||
await CloudPanelApiHelper.Delete(website.CloudPanel, $"site/{website.Domain}", null);
|
|
||||||
|
|
||||||
WebSpaceRepository.Delete(website);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> IsHostUp(CloudPanel cloudPanel)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await CloudPanelApiHelper.Post(cloudPanel, "", null);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (CloudPanelException e)
|
|
||||||
{
|
|
||||||
if (e.StatusCode == 404)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> IsHostUp(WebSpace w)
|
|
||||||
{
|
|
||||||
var webSpace = EnsureData(w);
|
|
||||||
|
|
||||||
return await IsHostUp(webSpace.CloudPanel);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task IssueSslCertificate(WebSpace w)
|
|
||||||
{
|
|
||||||
var webspace = EnsureData(w);
|
|
||||||
|
|
||||||
await CloudPanelApiHelper.Post(webspace.CloudPanel, "letsencrypt/install/certificate", new InstallLetsEncrypt()
|
|
||||||
{
|
|
||||||
DomainName = webspace.Domain
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Databases
|
|
||||||
|
|
||||||
public Task<MySqlDatabase[]> GetDatabases(WebSpace w)
|
|
||||||
{
|
|
||||||
return Task.FromResult(WebSpaceRepository
|
|
||||||
.Get()
|
|
||||||
.Include(x => x.Databases)
|
|
||||||
.First(x => x.Id == w.Id)
|
|
||||||
.Databases.ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task CreateDatabase(WebSpace w, string name, string password)
|
|
||||||
{
|
|
||||||
if (DatabaseRepository.Get().Any(x => x.UserName == name))
|
|
||||||
throw new DisplayException("A database with this name does already exist");
|
|
||||||
|
|
||||||
var webspace = EnsureData(w);
|
|
||||||
|
|
||||||
var database = new MySqlDatabase()
|
|
||||||
{
|
|
||||||
UserName = name,
|
|
||||||
Password = password
|
|
||||||
};
|
|
||||||
|
|
||||||
await CloudPanelApiHelper.Post(webspace.CloudPanel, "db", new AddDatabase()
|
|
||||||
{
|
|
||||||
DomainName = webspace.Domain,
|
|
||||||
DatabaseName = database.UserName,
|
|
||||||
DatabaseUserName = database.UserName,
|
|
||||||
DatabaseUserPassword = database.Password
|
|
||||||
});
|
|
||||||
|
|
||||||
webspace.Databases.Add(database);
|
|
||||||
WebSpaceRepository.Update(webspace);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DeleteDatabase(WebSpace w, MySqlDatabase database)
|
|
||||||
{
|
|
||||||
var webspace = EnsureData(w);
|
|
||||||
|
|
||||||
await CloudPanelApiHelper.Delete(webspace.CloudPanel, $"db/{database.UserName}", null);
|
|
||||||
|
|
||||||
webspace.Databases.Remove(database);
|
|
||||||
WebSpaceRepository.Update(webspace);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public Task<FileAccess> CreateFileAccess(WebSpace w)
|
|
||||||
{
|
|
||||||
var webspace = EnsureData(w);
|
|
||||||
|
|
||||||
return Task.FromResult<FileAccess>(
|
|
||||||
new SftpFileAccess(webspace.CloudPanel.Host, webspace.UserName, webspace.Password, 22, true)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private WebSpace EnsureData(WebSpace webSpace)
|
|
||||||
{
|
|
||||||
if (webSpace.CloudPanel == null || webSpace.Owner == null)
|
|
||||||
return WebSpaceRepository
|
|
||||||
.Get()
|
|
||||||
.Include(x => x.CloudPanel)
|
|
||||||
.Include(x => x.Owner)
|
|
||||||
.First(x => x.Id == webSpace.Id);
|
|
||||||
|
|
||||||
return webSpace;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
383
Moonlight/App/Services/WebsiteService.cs
Normal file
383
Moonlight/App/Services/WebsiteService.cs
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
using Logging.Net;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Moonlight.App.Database.Entities;
|
||||||
|
using Moonlight.App.Exceptions;
|
||||||
|
using Moonlight.App.Helpers;
|
||||||
|
using Moonlight.App.Helpers.Files;
|
||||||
|
using Moonlight.App.Models.Plesk.Requests;
|
||||||
|
using Moonlight.App.Models.Plesk.Resources;
|
||||||
|
using Moonlight.App.Repositories;
|
||||||
|
using FileAccess = Moonlight.App.Helpers.Files.FileAccess;
|
||||||
|
|
||||||
|
namespace Moonlight.App.Services;
|
||||||
|
|
||||||
|
public class WebsiteService
|
||||||
|
{
|
||||||
|
private readonly WebsiteRepository WebsiteRepository;
|
||||||
|
private readonly PleskServerRepository PleskServerRepository;
|
||||||
|
private readonly PleskApiHelper PleskApiHelper;
|
||||||
|
private readonly UserRepository UserRepository;
|
||||||
|
|
||||||
|
public WebsiteService(WebsiteRepository websiteRepository, PleskApiHelper pleskApiHelper, PleskServerRepository pleskServerRepository, UserRepository userRepository)
|
||||||
|
{
|
||||||
|
WebsiteRepository = websiteRepository;
|
||||||
|
PleskApiHelper = pleskApiHelper;
|
||||||
|
PleskServerRepository = pleskServerRepository;
|
||||||
|
UserRepository = userRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Website> Create(string baseDomain, User owner, PleskServer? ps = null)
|
||||||
|
{
|
||||||
|
if (WebsiteRepository.Get().Any(x => x.BaseDomain == baseDomain))
|
||||||
|
throw new DisplayException("A website with this domain does already exist");
|
||||||
|
|
||||||
|
var pleskServer = ps ?? PleskServerRepository.Get().First();
|
||||||
|
|
||||||
|
var ftpLogin = baseDomain;
|
||||||
|
var ftpPassword = StringHelper.GenerateString(16);
|
||||||
|
|
||||||
|
var w = new Website()
|
||||||
|
{
|
||||||
|
PleskServer = pleskServer,
|
||||||
|
Owner = owner,
|
||||||
|
BaseDomain = baseDomain,
|
||||||
|
PleskId = 0,
|
||||||
|
FtpPassword = ftpPassword,
|
||||||
|
FtpLogin = ftpLogin
|
||||||
|
};
|
||||||
|
|
||||||
|
var website = WebsiteRepository.Add(w);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var id = await GetAdminAccount(pleskServer);
|
||||||
|
|
||||||
|
var result = await PleskApiHelper.Post<CreateResult>(pleskServer, "domains", new CreateDomain()
|
||||||
|
{
|
||||||
|
Description = $"moonlight website {website.Id}",
|
||||||
|
Name = baseDomain,
|
||||||
|
HostingType = "virtual",
|
||||||
|
Plan = new()
|
||||||
|
{
|
||||||
|
Name = "Unlimited"
|
||||||
|
},
|
||||||
|
HostingSettings = new()
|
||||||
|
{
|
||||||
|
FtpLogin = ftpLogin,
|
||||||
|
FtpPassword = ftpPassword
|
||||||
|
},
|
||||||
|
OwnerClient = new()
|
||||||
|
{
|
||||||
|
Id = id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
website.PleskId = result.Id;
|
||||||
|
|
||||||
|
WebsiteRepository.Update(website);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
WebsiteRepository.Delete(website);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return website;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Delete(Website w)
|
||||||
|
{
|
||||||
|
var website = EnsureData(w);
|
||||||
|
|
||||||
|
await PleskApiHelper.Delete(website.PleskServer, $"domains/{w.PleskId}", null);
|
||||||
|
|
||||||
|
WebsiteRepository.Delete(website);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> IsHostUp(PleskServer pleskServer)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var res = await PleskApiHelper.Get<ServerStatus>(pleskServer, "server");
|
||||||
|
|
||||||
|
if (res != null)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> IsHostUp(Website w)
|
||||||
|
{
|
||||||
|
var website = EnsureData(w);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var res = await PleskApiHelper.Get<ServerStatus>(website.PleskServer, "server");
|
||||||
|
|
||||||
|
if (res != null)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Get host
|
||||||
|
|
||||||
|
public async Task<string> GetHost(PleskServer pleskServer)
|
||||||
|
{
|
||||||
|
return (await PleskApiHelper.Get<ServerStatus>(pleskServer, "server")).Hostname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> GetHost(Website w)
|
||||||
|
{
|
||||||
|
var website = EnsureData(w);
|
||||||
|
|
||||||
|
return await GetHost(website.PleskServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private async Task<int> GetAdminAccount(PleskServer pleskServer)
|
||||||
|
{
|
||||||
|
var users = await PleskApiHelper.Get<Client[]>(pleskServer, "clients");
|
||||||
|
|
||||||
|
var user = users.FirstOrDefault(x => x.Type == "admin");
|
||||||
|
|
||||||
|
if (user == null)
|
||||||
|
throw new DisplayException("No admin account in plesk found");
|
||||||
|
|
||||||
|
return user.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region SSL
|
||||||
|
public async Task<string[]> GetSslCertificates(Website w)
|
||||||
|
{
|
||||||
|
var website = EnsureData(w);
|
||||||
|
var certs = new List<string>();
|
||||||
|
|
||||||
|
var data = await ExecuteCli(website.PleskServer, "certificate", p =>
|
||||||
|
{
|
||||||
|
p.Add("-l");
|
||||||
|
p.Add("-domain");
|
||||||
|
p.Add(w.BaseDomain);
|
||||||
|
});
|
||||||
|
|
||||||
|
string[] lines = data.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
foreach (string line in lines)
|
||||||
|
{
|
||||||
|
if (line.Contains("Lets Encrypt"))
|
||||||
|
{
|
||||||
|
string[] parts = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
if(parts.Length > 6)
|
||||||
|
certs.Add($"{parts[4]} {parts[5]} {parts[6]}");
|
||||||
|
}
|
||||||
|
else if (line.Contains("Listing of SSL/TLS certificates repository was successful"))
|
||||||
|
{
|
||||||
|
// This line indicates the end of the certificate listing, so we can stop parsing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return certs.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CreateSslCertificate(Website w)
|
||||||
|
{
|
||||||
|
var website = EnsureData(w);
|
||||||
|
|
||||||
|
await ExecuteCli(website.PleskServer, "extension", p =>
|
||||||
|
{
|
||||||
|
p.Add("--exec");
|
||||||
|
p.Add("letsencrypt");
|
||||||
|
p.Add("cli.php");
|
||||||
|
p.Add("-d");
|
||||||
|
p.Add(website.BaseDomain);
|
||||||
|
p.Add("-m");
|
||||||
|
p.Add(website.Owner.Email);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteSslCertificate(Website w, string name)
|
||||||
|
{
|
||||||
|
var website = EnsureData(w);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ExecuteCli(website.PleskServer, "site", p =>
|
||||||
|
{
|
||||||
|
p.Add("-u");
|
||||||
|
p.Add(website.BaseDomain);
|
||||||
|
p.Add("-ssl");
|
||||||
|
p.Add("false");
|
||||||
|
});
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ExecuteCli(website.PleskServer, "certificate", p =>
|
||||||
|
{
|
||||||
|
p.Add("--remove");
|
||||||
|
p.Add(name);
|
||||||
|
p.Add("-domain");
|
||||||
|
p.Add(website.BaseDomain);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Warn("Error removing ssl certificate");
|
||||||
|
Logger.Warn(e);
|
||||||
|
|
||||||
|
throw new DisplayException("An unknown error occured while removing ssl certificate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (DisplayException)
|
||||||
|
{
|
||||||
|
// Redirect all display exception to soft error handler
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Warn("Error disabling ssl certificate");
|
||||||
|
Logger.Warn(e);
|
||||||
|
|
||||||
|
throw new DisplayException("An unknown error occured while disabling ssl certificate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Databases
|
||||||
|
|
||||||
|
public async Task<Models.Plesk.Resources.Database[]> GetDatabases(Website w)
|
||||||
|
{
|
||||||
|
var website = EnsureData(w);
|
||||||
|
|
||||||
|
var dbs = await PleskApiHelper.Get<Models.Plesk.Resources.Database[]>(
|
||||||
|
website.PleskServer,
|
||||||
|
$"databases?domain={w.BaseDomain}"
|
||||||
|
);
|
||||||
|
|
||||||
|
return dbs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CreateDatabase(Website w, string name, string password)
|
||||||
|
{
|
||||||
|
var website = EnsureData(w);
|
||||||
|
|
||||||
|
var server = await GetDefaultDatabaseServer(website);
|
||||||
|
|
||||||
|
if (server == null)
|
||||||
|
throw new DisplayException("No database server marked as default found");
|
||||||
|
|
||||||
|
var dbReq = new CreateDatabase()
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
Type = "mysql",
|
||||||
|
ParentDomain = new()
|
||||||
|
{
|
||||||
|
Name = website.BaseDomain
|
||||||
|
},
|
||||||
|
ServerId = server.Id
|
||||||
|
};
|
||||||
|
|
||||||
|
var db = await PleskApiHelper.Post<Models.Plesk.Resources.Database>(website.PleskServer, "databases", dbReq);
|
||||||
|
|
||||||
|
if (db == null)
|
||||||
|
throw new DisplayException("Unable to create database via api");
|
||||||
|
|
||||||
|
var dbUserReq = new CreateDatabaseUser()
|
||||||
|
{
|
||||||
|
DatabaseId = db.Id,
|
||||||
|
Login = name,
|
||||||
|
Password = password
|
||||||
|
};
|
||||||
|
|
||||||
|
await PleskApiHelper.Post(website.PleskServer, "dbusers", dbUserReq);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteDatabase(Website w, Models.Plesk.Resources.Database database)
|
||||||
|
{
|
||||||
|
var website = EnsureData(w);
|
||||||
|
|
||||||
|
var dbUsers = await PleskApiHelper.Get<DatabaseUser[]>(
|
||||||
|
website.PleskServer,
|
||||||
|
$"dbusers?dbId={database.Id}"
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach (var dbUser in dbUsers)
|
||||||
|
{
|
||||||
|
await PleskApiHelper.Delete(website.PleskServer, $"dbusers/{dbUser.Id}", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
await PleskApiHelper.Delete(website.PleskServer, $"databases/{database.Id}", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DatabaseServer?> GetDefaultDatabaseServer(PleskServer pleskServer)
|
||||||
|
{
|
||||||
|
var dbServers = await PleskApiHelper.Get<DatabaseServer[]>(pleskServer, "dbservers");
|
||||||
|
|
||||||
|
return dbServers.FirstOrDefault(x => x.IsDefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DatabaseServer?> GetDefaultDatabaseServer(Website w)
|
||||||
|
{
|
||||||
|
var website = EnsureData(w);
|
||||||
|
|
||||||
|
return await GetDefaultDatabaseServer(website.PleskServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public async Task<FileAccess> CreateFileAccess(Website w)
|
||||||
|
{
|
||||||
|
var website = EnsureData(w);
|
||||||
|
var host = await GetHost(website.PleskServer);
|
||||||
|
|
||||||
|
return new FtpFileAccess(host, 21, website.FtpLogin, website.FtpPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> ExecuteCli(
|
||||||
|
PleskServer server,
|
||||||
|
string cli, Action<List<string>>? parameters = null,
|
||||||
|
Action<Dictionary<string, string>>? variables = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var p = new List<string>();
|
||||||
|
var v = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
parameters?.Invoke(p);
|
||||||
|
variables?.Invoke(v);
|
||||||
|
|
||||||
|
var req = new CliCall()
|
||||||
|
{
|
||||||
|
Env = v,
|
||||||
|
Params = p
|
||||||
|
};
|
||||||
|
|
||||||
|
var res = await PleskApiHelper.Post<CliResult>(server, $"cli/{cli}/call", req);
|
||||||
|
|
||||||
|
return res.Stdout;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Website EnsureData(Website website)
|
||||||
|
{
|
||||||
|
if (website.PleskServer == null || website.Owner == null)
|
||||||
|
return WebsiteRepository
|
||||||
|
.Get()
|
||||||
|
.Include(x => x.PleskServer)
|
||||||
|
.Include(x => x.Owner)
|
||||||
|
.First(x => x.Id == website.Id);
|
||||||
|
|
||||||
|
return website;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,12 +19,10 @@
|
|||||||
<PackageReference Include="CloudFlare.Client" Version="6.1.4" />
|
<PackageReference Include="CloudFlare.Client" Version="6.1.4" />
|
||||||
<PackageReference Include="CurrieTechnologies.Razor.SweetAlert2" Version="5.4.0" />
|
<PackageReference Include="CurrieTechnologies.Razor.SweetAlert2" Version="5.4.0" />
|
||||||
<PackageReference Include="Discord.Net" Version="3.10.0" />
|
<PackageReference Include="Discord.Net" Version="3.10.0" />
|
||||||
<PackageReference Include="Discord.Net.Webhook" Version="3.10.0" />
|
|
||||||
<PackageReference Include="FluentFTP" Version="46.0.2" />
|
<PackageReference Include="FluentFTP" Version="46.0.2" />
|
||||||
<PackageReference Include="GravatarSharp.Core" Version="1.0.1.2" />
|
<PackageReference Include="GravatarSharp.Core" Version="1.0.1.2" />
|
||||||
<PackageReference Include="JWT" Version="10.0.2" />
|
<PackageReference Include="JWT" Version="10.0.2" />
|
||||||
<PackageReference Include="Logging.Net" Version="1.1.0" />
|
<PackageReference Include="Logging.Net" Version="1.1.0" />
|
||||||
<PackageReference Include="MailKit" Version="4.0.0" />
|
|
||||||
<PackageReference Include="Mappy.Net" Version="1.0.2" />
|
<PackageReference Include="Mappy.Net" Version="1.0.2" />
|
||||||
<PackageReference Include="Markdig" Version="0.31.0" />
|
<PackageReference Include="Markdig" Version="0.31.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.3">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.3">
|
||||||
@@ -43,7 +41,6 @@
|
|||||||
<PackageReference Include="PteroConsole.NET" Version="1.0.4" />
|
<PackageReference Include="PteroConsole.NET" Version="1.0.4" />
|
||||||
<PackageReference Include="QRCoder" Version="1.4.3" />
|
<PackageReference Include="QRCoder" Version="1.4.3" />
|
||||||
<PackageReference Include="RestSharp" Version="109.0.0-preview.1" />
|
<PackageReference Include="RestSharp" Version="109.0.0-preview.1" />
|
||||||
<PackageReference Include="SSH.NET" Version="2020.0.2" />
|
|
||||||
<PackageReference Include="UAParser" Version="3.1.47" />
|
<PackageReference Include="UAParser" Version="3.1.47" />
|
||||||
<PackageReference Include="XtermBlazor" Version="1.6.1" />
|
<PackageReference Include="XtermBlazor" Version="1.6.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -70,7 +67,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="App\ApiClients\CloudPanel\Resources\" />
|
|
||||||
<Folder Include="App\Http\Middleware" />
|
<Folder Include="App\Http\Middleware" />
|
||||||
<Folder Include="App\Models\Daemon\Requests" />
|
<Folder Include="App\Models\Daemon\Requests" />
|
||||||
<Folder Include="App\Models\Google\Resources" />
|
<Folder Include="App\Models\Google\Resources" />
|
||||||
|
|||||||
@@ -1,54 +1,29 @@
|
|||||||
@page "/"
|
@page "/"
|
||||||
@using Moonlight.App.Services
|
|
||||||
@namespace Moonlight.Pages
|
@namespace Moonlight.Pages
|
||||||
|
|
||||||
@inject SmartTranslateService SmartTranslateService
|
|
||||||
|
|
||||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
@{
|
@{
|
||||||
Layout = "_Layout";
|
Layout = "_Layout";
|
||||||
}
|
}
|
||||||
|
|
||||||
<component type="typeof(BlazorApp)" render-mode="ServerPrerendered"/>
|
<component type="typeof(BlazorApp)" render-mode="ServerPrerendered" />
|
||||||
|
|
||||||
<div id="components-reconnect-modal" class="my-reconnect-modal components-reconnect-hide">
|
<div id="components-reconnect-modal" class="my-reconnect-modal components-reconnect-hide">
|
||||||
<div class="show">
|
<div class="show">
|
||||||
<div class="modal d-block">
|
<p>
|
||||||
<div class="modal-dialog modal-dialog-centered mw-900px">
|
<br/>
|
||||||
<div class="modal-content">
|
Connecting to moonlight servers
|
||||||
<img src="/assets/media/svg/loading.svg" class="card-img-top w-25 mx-auto pt-5" alt="...">
|
</p>
|
||||||
<div class="pt-2 modal-body py-lg-10 px-lg-10">
|
|
||||||
<h2>@(SmartTranslateService.Translate("Your connection has been paused"))</h2>
|
|
||||||
<p class="mt-3 fw-normal fs-6">@(SmartTranslateService.Translate("We paused your connection because of inactivity. The resume just focus the tab and wait a few seconds"))</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="failed">
|
<div class="failed">
|
||||||
<div class="modal d-block">
|
<p>
|
||||||
<div class="modal-dialog modal-dialog-centered mw-900px">
|
<br />
|
||||||
<div class="modal-content">
|
Connection to moonlight servers failed
|
||||||
<img src="/assets/media/svg/serverdown.svg" class="card-img-top w-25 mx-auto pt-5" alt="...">
|
</p>
|
||||||
<div class="pt-2 modal-body py-lg-10 px-lg-10">
|
|
||||||
<h2>@(SmartTranslateService.Translate("Failed to reconnect to the moonlight servers"))</h2>
|
|
||||||
<p class="mt-3 fw-normal fs-6">@(SmartTranslateService.Translate("We were unable to reconnect to moonlight. Please refresh the page"))</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="rejected">
|
<div class="rejected">
|
||||||
<div class="modal d-block">
|
<p>
|
||||||
<div class="modal-dialog modal-dialog-centered mw-900px">
|
<br />
|
||||||
<div class="modal-content">
|
Connection to moonlight servers rejected
|
||||||
<img src="/assets/media/svg/serverdown.svg" class="card-img-top w-25 mx-auto pt-5" alt="...">
|
</p>
|
||||||
<div class="pt-2 modal-body py-lg-10 px-lg-10">
|
|
||||||
<h2>@(SmartTranslateService.Translate("Failed to reconnect to the moonlight servers. The connection has been rejected"))</h2>
|
|
||||||
<p class="mt-3 fw-normal fs-6">@(SmartTranslateService.Translate("We were unable to reconnect to moonlight. Most of the time this is caused by an update of moonlight. Please refresh the page"))</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
@using Microsoft.AspNetCore.Components.Web
|
@using Microsoft.AspNetCore.Components.Web
|
||||||
@using Moonlight.App.Extensions
|
|
||||||
@using Moonlight.App.Repositories
|
|
||||||
@using Moonlight.App.Services
|
@using Moonlight.App.Services
|
||||||
@namespace Moonlight.Pages
|
@namespace Moonlight.Pages
|
||||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
|
|
||||||
@inject ConfigService ConfigService
|
@inject ConfigService ConfigService
|
||||||
@inject LoadingMessageRepository LoadingMessageRepository
|
|
||||||
|
|
||||||
@{
|
@{
|
||||||
var headerConfig = ConfigService
|
var headerConfig = ConfigService
|
||||||
@@ -54,7 +51,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||||
<base href="~/"/>
|
<base href="~/"/>
|
||||||
<component type="typeof(HeadOutlet)" render-mode="ServerPrerendered"/>
|
<component type="typeof(HeadOutlet)" render-mode="ServerPrerendered"/>
|
||||||
|
|
||||||
<title>Loading</title>
|
<title>Loading</title>
|
||||||
</head>
|
</head>
|
||||||
<body
|
<body
|
||||||
@@ -76,13 +73,9 @@
|
|||||||
<div class="app-page-loader flex-column">
|
<div class="app-page-loader flex-column">
|
||||||
<img alt="Logo" src="@(moonlightConfig.GetValue<string>("AppUrl"))/api/moonlight/resources/images/logo.svg" class="h-25px"/>
|
<img alt="Logo" src="@(moonlightConfig.GetValue<string>("AppUrl"))/api/moonlight/resources/images/logo.svg" class="h-25px"/>
|
||||||
|
|
||||||
@{
|
|
||||||
var loadingMessage = LoadingMessageRepository.Get().Random();
|
|
||||||
}
|
|
||||||
|
|
||||||
<div class="d-flex align-items-center mt-5">
|
<div class="d-flex align-items-center mt-5">
|
||||||
<span class="spinner-border text-primary" role="status"></span>
|
<span class="spinner-border text-primary" role="status"></span>
|
||||||
<span class="text-muted fs-6 fw-semibold ms-5">@(loadingMessage.Message)</span>
|
<span class="text-muted fs-6 fw-semibold ms-5">CHANGEME</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -100,18 +93,25 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-search@0.8.2/lib/xterm-addon-search.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-search@0.8.2/lib/xterm-addon-search.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-web-links@0.5.0/lib/xterm-addon-web-links.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-web-links@0.5.0/lib/xterm-addon-web-links.min.js"></script>
|
||||||
|
|
||||||
|
<script src="/assets/js/xtermAddons.js"></script>
|
||||||
|
|
||||||
<script src="/_content/BlazorMonaco/lib/monaco-editor/min/vs/loader.js"></script>
|
<script src="/_content/BlazorMonaco/lib/monaco-editor/min/vs/loader.js"></script>
|
||||||
<script>require.config({ paths: { 'vs': '/_content/BlazorMonaco/lib/monaco-editor/min/vs' } });</script>
|
<script>require.config({ paths: { 'vs': '/_content/BlazorMonaco/lib/monaco-editor/min/vs' } });</script>
|
||||||
<script src="/_content/BlazorMonaco/lib/monaco-editor/min/vs/editor/editor.main.js"></script>
|
<script src="/_content/BlazorMonaco/lib/monaco-editor/min/vs/editor/editor.main.js"></script>
|
||||||
<script src="/_content/BlazorMonaco/jsInterop.js"></script>
|
<script src="/_content/BlazorMonaco/jsInterop.js"></script>
|
||||||
|
<script src="/assets/js/monacoTheme.js"></script>
|
||||||
|
|
||||||
<script src="/assets/js/scripts.bundle.js"></script>
|
<script src="/assets/js/scripts.bundle.js"></script>
|
||||||
|
<script src="/assets/js/flashbang.js"></script>
|
||||||
|
<script src="/assets/js/cookieUtils.js"></script>
|
||||||
|
<script src="/assets/js/clipboard.js"></script>
|
||||||
|
<script src="/assets/js/toastUtils.js"></script>
|
||||||
|
<script src="/assets/js/alertUtils.js"></script>
|
||||||
|
<script src="/assets/js/utils.js"></script>
|
||||||
|
<script src="/assets/js/loggingUtils.js"></script>
|
||||||
|
<script src="/assets/js/snow.js"></script>
|
||||||
|
<script src="/assets/js/recaptcha.js"></script>
|
||||||
<script src="/assets/js/moonlight.js"></script>
|
<script src="/assets/js/moonlight.js"></script>
|
||||||
|
|
||||||
<script>
|
|
||||||
moonlight.loading.registerXterm();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script src="_content/Blazor-ApexCharts/js/apex-charts.min.js"></script>
|
<script src="_content/Blazor-ApexCharts/js/apex-charts.min.js"></script>
|
||||||
<script src="_content/Blazor-ApexCharts/js/blazor-apex-charts.js"></script>
|
<script src="_content/Blazor-ApexCharts/js/blazor-apex-charts.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ using BlazorDownloadFile;
|
|||||||
using BlazorTable;
|
using BlazorTable;
|
||||||
using CurrieTechnologies.Razor.SweetAlert2;
|
using CurrieTechnologies.Razor.SweetAlert2;
|
||||||
using Logging.Net;
|
using Logging.Net;
|
||||||
using Moonlight.App.ApiClients.CloudPanel;
|
|
||||||
using Moonlight.App.Database;
|
using Moonlight.App.Database;
|
||||||
using Moonlight.App.Events;
|
|
||||||
using Moonlight.App.Helpers;
|
using Moonlight.App.Helpers;
|
||||||
using Moonlight.App.LogMigrator;
|
using Moonlight.App.LogMigrator;
|
||||||
using Moonlight.App.Repositories;
|
using Moonlight.App.Repositories;
|
||||||
@@ -19,7 +17,7 @@ using Moonlight.App.Services.Notifications;
|
|||||||
using Moonlight.App.Services.OAuth2;
|
using Moonlight.App.Services.OAuth2;
|
||||||
using Moonlight.App.Services.Sessions;
|
using Moonlight.App.Services.Sessions;
|
||||||
using Moonlight.App.Services.Statistics;
|
using Moonlight.App.Services.Statistics;
|
||||||
using Moonlight.App.Services.SupportChat;
|
using Moonlight.App.Services.Support;
|
||||||
|
|
||||||
namespace Moonlight
|
namespace Moonlight
|
||||||
{
|
{
|
||||||
@@ -43,13 +41,7 @@ namespace Moonlight
|
|||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
builder.Services.AddRazorPages();
|
builder.Services.AddRazorPages();
|
||||||
builder.Services.AddServerSideBlazor()
|
builder.Services.AddServerSideBlazor();
|
||||||
.AddHubOptions(options =>
|
|
||||||
{
|
|
||||||
options.MaximumReceiveMessageSize = 10000000;
|
|
||||||
options.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
|
|
||||||
options.HandshakeTimeout = TimeSpan.FromSeconds(10);
|
|
||||||
});
|
|
||||||
builder.Services.AddHttpContextAccessor();
|
builder.Services.AddHttpContextAccessor();
|
||||||
|
|
||||||
// Databases
|
// Databases
|
||||||
@@ -62,20 +54,23 @@ namespace Moonlight
|
|||||||
builder.Services.AddScoped<ServerRepository>();
|
builder.Services.AddScoped<ServerRepository>();
|
||||||
builder.Services.AddScoped<ServerBackupRepository>();
|
builder.Services.AddScoped<ServerBackupRepository>();
|
||||||
builder.Services.AddScoped<ImageRepository>();
|
builder.Services.AddScoped<ImageRepository>();
|
||||||
|
builder.Services.AddScoped<SupportMessageRepository>();
|
||||||
builder.Services.AddScoped<DomainRepository>();
|
builder.Services.AddScoped<DomainRepository>();
|
||||||
builder.Services.AddScoped<SharedDomainRepository>();
|
builder.Services.AddScoped<SharedDomainRepository>();
|
||||||
builder.Services.AddScoped<RevokeRepository>();
|
builder.Services.AddScoped<RevokeRepository>();
|
||||||
builder.Services.AddScoped<NotificationRepository>();
|
builder.Services.AddScoped<NotificationRepository>();
|
||||||
builder.Services.AddScoped<DdosAttackRepository>();
|
builder.Services.AddScoped<DdosAttackRepository>();
|
||||||
builder.Services.AddScoped<SubscriptionRepository>();
|
builder.Services.AddScoped<SubscriptionRepository>();
|
||||||
|
builder.Services.AddScoped<PleskServerRepository>();
|
||||||
|
builder.Services.AddScoped<WebsiteRepository>();
|
||||||
builder.Services.AddScoped<LoadingMessageRepository>();
|
builder.Services.AddScoped<LoadingMessageRepository>();
|
||||||
builder.Services.AddScoped<NewsEntryRepository>();
|
builder.Services.AddScoped<NewsEntryRepository>();
|
||||||
builder.Services.AddScoped<NodeAllocationRepository>();
|
|
||||||
builder.Services.AddScoped<StatisticsRepository>();
|
builder.Services.AddScoped<StatisticsRepository>();
|
||||||
|
|
||||||
builder.Services.AddScoped<AuditLogEntryRepository>();
|
builder.Services.AddScoped<AuditLogEntryRepository>();
|
||||||
builder.Services.AddScoped<ErrorLogEntryRepository>();
|
builder.Services.AddScoped<ErrorLogEntryRepository>();
|
||||||
builder.Services.AddScoped<SecurityLogEntryRepository>();
|
builder.Services.AddScoped<SecurityLogEntryRepository>();
|
||||||
builder.Services.AddScoped(typeof(Repository<>));
|
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
builder.Services.AddSingleton<ConfigService>();
|
builder.Services.AddSingleton<ConfigService>();
|
||||||
@@ -90,6 +85,7 @@ namespace Moonlight
|
|||||||
builder.Services.AddScoped<TotpService>();
|
builder.Services.AddScoped<TotpService>();
|
||||||
builder.Services.AddScoped<ToastService>();
|
builder.Services.AddScoped<ToastService>();
|
||||||
builder.Services.AddScoped<NodeService>();
|
builder.Services.AddScoped<NodeService>();
|
||||||
|
builder.Services.AddSingleton<MessageService>();
|
||||||
builder.Services.AddScoped<ServerService>();
|
builder.Services.AddScoped<ServerService>();
|
||||||
builder.Services.AddSingleton<PaperService>();
|
builder.Services.AddSingleton<PaperService>();
|
||||||
builder.Services.AddScoped<ClipboardService>();
|
builder.Services.AddScoped<ClipboardService>();
|
||||||
@@ -101,13 +97,8 @@ namespace Moonlight
|
|||||||
builder.Services.AddScoped<NotificationClientService>();
|
builder.Services.AddScoped<NotificationClientService>();
|
||||||
builder.Services.AddScoped<ModalService>();
|
builder.Services.AddScoped<ModalService>();
|
||||||
builder.Services.AddScoped<SmartDeployService>();
|
builder.Services.AddScoped<SmartDeployService>();
|
||||||
builder.Services.AddScoped<WebSpaceService>();
|
builder.Services.AddScoped<WebsiteService>();
|
||||||
builder.Services.AddScoped<StatisticsViewService>();
|
builder.Services.AddScoped<StatisticsViewService>();
|
||||||
builder.Services.AddSingleton<DateTimeService>();
|
|
||||||
builder.Services.AddSingleton<EventSystem>();
|
|
||||||
builder.Services.AddScoped<FileDownloadService>();
|
|
||||||
builder.Services.AddScoped<ForgeService>();
|
|
||||||
builder.Services.AddScoped<FabricService>();
|
|
||||||
|
|
||||||
builder.Services.AddScoped<GoogleOAuth2Service>();
|
builder.Services.AddScoped<GoogleOAuth2Service>();
|
||||||
builder.Services.AddScoped<DiscordOAuth2Service>();
|
builder.Services.AddScoped<DiscordOAuth2Service>();
|
||||||
@@ -115,6 +106,8 @@ namespace Moonlight
|
|||||||
builder.Services.AddScoped<SubscriptionService>();
|
builder.Services.AddScoped<SubscriptionService>();
|
||||||
builder.Services.AddScoped<SubscriptionAdminService>();
|
builder.Services.AddScoped<SubscriptionAdminService>();
|
||||||
|
|
||||||
|
builder.Services.AddSingleton<CleanupService>();
|
||||||
|
|
||||||
// Loggers
|
// Loggers
|
||||||
builder.Services.AddScoped<SecurityLogService>();
|
builder.Services.AddScoped<SecurityLogService>();
|
||||||
builder.Services.AddScoped<AuditLogService>();
|
builder.Services.AddScoped<AuditLogService>();
|
||||||
@@ -123,10 +116,10 @@ namespace Moonlight
|
|||||||
builder.Services.AddScoped<MailService>();
|
builder.Services.AddScoped<MailService>();
|
||||||
builder.Services.AddSingleton<TrashMailDetectorService>();
|
builder.Services.AddSingleton<TrashMailDetectorService>();
|
||||||
|
|
||||||
// Support chat
|
// Support
|
||||||
builder.Services.AddSingleton<SupportChatServerService>();
|
builder.Services.AddSingleton<SupportServerService>();
|
||||||
builder.Services.AddScoped<SupportChatClientService>();
|
builder.Services.AddScoped<SupportAdminService>();
|
||||||
builder.Services.AddScoped<SupportChatAdminService>();
|
builder.Services.AddScoped<SupportClientService>();
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
builder.Services.AddSingleton<SmartTranslateHelper>();
|
builder.Services.AddSingleton<SmartTranslateHelper>();
|
||||||
@@ -137,13 +130,11 @@ namespace Moonlight
|
|||||||
builder.Services.AddSingleton<PaperApiHelper>();
|
builder.Services.AddSingleton<PaperApiHelper>();
|
||||||
builder.Services.AddSingleton<HostSystemHelper>();
|
builder.Services.AddSingleton<HostSystemHelper>();
|
||||||
builder.Services.AddScoped<DaemonApiHelper>();
|
builder.Services.AddScoped<DaemonApiHelper>();
|
||||||
builder.Services.AddScoped<CloudPanelApiHelper>();
|
builder.Services.AddScoped<PleskApiHelper>();
|
||||||
|
|
||||||
// Background services
|
// Background services
|
||||||
builder.Services.AddSingleton<DiscordBotService>();
|
builder.Services.AddSingleton<DiscordBotService>();
|
||||||
builder.Services.AddSingleton<StatisticsCaptureService>();
|
builder.Services.AddSingleton<StatisticsCaptureService>();
|
||||||
builder.Services.AddSingleton<DiscordNotificationService>();
|
|
||||||
builder.Services.AddSingleton<CleanupService>();
|
|
||||||
|
|
||||||
// Third party services
|
// Third party services
|
||||||
builder.Services.AddBlazorTable();
|
builder.Services.AddBlazorTable();
|
||||||
@@ -169,12 +160,14 @@ namespace Moonlight
|
|||||||
|
|
||||||
app.MapBlazorHub();
|
app.MapBlazorHub();
|
||||||
app.MapFallbackToPage("/_Host");
|
app.MapFallbackToPage("/_Host");
|
||||||
|
|
||||||
|
// Support service
|
||||||
|
var supportServerService = app.Services.GetRequiredService<SupportServerService>();
|
||||||
|
|
||||||
// AutoStart services
|
// AutoStart services
|
||||||
_ = app.Services.GetRequiredService<CleanupService>();
|
_ = app.Services.GetRequiredService<CleanupService>();
|
||||||
_ = app.Services.GetRequiredService<DiscordBotService>();
|
_ = app.Services.GetRequiredService<DiscordBotService>();
|
||||||
_ = app.Services.GetRequiredService<StatisticsCaptureService>();
|
_ = app.Services.GetRequiredService<StatisticsCaptureService>();
|
||||||
_ = app.Services.GetRequiredService<DiscordNotificationService>();
|
|
||||||
|
|
||||||
// Discord bot service
|
// Discord bot service
|
||||||
//var discordBotService = app.Services.GetRequiredService<DiscordBotService>();
|
//var discordBotService = app.Services.GetRequiredService<DiscordBotService>();
|
||||||
|
|||||||
@@ -17,15 +17,12 @@
|
|||||||
"applicationUrl": "http://moonlight.testy:5118;https://localhost:7118;http://localhost:5118",
|
"applicationUrl": "http://moonlight.testy:5118;https://localhost:7118;http://localhost:5118",
|
||||||
"dotnetRunMessages": true
|
"dotnetRunMessages": true
|
||||||
},
|
},
|
||||||
"Sql Debug": {
|
"IIS Express": {
|
||||||
"commandName": "Project",
|
"commandName": "IISExpress",
|
||||||
"launchBrowser": true,
|
"launchBrowser": true,
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
"ML_SQL_DEBUG": "true"
|
}
|
||||||
},
|
|
||||||
"applicationUrl": "http://moonlight.testy:5118;https://localhost:7118;http://localhost:5118",
|
|
||||||
"dotnetRunMessages": true
|
|
||||||
},
|
},
|
||||||
"Docker": {
|
"Docker": {
|
||||||
"commandName": "Docker",
|
"commandName": "Docker",
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
@using Moonlight.App.Exceptions
|
@using Moonlight.App.Exceptions
|
||||||
@using Moonlight.App.Services
|
@using Moonlight.App.Services
|
||||||
@using Logging.Net
|
@using Logging.Net
|
||||||
@using Moonlight.App.ApiClients.CloudPanel
|
|
||||||
@inherits ErrorBoundaryBase
|
@inherits ErrorBoundaryBase
|
||||||
|
|
||||||
@inject AlertService AlertService
|
@inject AlertService AlertService
|
||||||
@@ -58,16 +57,12 @@ else
|
|||||||
SmartTranslateService.Translate("Error from daemon"),
|
SmartTranslateService.Translate("Error from daemon"),
|
||||||
wingsException.Message
|
wingsException.Message
|
||||||
);
|
);
|
||||||
|
|
||||||
//TODO: Error log service
|
|
||||||
|
|
||||||
Logger.Warn($"Wings exception status code: {wingsException.StatusCode}");
|
|
||||||
}
|
}
|
||||||
else if (exception is CloudPanelException cloudPanelException)
|
else if (exception is PleskException pleskException)
|
||||||
{
|
{
|
||||||
await AlertService.Error(
|
await AlertService.Error(
|
||||||
SmartTranslateService.Translate("Error from cloud panel"),
|
SmartTranslateService.Translate("Error from plesk"),
|
||||||
cloudPanelException.Message
|
pleskException.Message
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if (exception is NotImplementedException)
|
else if (exception is NotImplementedException)
|
||||||
|
|||||||
@@ -71,7 +71,7 @@
|
|||||||
{
|
{
|
||||||
if (firstRender)
|
if (firstRender)
|
||||||
{
|
{
|
||||||
await JsRuntime.InvokeVoidAsync("moonlight.loading.loadMonaco");
|
await JsRuntime.InvokeVoidAsync("initMonacoTheme");
|
||||||
|
|
||||||
Editor.OnDidInit = new EventCallback<MonacoEditorBase>(this, async () =>
|
Editor.OnDidInit = new EventCallback<MonacoEditorBase>(this, async () =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
@using Moonlight.App.Helpers.Files
|
@using Moonlight.App.Helpers.Files
|
||||||
@using Logging.Net
|
|
||||||
|
|
||||||
<div class="badge badge-lg badge-light-primary">
|
<div class="badge badge-lg badge-light-primary">
|
||||||
<div class="d-flex align-items-center flex-wrap">
|
<div class="d-flex align-items-center flex-wrap">
|
||||||
|
|||||||
@@ -35,90 +35,88 @@
|
|||||||
<table class="table align-middle table-row-dashed fs-6 gy-5 dataTable no-footer" style="width: 100%;">
|
<table class="table align-middle table-row-dashed fs-6 gy-5 dataTable no-footer" style="width: 100%;">
|
||||||
<tbody class="fw-semibold text-gray-600">
|
<tbody class="fw-semibold text-gray-600">
|
||||||
<LazyLoader Load="Load">
|
<LazyLoader Load="Load">
|
||||||
<ContentBlock @ref="ContentBlock" AllowContentOverride="true">
|
<tr class="even">
|
||||||
|
<td class="w-10px">
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<span class="icon-wrapper">
|
||||||
|
<i class="bx bx-md bx-up-arrow-alt text-primary"></i>
|
||||||
|
</span>
|
||||||
|
<a href="#" @onclick:preventDefault @onclick="GoUp" class="ms-3 text-gray-800 text-hover-primary">
|
||||||
|
<TL>Go up</TL>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td></td>
|
||||||
|
<td class="text-end">
|
||||||
|
<div class="d-flex justify-content-end">
|
||||||
|
<div class="ms-2">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@foreach (var file in Data)
|
||||||
|
{
|
||||||
<tr class="even">
|
<tr class="even">
|
||||||
<td class="w-10px">
|
<td class="w-10px">
|
||||||
|
@if (!HideSelect)
|
||||||
|
{
|
||||||
|
<div class="form-check form-check-sm form-check-custom form-check-solid">
|
||||||
|
@{
|
||||||
|
var toggle = ToggleStatusCache.ContainsKey(file) && ToggleStatusCache[file];
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (toggle)
|
||||||
|
{
|
||||||
|
<input @onclick="() => SetToggleState(file, false)" class="form-check-input" type="checkbox" checked="checked">
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<input @onclick="() => SetToggleState(file, true)" class="form-check-input" type="checkbox">
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<span class="icon-wrapper">
|
<span class="icon-wrapper">
|
||||||
<i class="bx bx-md bx-up-arrow-alt text-primary"></i>
|
@if (file.IsFile)
|
||||||
|
{
|
||||||
|
<i class="bx bx-md bx-file text-primary"></i>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<i class="bx bx-md bx-folder text-primary"></i>
|
||||||
|
}
|
||||||
</span>
|
</span>
|
||||||
<a href="#" @onclick:preventDefault @onclick="GoUp" class="ms-3 text-gray-800 text-hover-primary">
|
<a href="#" @onclick:preventDefault @onclick="() => OnClicked(file)" class="ms-3 text-gray-800 text-hover-primary">@(file.Name)</a>
|
||||||
<TL>Go up</TL>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td></td>
|
<td>@(Formatter.FormatSize(file.Size))</td>
|
||||||
<td class="text-end">
|
<td class="text-end">
|
||||||
<div class="d-flex justify-content-end">
|
<div class="d-flex justify-content-end">
|
||||||
<div class="ms-2">
|
<div class="ms-2 me-6">
|
||||||
|
@if (ContextActions.Any())
|
||||||
|
{
|
||||||
|
<ContextMenuTrigger MenuId="triggerMenu" MouseButtonTrigger="MouseButtonTrigger.Both" Data="file">
|
||||||
|
<button class="btn btn-sm btn-icon btn-light btn-active-light-primary me-2">
|
||||||
|
<span class="svg-icon svg-icon-5 m-0">
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect x="10" y="10" width="4" height="4" rx="2" fill="currentColor"></rect>
|
||||||
|
<rect x="17" y="10" width="4" height="4" rx="2" fill="currentColor"></rect>
|
||||||
|
<rect x="3" y="10" width="4" height="4" rx="2" fill="currentColor"></rect>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</ContextMenuTrigger>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@foreach (var file in Data)
|
}
|
||||||
{
|
|
||||||
<tr class="even">
|
|
||||||
<td class="w-10px">
|
|
||||||
@if (!HideSelect)
|
|
||||||
{
|
|
||||||
<div class="form-check form-check-sm form-check-custom form-check-solid">
|
|
||||||
@{
|
|
||||||
var toggle = ToggleStatusCache.ContainsKey(file) && ToggleStatusCache[file];
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (toggle)
|
|
||||||
{
|
|
||||||
<input @onclick="() => SetToggleState(file, false)" class="form-check-input" type="checkbox" checked="checked">
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<input @onclick="() => SetToggleState(file, true)" class="form-check-input" type="checkbox">
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<span class="icon-wrapper">
|
|
||||||
@if (file.IsFile)
|
|
||||||
{
|
|
||||||
<i class="bx bx-md bx-file text-primary"></i>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<i class="bx bx-md bx-folder text-primary"></i>
|
|
||||||
}
|
|
||||||
</span>
|
|
||||||
<a href="#" @onclick:preventDefault @onclick="() => OnClicked(file)" class="ms-3 text-gray-800 text-hover-primary">@(file.Name)</a>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>@(Formatter.FormatSize(file.Size))</td>
|
|
||||||
<td class="text-end">
|
|
||||||
<div class="d-flex justify-content-end">
|
|
||||||
<div class="ms-2 me-6">
|
|
||||||
@if (ContextActions.Any())
|
|
||||||
{
|
|
||||||
<ContextMenuTrigger MenuId="triggerMenu" MouseButtonTrigger="MouseButtonTrigger.Both" Data="file">
|
|
||||||
<button class="btn btn-sm btn-icon btn-light btn-active-light-primary me-2">
|
|
||||||
<span class="svg-icon svg-icon-5 m-0">
|
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<rect x="10" y="10" width="4" height="4" rx="2" fill="currentColor"></rect>
|
|
||||||
<rect x="17" y="10" width="4" height="4" rx="2" fill="currentColor"></rect>
|
|
||||||
<rect x="3" y="10" width="4" height="4" rx="2" fill="currentColor"></rect>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</ContextMenuTrigger>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</ContentBlock>
|
|
||||||
</LazyLoader>
|
</LazyLoader>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -140,13 +138,12 @@
|
|||||||
|
|
||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public FileAccess Access { get; set; }
|
public FileAccess Access { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Func<FileData, Task<bool>>? OnElementClicked { get; set; }
|
public Func<FileData, Task<bool>>? OnElementClicked { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Func<Task>? OnSelectionChanged { get; set; }
|
public Func<Task>? OnSelectionChanged { get; set; }
|
||||||
|
|
||||||
@@ -158,7 +155,7 @@
|
|||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool DisableScrolling { get; set; } = false;
|
public bool DisableScrolling { get; set; } = false;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public Func<FileData, bool>? Filter { get; set; }
|
public Func<FileData, bool>? Filter { get; set; }
|
||||||
|
|
||||||
@@ -172,19 +169,15 @@
|
|||||||
private Dictionary<FileData, bool> ToggleStatusCache = new();
|
private Dictionary<FileData, bool> ToggleStatusCache = new();
|
||||||
private bool AllToggled = false;
|
private bool AllToggled = false;
|
||||||
|
|
||||||
private ContentBlock ContentBlock;
|
|
||||||
|
|
||||||
public async Task Refresh()
|
public async Task Refresh()
|
||||||
{
|
{
|
||||||
ContentBlock?.SetBlocking(true);
|
|
||||||
|
|
||||||
var list = new List<FileData>();
|
var list = new List<FileData>();
|
||||||
|
|
||||||
foreach (var fileData in await Access.Ls())
|
foreach (var fileData in await Access.Ls())
|
||||||
{
|
{
|
||||||
if (Filter != null)
|
if (Filter != null)
|
||||||
{
|
{
|
||||||
if (Filter.Invoke(fileData))
|
if(Filter.Invoke(fileData))
|
||||||
list.Add(fileData);
|
list.Add(fileData);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -192,7 +185,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
Data = list.ToArray();
|
Data = list.ToArray();
|
||||||
|
|
||||||
ToggleStatusCache.Clear();
|
ToggleStatusCache.Clear();
|
||||||
AllToggled = false;
|
AllToggled = false;
|
||||||
|
|
||||||
@@ -203,8 +196,6 @@
|
|||||||
|
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
OnSelectionChanged?.Invoke();
|
OnSelectionChanged?.Invoke();
|
||||||
|
|
||||||
ContentBlock?.SetBlocking(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Load(LazyLoader arg)
|
private async Task Load(LazyLoader arg)
|
||||||
@@ -266,7 +257,7 @@
|
|||||||
if (canceled)
|
if (canceled)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await Access.Up();
|
await Access.Up();
|
||||||
await Refresh();
|
await Refresh();
|
||||||
}
|
}
|
||||||
@@ -282,4 +273,4 @@
|
|||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,13 +2,13 @@
|
|||||||
<div class="card-body pt-0 pb-0">
|
<div class="card-body pt-0 pb-0">
|
||||||
<ul class="nav nav-stretch nav-line-tabs nav-line-tabs-2x border-transparent fs-5 fw-bold">
|
<ul class="nav nav-stretch nav-line-tabs nav-line-tabs-2x border-transparent fs-5 fw-bold">
|
||||||
<li class="nav-item mt-2">
|
<li class="nav-item mt-2">
|
||||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 0 ? "active" : "")" href="/admin/webspaces">
|
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 0 ? "active" : "")" href="/admin/websites">
|
||||||
<TL>Webspaces</TL>
|
<TL>Websites</TL>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item mt-2">
|
<li class="nav-item mt-2">
|
||||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 1 ? "active" : "")" href="/admin/webspaces/servers">
|
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 1 ? "active" : "")" href="/admin/websites/servers">
|
||||||
<TL>Cloud panels</TL>
|
<TL>Plesk servers</TL>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
@if (AllowContentOverride)
|
|
||||||
{
|
|
||||||
if (IsBlocking)
|
|
||||||
{
|
|
||||||
<div class="overlay overlay-block">
|
|
||||||
<div class="overlay-wrapper p-5">
|
|
||||||
@(ChildContent)
|
|
||||||
</div>
|
|
||||||
<div class="overlay-layer">
|
|
||||||
<div class="spinner-border text-primary" role="status">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
@ChildContent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<div class="@(IsBlocking ? "overlay overlay-block" : "")">
|
|
||||||
<div class="@(IsBlocking ? "overlay-wrapper p-5" : "")">
|
|
||||||
@(ChildContent)
|
|
||||||
</div>
|
|
||||||
@if (IsBlocking)
|
|
||||||
{
|
|
||||||
<div class="overlay-layer">
|
|
||||||
<div class="spinner-border text-primary" role="status">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
@code
|
|
||||||
{
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment ChildContent { get; set; }
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public bool AllowContentOverride { get; set; } = false;
|
|
||||||
|
|
||||||
private bool IsBlocking = false;
|
|
||||||
|
|
||||||
public async Task SetBlocking(bool b)
|
|
||||||
{
|
|
||||||
IsBlocking = b;
|
|
||||||
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,36 +9,33 @@
|
|||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
@inject CookieService CookieService
|
@inject CookieService CookieService
|
||||||
|
|
||||||
@if (User != null)
|
<div class="menu menu-column justify-content-center"
|
||||||
{
|
data-kt-menu="true">
|
||||||
<div class="menu menu-column justify-content-center"
|
<div class="menu-item">
|
||||||
data-kt-menu="true">
|
<div class="dropdown">
|
||||||
<div class="menu-item">
|
<button class="btn btn-success dropdown-toggle" type="button" data-bs-toggle="dropdown">
|
||||||
<div class="dropdown">
|
<TL>Create</TL>
|
||||||
<button class="btn btn-success dropdown-toggle" type="button" data-bs-toggle="dropdown">
|
</button>
|
||||||
<TL>Create</TL>
|
<ul class="dropdown-menu">
|
||||||
</button>
|
<li>
|
||||||
<ul class="dropdown-menu">
|
<a class="dropdown-item py-2" href="/servers/create">
|
||||||
<li>
|
<TL>Server</TL>
|
||||||
<a class="dropdown-item py-2" href="/servers/create">
|
</a>
|
||||||
<TL>Server</TL>
|
</li>
|
||||||
</a>
|
<li>
|
||||||
</li>
|
<a class="dropdown-item py-2" href="/domains/create">
|
||||||
<li>
|
<TL>Domain</TL>
|
||||||
<a class="dropdown-item py-2" href="/domains/create">
|
</a>
|
||||||
<TL>Domain</TL>
|
</li>
|
||||||
</a>
|
<li>
|
||||||
</li>
|
<a class="dropdown-item py-2" href="/websites/create">
|
||||||
<li>
|
<TL>Website</TL>
|
||||||
<a class="dropdown-item py-2" href="/webspaces/create">
|
</a>
|
||||||
<TL>Webspace</TL>
|
</li>
|
||||||
</a>
|
</ul>
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
</div>
|
||||||
|
|
||||||
<div class="app-navbar flex-shrink-0">
|
<div class="app-navbar flex-shrink-0">
|
||||||
<div class="app-navbar-item ms-1 ms-lg-3">
|
<div class="app-navbar-item ms-1 ms-lg-3">
|
||||||
|
|||||||
@@ -45,11 +45,11 @@ else
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="menu-item">
|
<div class="menu-item">
|
||||||
<a class="menu-link" href="/webspaces">
|
<a class="menu-link" href="/websites">
|
||||||
<span class="menu-icon">
|
<span class="menu-icon">
|
||||||
<i class="bx bx-globe"></i>
|
<i class="bx bx-globe"></i>
|
||||||
</span>
|
</span>
|
||||||
<span class="menu-title"><TL>Webspaces</TL></span>
|
<span class="menu-title"><TL>Websites</TL></span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="menu-item">
|
<div class="menu-item">
|
||||||
@@ -148,11 +148,11 @@ else
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="menu-item">
|
<div class="menu-item">
|
||||||
<a class="menu-link" href="/admin/webspaces">
|
<a class="menu-link" href="/admin/websites">
|
||||||
<span class="menu-icon">
|
<span class="menu-icon">
|
||||||
<i class="bx bx-globe"></i>
|
<i class="bx bx-globe"></i>
|
||||||
</span>
|
</span>
|
||||||
<span class="menu-title"><TL>Webspaces</TL></span>
|
<span class="menu-title"><TL>Websites</TL></span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="menu-item">
|
<div class="menu-item">
|
||||||
|
|||||||
@@ -47,6 +47,6 @@
|
|||||||
|
|
||||||
private async void TriggerFlashbang()
|
private async void TriggerFlashbang()
|
||||||
{
|
{
|
||||||
await JsRuntime.InvokeVoidAsync("moonlight.flashbang.run");
|
await JsRuntime.InvokeVoidAsync("flashbang");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,6 @@
|
|||||||
@using Logging.Net
|
@using Logging.Net
|
||||||
@using BlazorContextMenu
|
@using BlazorContextMenu
|
||||||
@using Moonlight.App.Database.Entities
|
@using Moonlight.App.Database.Entities
|
||||||
@using Moonlight.App.Events
|
|
||||||
@using Moonlight.App.Services.Interop
|
@using Moonlight.App.Services.Interop
|
||||||
|
|
||||||
@inject ServerService ServerService
|
@inject ServerService ServerService
|
||||||
@@ -13,7 +12,7 @@
|
|||||||
@inject AlertService AlertService
|
@inject AlertService AlertService
|
||||||
@inject ToastService ToastService
|
@inject ToastService ToastService
|
||||||
@inject ClipboardService ClipboardService
|
@inject ClipboardService ClipboardService
|
||||||
@inject EventSystem Event
|
@inject MessageService MessageService
|
||||||
@inject SmartTranslateService SmartTranslateService
|
@inject SmartTranslateService SmartTranslateService
|
||||||
|
|
||||||
@implements IDisposable
|
@implements IDisposable
|
||||||
@@ -113,51 +112,64 @@
|
|||||||
|
|
||||||
protected override void OnInitialized()
|
protected override void OnInitialized()
|
||||||
{
|
{
|
||||||
Event.On<ServerBackup>("wings.backups.create", this, async (backup) =>
|
MessageService.Subscribe<ServerBackups, ServerBackup>("wings.backups.create", this, (backup) =>
|
||||||
|
{
|
||||||
|
if (AllBackups == null)
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
if (AllBackups.Any(x => x.Id == backup.Id))
|
||||||
|
{
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(1));
|
||||||
|
await ToastService.Success(SmartTranslateService.Translate("Backup successfully created"));
|
||||||
|
await LazyLoader.Reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
});
|
||||||
|
|
||||||
|
MessageService.Subscribe<ServerBackups, ServerBackup>("wings.backups.createfailed", this, (backup) =>
|
||||||
|
{
|
||||||
|
if (AllBackups == null)
|
||||||
|
return Task.CompletedTask;
|
||||||
|
|
||||||
|
if (AllBackups.Any(x => x.Id == backup.Id))
|
||||||
|
{
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await ToastService.Error(SmartTranslateService.Translate("Backup creation failed"));
|
||||||
|
await LazyLoader.Reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
});
|
||||||
|
|
||||||
|
MessageService.Subscribe<ServerBackups, ServerBackup>("wings.backups.delete", this, async (backup) =>
|
||||||
{
|
{
|
||||||
if (AllBackups == null)
|
if (AllBackups == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (AllBackups.Any(x => x.Id == backup.Id))
|
if (AllBackups.Any(x => x.Id == backup.Id))
|
||||||
{
|
{
|
||||||
await Task.Delay(TimeSpan.FromSeconds(1));
|
Task.Run(async () =>
|
||||||
await ToastService.Success(SmartTranslateService.Translate("Backup successfully created"));
|
{
|
||||||
await LazyLoader.Reload();
|
await ToastService.Success(SmartTranslateService.Translate("Backup successfully deleted"));
|
||||||
|
await LazyLoader.Reload();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Event.On<ServerBackup>("wings.backups.createFailed", this, async (backup) =>
|
MessageService.Subscribe<ServerBackups, ServerBackup>("wings.backups.restore", this, async (backup) =>
|
||||||
{
|
{
|
||||||
if (AllBackups == null)
|
if (AllBackups == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (AllBackups.Any(x => x.Id == backup.Id))
|
if (AllBackups.Any(x => x.Id == backup.Id))
|
||||||
{
|
{
|
||||||
await ToastService.Error(SmartTranslateService.Translate("Backup creation failed"));
|
Task.Run(async () => { await ToastService.Success(SmartTranslateService.Translate("Backup successfully restored")); });
|
||||||
await LazyLoader.Reload();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Event.On<ServerBackup>("wings.backups.delete", this, async (backup) =>
|
|
||||||
{
|
|
||||||
if (AllBackups == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (AllBackups.Any(x => x.Id == backup.Id))
|
|
||||||
{
|
|
||||||
await ToastService.Success(SmartTranslateService.Translate("Backup successfully deleted"));
|
|
||||||
await LazyLoader.Reload();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Event.On<ServerBackup>("wings.backups.restore", this, async (backup) =>
|
|
||||||
{
|
|
||||||
if (AllBackups == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (AllBackups.Any(x => x.Id == backup.Id))
|
|
||||||
{
|
|
||||||
await ToastService.Success(SmartTranslateService.Translate("Backup successfully restored"));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -174,102 +186,95 @@
|
|||||||
|
|
||||||
private async Task Download(ServerBackup serverBackup)
|
private async Task Download(ServerBackup serverBackup)
|
||||||
{
|
{
|
||||||
var url = await ServerService.DownloadBackup(CurrentServer, serverBackup);
|
|
||||||
|
|
||||||
NavigationManager.NavigateTo(url);
|
|
||||||
await ToastService.Success(SmartTranslateService.Translate("Backup download successfully started"));
|
|
||||||
|
|
||||||
/*
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var url = await ServerService.DownloadBackup(CurrentServer, serverBackup);
|
||||||
|
|
||||||
|
NavigationManager.NavigateTo(url);
|
||||||
|
await ToastService.Success(SmartTranslateService.Translate("Backup download successfully started"));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.Warn("Error starting backup download");
|
Logger.Warn("Error starting backup download");
|
||||||
Logger.Warn(e);
|
Logger.Warn(e);
|
||||||
await ToastService.Error(SmartTranslateService.Translate("Backup download failed"));
|
await ToastService.Error(SmartTranslateService.Translate("Backup download failed"));
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CopyUrl(ServerBackup serverBackup)
|
private async Task CopyUrl(ServerBackup serverBackup)
|
||||||
{
|
{
|
||||||
var url = await ServerService.DownloadBackup(CurrentServer, serverBackup);
|
|
||||||
|
|
||||||
await ClipboardService.Copy(url);
|
|
||||||
await AlertService.Success(
|
|
||||||
SmartTranslateService.Translate("Success"),
|
|
||||||
SmartTranslateService.Translate("Backup URL successfully copied to your clipboard"));
|
|
||||||
|
|
||||||
/*
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var url = await ServerService.DownloadBackup(CurrentServer, serverBackup);
|
||||||
|
|
||||||
|
await ClipboardService.CopyToClipboard(url);
|
||||||
|
await AlertService.Success(
|
||||||
|
SmartTranslateService.Translate("Success"),
|
||||||
|
SmartTranslateService.Translate("Backup URL successfully copied to your clipboard"));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.Warn("Error copying backup url");
|
Logger.Warn("Error copying backup url");
|
||||||
Logger.Warn(e);
|
Logger.Warn(e);
|
||||||
await ToastService.Error(SmartTranslateService.Translate("An unknown error occured while generating backup url"));
|
await ToastService.Error(SmartTranslateService.Translate("An unknown error occured while generating backup url"));
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Delete(ServerBackup serverBackup)
|
private async Task Delete(ServerBackup serverBackup)
|
||||||
{
|
{
|
||||||
await ToastService.Info(SmartTranslateService.Translate("Backup deletion started"));
|
|
||||||
await ServerService.DeleteBackup(CurrentServer, serverBackup);
|
|
||||||
|
|
||||||
/*
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
await ToastService.Info(SmartTranslateService.Translate("Backup deletion started"));
|
||||||
|
await ServerService.DeleteBackup(CurrentServer, serverBackup);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.Warn("Error deleting backup");
|
Logger.Warn("Error deleting backup");
|
||||||
Logger.Warn(e);
|
Logger.Warn(e);
|
||||||
await ToastService.Error(SmartTranslateService.Translate("An unknown error occured while starting backup deletion"));
|
await ToastService.Error(SmartTranslateService.Translate("An unknown error occured while starting backup deletion"));
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Restore(ServerBackup serverBackup)
|
private async Task Restore(ServerBackup serverBackup)
|
||||||
{
|
{
|
||||||
await ServerService.RestoreBackup(CurrentServer, serverBackup);
|
|
||||||
await ToastService.Info(SmartTranslateService.Translate("Backup restore started"));
|
|
||||||
|
|
||||||
/*
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
await ServerService.RestoreBackup(CurrentServer, serverBackup);
|
||||||
|
|
||||||
|
await ToastService.Info(SmartTranslateService.Translate("Backup restore started"));
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.Warn("Error restoring backup");
|
Logger.Warn("Error restoring backup");
|
||||||
Logger.Warn(e);
|
Logger.Warn(e);
|
||||||
await ToastService.Error(SmartTranslateService.Translate("An unknown error occured while restoring a backup"));
|
await ToastService.Error(SmartTranslateService.Translate("An unknown error occured while restoring a backup"));
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Create()
|
private async Task Create()
|
||||||
{
|
{
|
||||||
await ToastService.Info(SmartTranslateService.Translate("Started backup creation"));
|
|
||||||
await ServerService.CreateBackup(CurrentServer);
|
|
||||||
await LazyLoader.Reload();
|
|
||||||
|
|
||||||
/*
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
await ToastService.Info(SmartTranslateService.Translate("Started backup creation"));
|
||||||
|
var backup = await ServerService.CreateBackup(CurrentServer);
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
// Modify the backup list so no reload needed. Also the create event will work
|
// Modify the backup list so no reload needed. Also the create event will work
|
||||||
//var list = AllBackups!.ToList();
|
var list = AllBackups!.ToList();
|
||||||
//list.Add(backup);
|
list.Add(backup);
|
||||||
//AllBackups = list.ToArray();
|
AllBackups = list.ToArray();
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
await LazyLoader.Reload();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.Warn("Error creating backup");
|
Logger.Warn("Error creating backup");
|
||||||
Logger.Warn(e);
|
Logger.Warn(e);
|
||||||
await ToastService.Error(SmartTranslateService.Translate("An unknown error has occured while creating a backup"));
|
await ToastService.Error(SmartTranslateService.Translate("An unknown error has occured while creating a backup"));
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnContextMenuClick(ItemClickEventArgs args)
|
private async Task OnContextMenuClick(ItemClickEventArgs args)
|
||||||
@@ -295,11 +300,11 @@
|
|||||||
await Refresh(LazyLoader);
|
await Refresh(LazyLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
await Event.Off("wings.backups.create", this);
|
MessageService.Unsubscribe("wings.backups.create", this);
|
||||||
await Event.Off("wings.backups.createFailed", this);
|
MessageService.Unsubscribe("wings.backups.createfailed", this);
|
||||||
await Event.Off("wings.backups.restore", this);
|
MessageService.Unsubscribe("wings.backups.restore", this);
|
||||||
await Event.Off("wings.backups.delete", this);
|
MessageService.Unsubscribe("wings.backups.delete", this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,16 +55,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col fs-5">
|
<div class="col fs-5">
|
||||||
<span class="fw-bold"><TL>Server ID</TL>:</span>
|
<span class="fw-bold"><TL>Server ID</TL>:</span>
|
||||||
<span class="ms-1 text-muted">
|
<span class="ms-1 text-muted">@(CurrentServer.Id)</span>
|
||||||
@if (User.Admin)
|
|
||||||
{
|
|
||||||
<a href="/admin/servers/edit/@(CurrentServer.Id)">@(CurrentServer.Id)</a>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
@(CurrentServer.Id)
|
|
||||||
}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col fs-5">
|
<div class="col fs-5">
|
||||||
<span class="fw-bold"><TL>Status</TL>:</span>
|
<span class="fw-bold"><TL>Status</TL>:</span>
|
||||||
@@ -173,9 +164,6 @@
|
|||||||
{
|
{
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
public Server CurrentServer { get; set; }
|
public Server CurrentServer { get; set; }
|
||||||
|
|
||||||
[CascadingParameter]
|
|
||||||
public User User { get; set; }
|
|
||||||
|
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
public PteroConsole Console { get; set; }
|
public PteroConsole Console { get; set; }
|
||||||
|
|||||||
@@ -37,12 +37,6 @@
|
|||||||
if(Tags.Contains("paperversion"))
|
if(Tags.Contains("paperversion"))
|
||||||
Settings.Add("Paper version", typeof(PaperVersionSetting));
|
Settings.Add("Paper version", typeof(PaperVersionSetting));
|
||||||
|
|
||||||
if(Tags.Contains("forgeversion"))
|
|
||||||
Settings.Add("Forge version", typeof(ForgeVersionSetting));
|
|
||||||
|
|
||||||
if(Tags.Contains("fabricversion"))
|
|
||||||
Settings.Add("Fabric version", typeof(FabricVersionSetting));
|
|
||||||
|
|
||||||
if(Tags.Contains("join2start"))
|
if(Tags.Contains("join2start"))
|
||||||
Settings.Add("Join2Start", typeof(Join2StartSetting));
|
Settings.Add("Join2Start", typeof(Join2StartSetting));
|
||||||
|
|
||||||
|
|||||||
@@ -1,173 +0,0 @@
|
|||||||
@using Moonlight.App.Services
|
|
||||||
@using Microsoft.EntityFrameworkCore
|
|
||||||
@using Moonlight.App.Database.Entities
|
|
||||||
@using Moonlight.App.Repositories
|
|
||||||
@using Moonlight.App.Repositories.Servers
|
|
||||||
@using Moonlight.App.Helpers
|
|
||||||
|
|
||||||
@inject ServerService ServerService
|
|
||||||
@inject ServerRepository ServerRepository
|
|
||||||
@inject ImageRepository ImageRepository
|
|
||||||
@inject FabricService FabricService
|
|
||||||
@inject SmartTranslateService TranslationService
|
|
||||||
|
|
||||||
<div class="col">
|
|
||||||
<div class="card card-body">
|
|
||||||
<LazyLoader Load="Load">
|
|
||||||
<label class="mb-2 form-label">
|
|
||||||
<TL>Fabric version</TL>
|
|
||||||
</label>
|
|
||||||
<input class="mb-2 form-control" disabled="" value="@(FabricVersion)"/>
|
|
||||||
<label class="mb-2 form-label">
|
|
||||||
<TL>Fabric loader version</TL>
|
|
||||||
</label>
|
|
||||||
<input class="mb-2 form-control" disabled="" value="@(LoaderVersion)"/>
|
|
||||||
<label class="mb-2 form-label">
|
|
||||||
<TL>Minecraft version</TL>
|
|
||||||
</label>
|
|
||||||
<select class="mb-2 form-select" @bind="CurrentVersion">
|
|
||||||
@foreach (var version in Versions)
|
|
||||||
{
|
|
||||||
if (version == CurrentVersion)
|
|
||||||
{
|
|
||||||
<option value="@(version)" selected="">@(version)</option>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<option value="@(version)">@(version)</option>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
<WButton
|
|
||||||
OnClick="Save"
|
|
||||||
Text="@(TranslationService.Translate("Change"))"
|
|
||||||
WorkingText="@(TranslationService.Translate("Changing"))"
|
|
||||||
CssClasses="btn-primary">
|
|
||||||
</WButton>
|
|
||||||
</LazyLoader>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@code
|
|
||||||
{
|
|
||||||
[CascadingParameter]
|
|
||||||
public Server CurrentServer { get; set; }
|
|
||||||
|
|
||||||
private string[] Versions = Array.Empty<string>();
|
|
||||||
private string CurrentVersion = "";
|
|
||||||
|
|
||||||
private string FabricVersion = "";
|
|
||||||
private string LoaderVersion = "";
|
|
||||||
|
|
||||||
|
|
||||||
private async Task Load(LazyLoader lazyLoader)
|
|
||||||
{
|
|
||||||
// Fabric version
|
|
||||||
|
|
||||||
var fabricVersion = LiveMigrateVar(
|
|
||||||
"FABRIC_VERSION",
|
|
||||||
await FabricService.GetLatestInstallerVersion(),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
FabricVersion = fabricVersion.Value;
|
|
||||||
|
|
||||||
// Fabric loader version
|
|
||||||
|
|
||||||
var loaderVersion = LiveMigrateVar(
|
|
||||||
"LOADER_VERSION",
|
|
||||||
await FabricService.GetLatestLoaderVersion(),
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
LoaderVersion = loaderVersion.Value;
|
|
||||||
|
|
||||||
// Minecraft versions
|
|
||||||
|
|
||||||
Versions = await FabricService.GetGameVersions();
|
|
||||||
|
|
||||||
var mcVersion = LiveMigrateVar(
|
|
||||||
"MC_VERSION",
|
|
||||||
Versions.First()
|
|
||||||
);
|
|
||||||
|
|
||||||
CurrentVersion = mcVersion.Value;
|
|
||||||
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ServerVariable LiveMigrateVar(string key, string value, bool overwrite = false)
|
|
||||||
{
|
|
||||||
var v = CurrentServer.Variables.FirstOrDefault(x => x.Key == key);
|
|
||||||
|
|
||||||
if (v == null)
|
|
||||||
{
|
|
||||||
CurrentServer.Variables.Add(new()
|
|
||||||
{
|
|
||||||
Key = key,
|
|
||||||
Value = value
|
|
||||||
});
|
|
||||||
|
|
||||||
ServerRepository.Update(CurrentServer);
|
|
||||||
|
|
||||||
return CurrentServer.Variables.First(x => x.Key == key);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(v.Value) || overwrite)
|
|
||||||
{
|
|
||||||
v.Value = value;
|
|
||||||
|
|
||||||
ServerRepository.Update(CurrentServer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task Save()
|
|
||||||
{
|
|
||||||
var vars = CurrentServer.Variables;
|
|
||||||
var versionVar = vars.First(x => x.Key == "MC_VERSION");
|
|
||||||
|
|
||||||
versionVar.Value = CurrentVersion;
|
|
||||||
|
|
||||||
// This searches for the display name of a version using the constructed full version
|
|
||||||
var version = ParseHelper.MinecraftToInt(CurrentVersion);
|
|
||||||
|
|
||||||
var serverImage = ImageRepository
|
|
||||||
.Get()
|
|
||||||
.Include(x => x.DockerImages)
|
|
||||||
.First(x => x.Id == CurrentServer.Image.Id);
|
|
||||||
|
|
||||||
var dockerImages = serverImage.DockerImages;
|
|
||||||
|
|
||||||
var dockerImageToUpdate = dockerImages.Last();
|
|
||||||
|
|
||||||
if (version < 1130)
|
|
||||||
{
|
|
||||||
dockerImageToUpdate = dockerImages.First(x => x.Name.Contains("8"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (version >= 1130)
|
|
||||||
{
|
|
||||||
dockerImageToUpdate = dockerImages.First(x => x.Name.Contains("11"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (version >= 1170)
|
|
||||||
{
|
|
||||||
dockerImageToUpdate = dockerImages.First(x => x.Name.Contains("16"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (version >= 1190)
|
|
||||||
{
|
|
||||||
dockerImageToUpdate = dockerImages.First(x => x.Name.Contains("17"));
|
|
||||||
}
|
|
||||||
|
|
||||||
CurrentServer.DockerImageIndex = dockerImages.IndexOf(dockerImageToUpdate);
|
|
||||||
|
|
||||||
ServerRepository.Update(CurrentServer);
|
|
||||||
|
|
||||||
await ServerService.Reinstall(CurrentServer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
@using Moonlight.App.Services
|
|
||||||
@using Microsoft.EntityFrameworkCore
|
|
||||||
@using Moonlight.App.Database.Entities
|
|
||||||
@using Moonlight.App.Repositories
|
|
||||||
@using Moonlight.App.Repositories.Servers
|
|
||||||
@using Logging.Net
|
|
||||||
@using Moonlight.App.Helpers
|
|
||||||
|
|
||||||
@inject ServerService ServerService
|
|
||||||
@inject ServerRepository ServerRepository
|
|
||||||
@inject ImageRepository ImageRepository
|
|
||||||
@inject ForgeService ForgeService
|
|
||||||
@inject SmartTranslateService TranslationService
|
|
||||||
|
|
||||||
<div class="col">
|
|
||||||
<div class="card card-body">
|
|
||||||
<LazyLoader Load="Load">
|
|
||||||
<label class="mb-2 form-label"><TL>Forge version</TL></label>
|
|
||||||
<select class="mb-2 form-select" @bind="CurrentVersion">
|
|
||||||
@foreach (var version in Versions.Keys)
|
|
||||||
{
|
|
||||||
if (DisplayToData(version) == CurrentVersion)
|
|
||||||
{
|
|
||||||
<option value="@(DisplayToData(version))" selected="">@(version)</option>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<option value="@(DisplayToData(version))">@(version)</option>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</select>
|
|
||||||
<WButton
|
|
||||||
OnClick="Save"
|
|
||||||
Text="@(TranslationService.Translate("Change"))"
|
|
||||||
WorkingText="@(TranslationService.Translate("Changing"))"
|
|
||||||
CssClasses="btn-primary">
|
|
||||||
</WButton>
|
|
||||||
</LazyLoader>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@code
|
|
||||||
{
|
|
||||||
[CascadingParameter]
|
|
||||||
public Server CurrentServer { get; set; }
|
|
||||||
|
|
||||||
private Dictionary<string, string> Versions = new();
|
|
||||||
private string CurrentVersion = "";
|
|
||||||
|
|
||||||
|
|
||||||
private async Task Load(LazyLoader lazyLoader)
|
|
||||||
{
|
|
||||||
Versions = await ForgeService.GetVersions();
|
|
||||||
|
|
||||||
var vars = CurrentServer.Variables;
|
|
||||||
var versionVar = vars.FirstOrDefault(x => x.Key == "FORGE_VERSION");
|
|
||||||
|
|
||||||
// Live migration
|
|
||||||
if (versionVar == null)
|
|
||||||
{
|
|
||||||
CurrentServer.Variables.Add(new ()
|
|
||||||
{
|
|
||||||
Key = "FORGE_VERSION",
|
|
||||||
Value = LatestVersion()
|
|
||||||
});
|
|
||||||
|
|
||||||
ServerRepository.Update(CurrentServer);
|
|
||||||
|
|
||||||
versionVar = vars.First(x => x.Key == "FORGE_VERSION");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(versionVar.Value))
|
|
||||||
{
|
|
||||||
versionVar.Value = LatestVersion();
|
|
||||||
ServerRepository.Update(CurrentServer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CurrentVersion = versionVar.Value;
|
|
||||||
|
|
||||||
await InvokeAsync(StateHasChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string DisplayToData(string display)
|
|
||||||
{
|
|
||||||
return display
|
|
||||||
.Replace("-recommended", "")
|
|
||||||
.Replace("-latest", "") + "-" + Versions[display];
|
|
||||||
}
|
|
||||||
|
|
||||||
private string LatestVersion()
|
|
||||||
{
|
|
||||||
var versionsSorted = Versions.Keys
|
|
||||||
.OrderByDescending(ParseHelper.MinecraftToInt);
|
|
||||||
|
|
||||||
return DisplayToData(versionsSorted.First());
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task Save()
|
|
||||||
{
|
|
||||||
var vars = CurrentServer.Variables;
|
|
||||||
var versionVar = vars.First(x => x.Key == "FORGE_VERSION");
|
|
||||||
|
|
||||||
versionVar.Value = CurrentVersion;
|
|
||||||
|
|
||||||
// This searches for the display name of a version using the constructed full version
|
|
||||||
var version = ParseHelper.MinecraftToInt(
|
|
||||||
Versions.First(
|
|
||||||
x => DisplayToData(x.Key) == CurrentVersion).Key);
|
|
||||||
|
|
||||||
var serverImage = ImageRepository
|
|
||||||
.Get()
|
|
||||||
.Include(x => x.DockerImages)
|
|
||||||
.First(x => x.Id == CurrentServer.Image.Id);
|
|
||||||
|
|
||||||
var dockerImages = serverImage.DockerImages;
|
|
||||||
|
|
||||||
var dockerImageToUpdate = dockerImages.Last();
|
|
||||||
|
|
||||||
if (version < 1130)
|
|
||||||
{
|
|
||||||
dockerImageToUpdate = dockerImages.First(x => x.Name.Contains("8"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (version >= 1130)
|
|
||||||
{
|
|
||||||
dockerImageToUpdate = dockerImages.First(x => x.Name.Contains("11"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (version >= 1170)
|
|
||||||
{
|
|
||||||
dockerImageToUpdate = dockerImages.First(x => x.Name.Contains("16"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (version >= 1190)
|
|
||||||
{
|
|
||||||
dockerImageToUpdate = dockerImages.First(x => x.Name.Contains("17"));
|
|
||||||
}
|
|
||||||
|
|
||||||
CurrentServer.DockerImageIndex = dockerImages.IndexOf(dockerImageToUpdate);
|
|
||||||
|
|
||||||
ServerRepository.Update(CurrentServer);
|
|
||||||
|
|
||||||
await ServerService.Reinstall(CurrentServer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
@using Moonlight.App.Database.Entities
|
|
||||||
@using Moonlight.App.Services
|
|
||||||
@using Moonlight.App.Services.Interop
|
|
||||||
|
|
||||||
@inject WebSpaceService WebSpaceService
|
|
||||||
@inject SmartTranslateService SmartTranslateService
|
|
||||||
@inject AlertService AlertService
|
|
||||||
|
|
||||||
<div class="row gy-5 g-xl-10">
|
|
||||||
<div class="col-xl-4 mb-xl-10">
|
|
||||||
<div class="card h-md-100">
|
|
||||||
<div class="card-body d-flex flex-column flex-center">
|
|
||||||
<img class="img-fluid" src="https://image.thum.io/get/http://@(CurrentWebSpace.Domain)" alt="Website screenshot"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-xl-8 mb-5 mb-xl-10">
|
|
||||||
<div class="card card-flush h-xl-100">
|
|
||||||
<div class="card-body pt-2">
|
|
||||||
<LazyLoader @ref="LazyLoader" Load="Load">
|
|
||||||
<div class="row mt-5">
|
|
||||||
<div class="card border">
|
|
||||||
<div class="card-header">
|
|
||||||
<span class="card-title">
|
|
||||||
<TL>SSL certificates</TL>
|
|
||||||
</span>
|
|
||||||
<div class="card-toolbar">
|
|
||||||
<WButton Text="@(SmartTranslateService.Translate("Issue certificate"))"
|
|
||||||
WorkingText="@(SmartTranslateService.Translate("Working"))"
|
|
||||||
CssClasses="btn-success"
|
|
||||||
OnClick="IssueCertificate">
|
|
||||||
</WButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</LazyLoader>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@code
|
|
||||||
{
|
|
||||||
[CascadingParameter]
|
|
||||||
public WebSpace CurrentWebSpace { get; set; }
|
|
||||||
|
|
||||||
private LazyLoader LazyLoader;
|
|
||||||
|
|
||||||
private Task Load(LazyLoader lazyLoader)
|
|
||||||
{
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task IssueCertificate()
|
|
||||||
{
|
|
||||||
await WebSpaceService.IssueSslCertificate(CurrentWebSpace);
|
|
||||||
await AlertService.Success(SmartTranslateService.Translate("Lets Encrypt certificate successfully issued"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
@using Moonlight.App.Database.Entities
|
|
||||||
@using Moonlight.App.Services
|
|
||||||
|
|
||||||
@inject WebSpaceService WebSpaceService
|
|
||||||
|
|
||||||
<div class="card card-flush h-xl-100">
|
|
||||||
<div class="card-body pt-2">
|
|
||||||
<div class="mt-7 row fv-row mb-7">
|
|
||||||
<div class="col-md-3 text-md-start">
|
|
||||||
<label class="fs-6 fw-semibold form-label mt-3">
|
|
||||||
<TL>Sftp Host</TL>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-9">
|
|
||||||
<input type="text" class="form-control form-control-solid disabled" disabled="disabled" value="@(CurrentWebSpace.CloudPanel.Host)">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row fv-row mb-7">
|
|
||||||
<div class="col-md-3 text-md-start">
|
|
||||||
<label class="fs-6 fw-semibold form-label mt-3">
|
|
||||||
<TL>Sftp Port</TL>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-9">
|
|
||||||
<input type="text" class="form-control form-control-solid disabled" disabled="disabled" value="21">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row fv-row mb-7">
|
|
||||||
<div class="col-md-3 text-md-start">
|
|
||||||
<label class="fs-6 fw-semibold form-label mt-3">
|
|
||||||
<TL>Sftp Username</TL>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-9">
|
|
||||||
<input type="text" class="form-control form-control-solid disabled" disabled="disabled" value="@(CurrentWebSpace.UserName)">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row fv-row mb-7">
|
|
||||||
<div class="col-md-3 text-md-start">
|
|
||||||
<label class="fs-6 fw-semibold form-label mt-3">
|
|
||||||
<TL>Sftp Password</TL>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-9">
|
|
||||||
<input type="text" class="form-control form-control-solid disabled blur-unless-hover" disabled="disabled" value="@(CurrentWebSpace.Password)">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@code
|
|
||||||
{
|
|
||||||
[CascadingParameter]
|
|
||||||
public WebSpace CurrentWebSpace { get; set; }
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
@using Moonlight.App.Database.Entities
|
||||||
|
@using Moonlight.App.Services
|
||||||
|
|
||||||
|
@inject WebsiteService WebsiteService
|
||||||
|
@inject SmartTranslateService SmartTranslateService
|
||||||
|
|
||||||
|
<div class="row gy-5 g-xl-10">
|
||||||
|
<div class="col-xl-4 mb-xl-10">
|
||||||
|
<div class="card h-md-100">
|
||||||
|
<div class="card-body d-flex flex-column flex-center">
|
||||||
|
<img class="img-fluid" src="https://image.thum.io/get/http://@(CurrentWebsite.BaseDomain)" alt="Website screenshot"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-xl-8 mb-5 mb-xl-10">
|
||||||
|
<div class="card card-flush h-xl-100">
|
||||||
|
<div class="card-body pt-2">
|
||||||
|
<LazyLoader @ref="LazyLoader" Load="Load">
|
||||||
|
<div class="row mt-5">
|
||||||
|
<div class="card border">
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-title">
|
||||||
|
<TL>SSL certificates</TL>
|
||||||
|
</span>
|
||||||
|
<div class="card-toolbar">
|
||||||
|
<WButton Text="@(SmartTranslateService.Translate("Issue certificate"))"
|
||||||
|
WorkingText="@(SmartTranslateService.Translate("Working"))"
|
||||||
|
CssClasses="btn-success"
|
||||||
|
OnClick="CreateCertificate">
|
||||||
|
</WButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
@if (Certs.Any())
|
||||||
|
{
|
||||||
|
<table class="table align-middle gs-0 gy-3">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="p-0 w-50px"></th>
|
||||||
|
<th class="p-0 min-w-150px"></th>
|
||||||
|
<th class="p-0 min-w-120px"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach (var cert in Certs)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class="symbol symbol-50px me-2">
|
||||||
|
<span class="symbol-label">
|
||||||
|
<i class="bx bx-md bx-receipt text-dark"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="text-dark fw-bold fs-6">@(cert)</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-end">
|
||||||
|
<WButton Text="@(SmartTranslateService.Translate("Delete"))"
|
||||||
|
WorkingText="@(SmartTranslateService.Translate("Working"))"
|
||||||
|
CssClasses="btn btn-danger"
|
||||||
|
OnClick="() => DeleteCertificate(cert)">
|
||||||
|
</WButton>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
<TL>No SSL certificates found</TL>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</LazyLoader>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code
|
||||||
|
{
|
||||||
|
[CascadingParameter]
|
||||||
|
public Website CurrentWebsite { get; set; }
|
||||||
|
|
||||||
|
private string[] Certs;
|
||||||
|
|
||||||
|
private LazyLoader LazyLoader;
|
||||||
|
|
||||||
|
private async Task Load(LazyLoader lazyLoader)
|
||||||
|
{
|
||||||
|
await lazyLoader.SetText("Loading certificates");
|
||||||
|
Certs = await WebsiteService.GetSslCertificates(CurrentWebsite);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CreateCertificate()
|
||||||
|
{
|
||||||
|
await WebsiteService.CreateSslCertificate(CurrentWebsite);
|
||||||
|
await LazyLoader.Reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DeleteCertificate(string name)
|
||||||
|
{
|
||||||
|
await WebsiteService.DeleteSslCertificate(CurrentWebsite, name);
|
||||||
|
await LazyLoader.Reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user