Added new file manager, renamed websites to webspaces. Added cloudpanel integration partialy. Added generic repos and more stuff
This commit is contained in:
parent
8929c2793d
commit
fd008e56aa
40 changed files with 3430 additions and 691 deletions
83
Moonlight/App/ApiClients/CloudPanel/CloudPanelApiHelper.cs
Normal file
83
Moonlight/App/ApiClients/CloudPanel/CloudPanelApiHelper.cs
Normal file
|
@ -0,0 +1,83 @@
|
|||
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;
|
||||
}
|
||||
}
|
32
Moonlight/App/ApiClients/CloudPanel/CloudPanelException.cs
Normal file
32
Moonlight/App/ApiClients/CloudPanel/CloudPanelException.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
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)
|
||||
{
|
||||
}
|
||||
}
|
16
Moonlight/App/ApiClients/CloudPanel/Requests/AddPhpSite.cs
Normal file
16
Moonlight/App/ApiClients/CloudPanel/Requests/AddPhpSite.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
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; } = "";
|
||||
}
|
|
@ -43,6 +43,10 @@ public class DataContext : DbContext
|
|||
public DbSet<Website> Websites { get; set; }
|
||||
public DbSet<StatisticsData> Statistics { 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; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
|
|
10
Moonlight/App/Database/Entities/CloudPanel.cs
Normal file
10
Moonlight/App/Database/Entities/CloudPanel.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Moonlight.App.Database.Entities;
|
||||
|
||||
public class CloudPanel
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = "";
|
||||
public string ApiUrl { get; set; } = "";
|
||||
public string ApiKey { get; set; } = "";
|
||||
public string Host { get; set; } = "";
|
||||
}
|
9
Moonlight/App/Database/Entities/MySqlDatabase.cs
Normal file
9
Moonlight/App/Database/Entities/MySqlDatabase.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
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; } = "";
|
||||
}
|
13
Moonlight/App/Database/Entities/WebSpace.cs
Normal file
13
Moonlight/App/Database/Entities/WebSpace.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
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; }
|
||||
}
|
1099
Moonlight/App/Database/Migrations/20230419120719_AddedCloudPanelModels.Designer.cs
generated
Normal file
1099
Moonlight/App/Database/Migrations/20230419120719_AddedCloudPanelModels.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,121 @@
|
|||
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");
|
||||
}
|
||||
}
|
||||
}
|
1103
Moonlight/App/Database/Migrations/20230419125155_AddedHostFieldToCloudPanelModel.Designer.cs
generated
Normal file
1103
Moonlight/App/Database/Migrations/20230419125155_AddedHostFieldToCloudPanelModel.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,29 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Moonlight.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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,6 +19,33 @@ namespace Moonlight.App.Database.Migrations
|
|||
.HasAnnotation("ProductVersion", "7.0.3")
|
||||
.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 =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
@ -281,6 +308,30 @@ namespace Moonlight.App.Database.Migrations
|
|||
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 =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
@ -748,6 +799,43 @@ namespace Moonlight.App.Database.Migrations
|
|||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("CloudPanelId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Domain")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("OwnerId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Password")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("VHostTemplate")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CloudPanelId");
|
||||
|
||||
b.HasIndex("OwnerId");
|
||||
|
||||
b.ToTable("WebSpaces");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
@ -828,6 +916,17 @@ namespace Moonlight.App.Database.Migrations
|
|||
.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 =>
|
||||
{
|
||||
b.HasOne("Moonlight.App.Database.Entities.Node", null)
|
||||
|
@ -932,6 +1031,25 @@ namespace Moonlight.App.Database.Migrations
|
|||
b.Navigation("CurrentSubscription");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.App.Database.Entities.CloudPanel", "CloudPanel")
|
||||
.WithMany()
|
||||
.HasForeignKey("CloudPanelId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Moonlight.App.Database.Entities.User", "Owner")
|
||||
.WithMany()
|
||||
.HasForeignKey("OwnerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("CloudPanel");
|
||||
|
||||
b.Navigation("Owner");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.App.Database.Entities.User", "Owner")
|
||||
|
@ -971,6 +1089,11 @@ namespace Moonlight.App.Database.Migrations
|
|||
|
||||
b.Navigation("Variables");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b =>
|
||||
{
|
||||
b.Navigation("Databases");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
|
|
206
Moonlight/App/Helpers/Files/SftpFileAccess.cs
Normal file
206
Moonlight/App/Helpers/Files/SftpFileAccess.cs
Normal file
|
@ -0,0 +1,206 @@
|
|||
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);
|
||||
}
|
||||
}
|
58
Moonlight/App/Helpers/SyncStreamAdapter.cs
Normal file
58
Moonlight/App/Helpers/SyncStreamAdapter.cs
Normal file
|
@ -0,0 +1,58 @@
|
|||
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);
|
||||
}
|
||||
}
|
19
Moonlight/App/Models/Forms/CloudPanelDataModel.cs
Normal file
19
Moonlight/App/Models/Forms/CloudPanelDataModel.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
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; }
|
||||
}
|
40
Moonlight/App/Repositories/Repository.cs
Normal file
40
Moonlight/App/Repositories/Repository.cs
Normal file
|
@ -0,0 +1,40 @@
|
|||
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();
|
||||
}
|
||||
}
|
|
@ -7,17 +7,17 @@ public class SmartDeployService
|
|||
{
|
||||
private readonly NodeRepository NodeRepository;
|
||||
private readonly PleskServerRepository PleskServerRepository;
|
||||
private readonly WebsiteService WebsiteService;
|
||||
private readonly WebSpaceService WebSpaceService;
|
||||
private readonly NodeService NodeService;
|
||||
|
||||
public SmartDeployService(
|
||||
NodeRepository nodeRepository,
|
||||
NodeService nodeService, PleskServerRepository pleskServerRepository, WebsiteService websiteService)
|
||||
NodeService nodeService, PleskServerRepository pleskServerRepository, WebSpaceService webSpaceService)
|
||||
{
|
||||
NodeRepository = nodeRepository;
|
||||
NodeService = nodeService;
|
||||
PleskServerRepository = pleskServerRepository;
|
||||
WebsiteService = websiteService;
|
||||
WebSpaceService = webSpaceService;
|
||||
}
|
||||
|
||||
public async Task<Node?> GetNode()
|
||||
|
@ -44,10 +44,7 @@ public class SmartDeployService
|
|||
|
||||
foreach (var pleskServer in PleskServerRepository.Get().ToArray())
|
||||
{
|
||||
if (await WebsiteService.IsHostUp(pleskServer))
|
||||
{
|
||||
result.Add(pleskServer);
|
||||
}
|
||||
result.Add(pleskServer);
|
||||
}
|
||||
|
||||
return result.FirstOrDefault();
|
||||
|
|
|
@ -9,7 +9,7 @@ public class StatisticsCaptureService
|
|||
private readonly ConfigService ConfigService;
|
||||
private readonly StatisticsRepository StatisticsRepository;
|
||||
private readonly IServiceScopeFactory ServiceScopeFactory;
|
||||
private readonly WebsiteService WebsiteService;
|
||||
private readonly WebSpaceService WebSpaceService;
|
||||
private readonly PleskServerRepository PleskServerRepository;
|
||||
private PeriodicTimer Timer;
|
||||
|
||||
|
@ -21,7 +21,7 @@ public class StatisticsCaptureService
|
|||
DataContext = provider.GetRequiredService<DataContext>();
|
||||
ConfigService = configService;
|
||||
StatisticsRepository = provider.GetRequiredService<StatisticsRepository>();
|
||||
WebsiteService = provider.GetRequiredService<WebsiteService>();
|
||||
WebSpaceService = provider.GetRequiredService<WebSpaceService>();
|
||||
PleskServerRepository = provider.GetRequiredService<PleskServerRepository>();
|
||||
|
||||
var config = ConfigService.GetSection("Moonlight").GetSection("Statistics");
|
||||
|
@ -48,7 +48,7 @@ public class StatisticsCaptureService
|
|||
|
||||
await foreach (var pleskServer in PleskServerRepository.Get())
|
||||
{
|
||||
databases += (await WebsiteService.GetDefaultDatabaseServer(pleskServer)).DbCount;
|
||||
//databases += (await WebsiteService.GetDefaultDatabaseServer(pleskServer)).DbCount;
|
||||
}
|
||||
|
||||
StatisticsRepository.Add("statistics.databasesCount", databases);
|
||||
|
|
166
Moonlight/App/Services/WebSpaceService.cs
Normal file
166
Moonlight/App/Services/WebSpaceService.cs
Normal file
|
@ -0,0 +1,166 @@
|
|||
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 CloudPanelApiHelper CloudPanelApiHelper;
|
||||
|
||||
public WebSpaceService(Repository<CloudPanel> cloudPanelRepository, Repository<WebSpace> webSpaceRepository, CloudPanelApiHelper cloudPanelApiHelper)
|
||||
{
|
||||
CloudPanelRepository = cloudPanelRepository;
|
||||
WebSpaceRepository = webSpaceRepository;
|
||||
CloudPanelApiHelper = cloudPanelApiHelper;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
//var res = await PleskApiHelper.Get<ServerStatus>(pleskServer, "server");
|
||||
|
||||
return true;
|
||||
|
||||
//if (res != null)
|
||||
// return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<bool> IsHostUp(WebSpace w)
|
||||
{
|
||||
var webSpace = EnsureData(w);
|
||||
|
||||
return await IsHostUp(webSpace.CloudPanel);
|
||||
}
|
||||
|
||||
#region SSL
|
||||
public async Task<string[]> GetSslCertificates(WebSpace w)
|
||||
{
|
||||
var certs = new List<string>();
|
||||
return certs.ToArray();
|
||||
}
|
||||
|
||||
public async Task CreateSslCertificate(WebSpace w)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public async Task DeleteSslCertificate(WebSpace w, string name)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Databases
|
||||
|
||||
public async Task<Models.Plesk.Resources.Database[]> GetDatabases(WebSpace w)
|
||||
{
|
||||
return Array.Empty<Models.Plesk.Resources.Database>();
|
||||
}
|
||||
|
||||
public async Task CreateDatabase(WebSpace w, string name, string password)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public async Task DeleteDatabase(WebSpace w, Models.Plesk.Resources.Database database)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
|
@ -1,383 +0,0 @@
|
|||
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;
|
||||
}
|
||||
}
|
|
@ -41,6 +41,7 @@
|
|||
<PackageReference Include="PteroConsole.NET" Version="1.0.4" />
|
||||
<PackageReference Include="QRCoder" Version="1.4.3" />
|
||||
<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="XtermBlazor" Version="1.6.1" />
|
||||
</ItemGroup>
|
||||
|
@ -67,6 +68,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="App\ApiClients\CloudPanel\Resources\" />
|
||||
<Folder Include="App\Http\Middleware" />
|
||||
<Folder Include="App\Models\Daemon\Requests" />
|
||||
<Folder Include="App\Models\Google\Resources" />
|
||||
|
|
|
@ -2,6 +2,7 @@ using BlazorDownloadFile;
|
|||
using BlazorTable;
|
||||
using CurrieTechnologies.Razor.SweetAlert2;
|
||||
using Logging.Net;
|
||||
using Moonlight.App.ApiClients.CloudPanel;
|
||||
using Moonlight.App.Database;
|
||||
using Moonlight.App.Helpers;
|
||||
using Moonlight.App.LogMigrator;
|
||||
|
@ -76,6 +77,7 @@ namespace Moonlight
|
|||
builder.Services.AddScoped<AuditLogEntryRepository>();
|
||||
builder.Services.AddScoped<ErrorLogEntryRepository>();
|
||||
builder.Services.AddScoped<SecurityLogEntryRepository>();
|
||||
builder.Services.AddScoped(typeof(Repository<>));
|
||||
|
||||
// Services
|
||||
builder.Services.AddSingleton<ConfigService>();
|
||||
|
@ -102,7 +104,7 @@ namespace Moonlight
|
|||
builder.Services.AddScoped<NotificationClientService>();
|
||||
builder.Services.AddScoped<ModalService>();
|
||||
builder.Services.AddScoped<SmartDeployService>();
|
||||
builder.Services.AddScoped<WebsiteService>();
|
||||
builder.Services.AddScoped<WebSpaceService>();
|
||||
builder.Services.AddScoped<StatisticsViewService>();
|
||||
|
||||
builder.Services.AddScoped<GoogleOAuth2Service>();
|
||||
|
@ -136,6 +138,7 @@ namespace Moonlight
|
|||
builder.Services.AddSingleton<HostSystemHelper>();
|
||||
builder.Services.AddScoped<DaemonApiHelper>();
|
||||
builder.Services.AddScoped<PleskApiHelper>();
|
||||
builder.Services.AddScoped<CloudPanelApiHelper>();
|
||||
|
||||
// Background services
|
||||
builder.Services.AddSingleton<DiscordBotService>();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@using Moonlight.App.Helpers.Files
|
||||
@using Logging.Net
|
||||
|
||||
<div class="badge badge-lg badge-light-primary">
|
||||
<div class="d-flex align-items-center flex-wrap">
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
<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">
|
||||
<li class="nav-item mt-2">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 0 ? "active" : "")" href="/admin/websites">
|
||||
<TL>Websites</TL>
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 0 ? "active" : "")" href="/admin/webspaces">
|
||||
<TL>Webspaces</TL>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item mt-2">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 1 ? "active" : "")" href="/admin/websites/servers">
|
||||
<TL>Plesk servers</TL>
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 1 ? "active" : "")" href="/admin/webspaces/servers">
|
||||
<TL>Cloud panels</TL>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
|
@ -1,14 +1,14 @@
|
|||
@using Moonlight.App.Database.Entities
|
||||
@using Moonlight.App.Services
|
||||
|
||||
@inject WebsiteService WebsiteService
|
||||
@inject WebSpaceService WebSpaceService
|
||||
@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"/>
|
||||
<img class="img-fluid" src="https://image.thum.io/get/http://@(CurrentWebSpace.Domain)" alt="Website screenshot"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -85,7 +85,7 @@
|
|||
@code
|
||||
{
|
||||
[CascadingParameter]
|
||||
public Website CurrentWebsite { get; set; }
|
||||
public WebSpace CurrentWebSpace { get; set; }
|
||||
|
||||
private string[] Certs;
|
||||
|
||||
|
@ -94,18 +94,18 @@
|
|||
private async Task Load(LazyLoader lazyLoader)
|
||||
{
|
||||
await lazyLoader.SetText("Loading certificates");
|
||||
Certs = await WebsiteService.GetSslCertificates(CurrentWebsite);
|
||||
Certs = await WebSpaceService.GetSslCertificates(CurrentWebSpace);
|
||||
}
|
||||
|
||||
private async Task CreateCertificate()
|
||||
{
|
||||
await WebsiteService.CreateSslCertificate(CurrentWebsite);
|
||||
await WebSpaceService.CreateSslCertificate(CurrentWebSpace);
|
||||
await LazyLoader.Reload();
|
||||
}
|
||||
|
||||
private async Task DeleteCertificate(string name)
|
||||
{
|
||||
await WebsiteService.DeleteSslCertificate(CurrentWebsite, name);
|
||||
await WebSpaceService.DeleteSslCertificate(CurrentWebSpace, name);
|
||||
await LazyLoader.Reload();
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
@using Moonlight.App.Services
|
||||
|
||||
@inject SmartTranslateService SmartTranslateService
|
||||
@inject WebsiteService WebsiteService
|
||||
@inject WebSpaceService WebSpaceService
|
||||
|
||||
<LazyLoader @ref="LazyLoader" Load="Load">
|
||||
<div class="card w-100 mb-4">
|
||||
|
@ -93,7 +93,7 @@
|
|||
else
|
||||
{
|
||||
<div class="alert alert-warning">
|
||||
<TL>No databases found for this website</TL>
|
||||
<TL>No databases found for this webspace</TL>
|
||||
</div>
|
||||
}
|
||||
</LazyLoader>
|
||||
|
@ -101,7 +101,7 @@
|
|||
@code
|
||||
{
|
||||
[CascadingParameter]
|
||||
public Website CurrentWebsite { get; set; }
|
||||
public WebSpace CurrentWebSpace { get; set; }
|
||||
|
||||
private LazyLoader LazyLoader;
|
||||
private Database[] Databases;
|
||||
|
@ -112,25 +112,19 @@
|
|||
|
||||
private async Task Load(LazyLoader arg)
|
||||
{
|
||||
Databases = await WebsiteService.GetDatabases(CurrentWebsite);
|
||||
|
||||
if (Databases.Any())
|
||||
{
|
||||
DatabaseServer = (await WebsiteService.GetDefaultDatabaseServer(CurrentWebsite))!;
|
||||
Host = await WebsiteService.GetHost(CurrentWebsite);
|
||||
}
|
||||
Databases = await WebSpaceService.GetDatabases(CurrentWebSpace);
|
||||
}
|
||||
|
||||
private async Task OnValidSubmit()
|
||||
{
|
||||
await WebsiteService.CreateDatabase(CurrentWebsite, Model.Name, Model.Password);
|
||||
await WebSpaceService.CreateDatabase(CurrentWebSpace, Model.Name, Model.Password);
|
||||
Model = new();
|
||||
await LazyLoader.Reload();
|
||||
}
|
||||
|
||||
private async Task DeleteDatabase(Database database)
|
||||
{
|
||||
await WebsiteService.DeleteDatabase(CurrentWebsite, database);
|
||||
await WebSpaceService.DeleteDatabase(CurrentWebSpace, database);
|
||||
await LazyLoader.Reload();
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
@using Moonlight.App.Services
|
||||
@using Moonlight.Shared.Components.FileManagerPartials
|
||||
|
||||
@inject WebsiteService WebsiteService
|
||||
@inject WebSpaceService WebSpaceService
|
||||
|
||||
<LazyLoader Load="Load">
|
||||
<FileManager Access="Access">
|
||||
|
@ -13,12 +13,12 @@
|
|||
@code
|
||||
{
|
||||
[CascadingParameter]
|
||||
public Website CurrentWebsite { get; set; }
|
||||
public WebSpace CurrentWebSpace { get; set; }
|
||||
|
||||
private FileAccess Access;
|
||||
|
||||
private async Task Load(LazyLoader arg)
|
||||
{
|
||||
Access = await WebsiteService.CreateFileAccess(CurrentWebsite);
|
||||
Access = await WebSpaceService.CreateFileAccess(CurrentWebSpace);
|
||||
}
|
||||
}
|
55
Moonlight/Shared/Components/WebsiteControl/WebSpaceFtp.razor
Normal file
55
Moonlight/Shared/Components/WebsiteControl/WebSpaceFtp.razor
Normal file
|
@ -0,0 +1,55 @@
|
|||
@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>Ftp 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>Ftp 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>Ftp 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>Ftp 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; }
|
||||
}
|
|
@ -10,8 +10,8 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="d-flex flex-column">
|
||||
<div class="mb-1 fs-4">@(Website.BaseDomain)</div>
|
||||
<div class="text-muted fs-5">@(Website.PleskServer.Name)</div>
|
||||
<div class="mb-1 fs-4">@(WebSpace.Domain)</div>
|
||||
<div class="text-muted fs-5">@(WebSpace.CloudPanel.Name)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -24,22 +24,22 @@
|
|||
<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">
|
||||
<li class="nav-item mt-2">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 0 ? "active" : "")" href="/website/@(Website.Id)">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 0 ? "active" : "")" href="/webspace/@(WebSpace.Id)">
|
||||
<TL>Dashboard</TL>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item mt-2">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 1 ? "active" : "")" href="/website/@(Website.Id)/files">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 1 ? "active" : "")" href="/webspace/@(WebSpace.Id)/files">
|
||||
<TL>Files</TL>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item mt-2">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 2 ? "active" : "")" href="/website/@(Website.Id)/ftp">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 2 ? "active" : "")" href="/webspace/@(WebSpace.Id)/ftp">
|
||||
<TL>Ftp</TL>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item mt-2">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 3 ? "active" : "")" href="/website/@(Website.Id)/databases">
|
||||
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 3 ? "active" : "")" href="/webspace/@(WebSpace.Id)/databases">
|
||||
<TL>Databases</TL>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -53,5 +53,5 @@
|
|||
public int Index { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Website Website { get; set; }
|
||||
public WebSpace WebSpace { get; set; }
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
@using Moonlight.App.Database.Entities
|
||||
@using Moonlight.App.Services
|
||||
|
||||
@inject WebsiteService WebsiteService
|
||||
|
||||
<div class="card card-flush h-xl-100">
|
||||
<div class="card-body pt-2">
|
||||
<LazyLoader Load="Load">
|
||||
<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>Ftp Host</TL>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<input type="text" class="form-control form-control-solid disabled" disabled="disabled" value="@(FtpHost)">
|
||||
</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>Ftp 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>Ftp Username</TL>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<input type="text" class="form-control form-control-solid disabled" disabled="disabled" value="@(Website.FtpLogin)">
|
||||
</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>Ftp 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="@(Website.FtpPassword)">
|
||||
</div>
|
||||
</div>
|
||||
</LazyLoader>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
[CascadingParameter]
|
||||
public Website Website { get; set; }
|
||||
|
||||
private string FtpHost = "N/A";
|
||||
|
||||
private async Task Load(LazyLoader arg)
|
||||
{
|
||||
FtpHost = await WebsiteService.GetHost(Website.PleskServer);
|
||||
}
|
||||
}
|
|
@ -33,7 +33,7 @@
|
|||
</a>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6 col-xl">
|
||||
<a class="mt-4 card" href="/websites">
|
||||
<a class="mt-4 card" href="/admin/webspaces">
|
||||
<div class="card-body">
|
||||
<div class="row align-items-center gx-0">
|
||||
<div class="col">
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
@page "/admin/websites/servers/edit/{Id:int}"
|
||||
|
||||
@using Moonlight.App.Models.Forms
|
||||
@using Moonlight.App.Repositories
|
||||
@using Moonlight.App.Database.Entities
|
||||
|
||||
@inject PleskServerRepository PleskServerRepository
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<OnlyAdmin>
|
||||
<LazyLoader Load="Load">
|
||||
@if (PleskServer == null)
|
||||
{
|
||||
<div class="d-flex justify-content-center flex-center">
|
||||
<div class="card">
|
||||
<img src="/assets/media/svg/nodata.svg" class="card-img-top w-50 mx-auto pt-5" alt="Not found image"/>
|
||||
<div class="card-body text-center">
|
||||
<h1 class="card-title">
|
||||
<TL>Plesk server not found</TL>
|
||||
</h1>
|
||||
<p class="card-text fs-4">
|
||||
<TL>A plesk server with that id cannot be found</TL>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="card card-body p-10">
|
||||
<SmartForm Model="Model" OnValidSubmit="OnValidSubmit">
|
||||
<label class="form-label">
|
||||
<TL>Name</TL>
|
||||
</label>
|
||||
<div class="input-group mb-5">
|
||||
<InputText @bind-Value="Model.Name" class="form-control"></InputText>
|
||||
</div>
|
||||
<label class="form-label">
|
||||
<TL>Api Url</TL>
|
||||
</label>
|
||||
<div class="input-group mb-5">
|
||||
<InputText @bind-Value="Model.ApiUrl" class="form-control"></InputText>
|
||||
</div>
|
||||
<label class="form-label">
|
||||
<TL>Api Key</TL>
|
||||
</label>
|
||||
<div class="input-group mb-5">
|
||||
<InputText @bind-Value="Model.ApiKey" class="blur-unless-hover form-control"></InputText>
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit" class="btn btn-primary float-end">
|
||||
<TL>Save</TL>
|
||||
</button>
|
||||
</div>
|
||||
</SmartForm>
|
||||
</div>
|
||||
}
|
||||
</LazyLoader>
|
||||
</OnlyAdmin>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public int Id { get; set; }
|
||||
|
||||
private PleskServer? PleskServer;
|
||||
|
||||
private PleskServerDataModel Model = new();
|
||||
|
||||
private Task OnValidSubmit()
|
||||
{
|
||||
PleskServer!.Name = Model.Name;
|
||||
PleskServer.ApiUrl = Model.ApiUrl;
|
||||
PleskServer.ApiKey = Model.ApiKey;
|
||||
|
||||
PleskServerRepository.Update(PleskServer);
|
||||
|
||||
NavigationManager.NavigateTo("/admin/websites/servers");
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task Load(LazyLoader arg)
|
||||
{
|
||||
PleskServer = PleskServerRepository
|
||||
.Get()
|
||||
.FirstOrDefault(x => x.Id == Id);
|
||||
|
||||
if (PleskServer != null)
|
||||
{
|
||||
Model.Name = PleskServer.Name;
|
||||
Model.ApiUrl = PleskServer.ApiUrl;
|
||||
Model.ApiKey = PleskServer.ApiKey;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
@page "/admin/websites/"
|
||||
@page "/admin/webspaces/"
|
||||
|
||||
@using Moonlight.Shared.Components.Navigations
|
||||
@using Moonlight.App.Services
|
||||
|
@ -8,53 +8,53 @@
|
|||
@using BlazorTable
|
||||
|
||||
@inject SmartTranslateService SmartTranslateService
|
||||
@inject WebsiteRepository WebsiteRepository
|
||||
@inject WebsiteService WebsiteService
|
||||
@inject Repository<WebSpace> WebSpaceRepository
|
||||
@inject WebSpaceService WebSpaceService
|
||||
|
||||
<OnlyAdmin>
|
||||
<AdminWebsitesNavigation Index="0"/>
|
||||
<AdminWebspacesNavigation Index="0"/>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header border-0 pt-5">
|
||||
<h3 class="card-title align-items-start flex-column">
|
||||
<span class="card-label fw-bold fs-3 mb-1">
|
||||
<TL>Websites</TL>
|
||||
<TL>Webspaces</TL>
|
||||
</span>
|
||||
</h3>
|
||||
<div class="card-toolbar">
|
||||
<a href="/admin/websites/new" class="btn btn-sm btn-light-success">
|
||||
<a href="/admin/webspaces/new" class="btn btn-sm btn-light-success">
|
||||
<i class="bx bx-user-plus"></i>
|
||||
<TL>New website</TL>
|
||||
<TL>New webspace</TL>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<LazyLoader @ref="LazyLoader" Load="Load">
|
||||
<div class="table-responsive">
|
||||
<Table TableItem="Website" Items="Websites" PageSize="25" TableClass="table table-row-bordered table-row-gray-100 align-middle gs-0 gy-3" TableHeadClass="fw-bold text-muted">
|
||||
<Column TableItem="Website" Title="@(SmartTranslateService.Translate("Id"))" Field="@(x => x.Id)" Sortable="true" Filterable="true"/>
|
||||
<Column TableItem="Website" Title="@(SmartTranslateService.Translate("Base domain"))" Field="@(x => x.BaseDomain)" Sortable="true" Filterable="true">
|
||||
<Table TableItem="WebSpace" Items="WebSpaces" PageSize="25" TableClass="table table-row-bordered table-row-gray-100 align-middle gs-0 gy-3" TableHeadClass="fw-bold text-muted">
|
||||
<Column TableItem="WebSpace" Title="@(SmartTranslateService.Translate("Id"))" Field="@(x => x.Id)" Sortable="true" Filterable="true"/>
|
||||
<Column TableItem="WebSpace" Title="@(SmartTranslateService.Translate("Domain"))" Field="@(x => x.Domain)" Sortable="true" Filterable="true">
|
||||
<Template>
|
||||
<a href="/website/@(context.Id)/">
|
||||
@(context.BaseDomain)
|
||||
<a href="/webspace/@(context.Id)/">
|
||||
@(context.Domain)
|
||||
</a>
|
||||
</Template>
|
||||
</Column>
|
||||
<Column TableItem="Website" Title="@(SmartTranslateService.Translate("Owner"))" Field="@(x => x.Id)" Sortable="false" Filterable="false">
|
||||
<Column TableItem="WebSpace" Title="@(SmartTranslateService.Translate("Owner"))" Field="@(x => x.Id)" Sortable="false" Filterable="false">
|
||||
<Template>
|
||||
<a href="/admin/users/view/@(context.Owner.Id)">
|
||||
@(context.Owner.Email)
|
||||
</a>
|
||||
</Template>
|
||||
</Column>
|
||||
<Column TableItem="Website" Title="@(SmartTranslateService.Translate("Plesk server"))" Field="@(x => x.PleskServer.Id)" Sortable="true" Filterable="true">
|
||||
<Column TableItem="WebSpace" Title="@(SmartTranslateService.Translate("Cloud panel"))" Field="@(x => x.CloudPanel.Name)" Sortable="true" Filterable="true">
|
||||
<Template>
|
||||
<a href="/admin/websites/servers/edit/@(context.PleskServer.Id)/">
|
||||
@(context.PleskServer.Name)
|
||||
<a href="/admin/webspaces/servers/edit/@(context.CloudPanel.Id)/">
|
||||
@(context.CloudPanel.Name)
|
||||
</a>
|
||||
</Template>
|
||||
</Column>
|
||||
<Column TableItem="Website" Title="@(SmartTranslateService.Translate("Manage"))" Field="@(x => x.Id)" Sortable="false" Filterable="false">
|
||||
<Column TableItem="WebSpace" Title="@(SmartTranslateService.Translate("Manage"))" Field="@(x => x.Id)" Sortable="false" Filterable="false">
|
||||
<Template>
|
||||
<DeleteButton Confirm="true" OnClick="() => Delete(context)">
|
||||
</DeleteButton>
|
||||
|
@ -72,22 +72,21 @@
|
|||
{
|
||||
private LazyLoader LazyLoader;
|
||||
|
||||
private Website[] Websites;
|
||||
private WebSpace[] WebSpaces;
|
||||
|
||||
private Task Load(LazyLoader lazyLoader)
|
||||
{
|
||||
Websites = WebsiteRepository
|
||||
WebSpaces = WebSpaceRepository
|
||||
.Get()
|
||||
.Include(x => x.Owner)
|
||||
.Include(x => x.PleskServer)
|
||||
.Include(x => x.CloudPanel)
|
||||
.ToArray();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task Delete(Website website)
|
||||
private async Task Delete(WebSpace webSpace)
|
||||
{
|
||||
await WebsiteService.Delete(website);
|
||||
await WebSpaceService.Delete(webSpace);
|
||||
await LazyLoader.Reload();
|
||||
}
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
@page "/admin/websites/new"
|
||||
@page "/admin/webspaces/new"
|
||||
|
||||
@using Moonlight.App.Models.Forms
|
||||
@using Moonlight.App.Services
|
||||
@using Moonlight.App.Database.Entities
|
||||
@using Moonlight.App.Repositories
|
||||
|
||||
@inject WebsiteService WebsiteService
|
||||
@inject WebSpaceService WebSpaceService
|
||||
@inject UserRepository UserRepository
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
|
@ -46,9 +46,9 @@
|
|||
|
||||
private async Task OnValidSubmit()
|
||||
{
|
||||
await WebsiteService.Create(Model.BaseDomain, Model.User);
|
||||
await WebSpaceService.Create(Model.BaseDomain, Model.User);
|
||||
|
||||
NavigationManager.NavigateTo("/admin/websites");
|
||||
NavigationManager.NavigateTo("/admin/webspaces");
|
||||
}
|
||||
|
||||
private Task Load(LazyLoader arg)
|
102
Moonlight/Shared/Views/Admin/Webspaces/Servers/Edit.razor
Normal file
102
Moonlight/Shared/Views/Admin/Webspaces/Servers/Edit.razor
Normal file
|
@ -0,0 +1,102 @@
|
|||
@page "/admin/webspaces/servers/edit/{Id:int}"
|
||||
|
||||
@using Moonlight.App.Models.Forms
|
||||
@using Moonlight.App.Repositories
|
||||
@using Moonlight.App.Database.Entities
|
||||
@using Mappy.Net
|
||||
|
||||
@inject Repository<CloudPanel> CloudPanelRepository
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<OnlyAdmin>
|
||||
<LazyLoader Load="Load">
|
||||
@if (CloudPanel == null)
|
||||
{
|
||||
<div class="d-flex justify-content-center flex-center">
|
||||
<div class="card">
|
||||
<img src="/assets/media/svg/nodata.svg" class="card-img-top w-50 mx-auto pt-5" alt="Not found image"/>
|
||||
<div class="card-body text-center">
|
||||
<h1 class="card-title">
|
||||
<TL>Cloud panel not found</TL>
|
||||
</h1>
|
||||
<p class="card-text fs-4">
|
||||
<TL>A cloud panel with that id cannot be found</TL>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="card card-body p-10">
|
||||
<SmartForm Model="Model" OnValidSubmit="OnValidSubmit">
|
||||
<label class="form-label">
|
||||
<TL>Name</TL>
|
||||
</label>
|
||||
<div class="input-group mb-5">
|
||||
<InputText @bind-Value="Model.Name" class="form-control"></InputText>
|
||||
</div>
|
||||
<label class="form-label">
|
||||
<TL>Host</TL>
|
||||
</label>
|
||||
<div class="input-group mb-5">
|
||||
<InputText @bind-Value="Model.Host" class="form-control"></InputText>
|
||||
</div>
|
||||
<label class="form-label">
|
||||
<TL>Api Url</TL>
|
||||
</label>
|
||||
<div class="input-group mb-5">
|
||||
<InputText @bind-Value="Model.ApiUrl" class="form-control"></InputText>
|
||||
</div>
|
||||
<label class="form-label">
|
||||
<TL>Api Key</TL>
|
||||
</label>
|
||||
<div class="input-group mb-5">
|
||||
<InputText @bind-Value="Model.ApiKey" class="blur-unless-hover form-control"></InputText>
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit" class="btn btn-primary float-end">
|
||||
<TL>Save</TL>
|
||||
</button>
|
||||
</div>
|
||||
</SmartForm>
|
||||
</div>
|
||||
}
|
||||
</LazyLoader>
|
||||
</OnlyAdmin>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public int Id { get; set; }
|
||||
|
||||
private CloudPanel? CloudPanel;
|
||||
|
||||
private CloudPanelDataModel Model = new();
|
||||
|
||||
private Task OnValidSubmit()
|
||||
{
|
||||
// Apply changes by mapping values using the override feature
|
||||
CloudPanel = Mapper.Map(CloudPanel!, Model);
|
||||
|
||||
CloudPanelRepository.Update(CloudPanel);
|
||||
|
||||
NavigationManager.NavigateTo("/admin/webspaces/servers");
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task Load(LazyLoader arg)
|
||||
{
|
||||
CloudPanel = CloudPanelRepository
|
||||
.Get()
|
||||
.FirstOrDefault(x => x.Id == Id);
|
||||
|
||||
if (CloudPanel != null)
|
||||
{
|
||||
Model = Mapper.Map<CloudPanelDataModel>(CloudPanel);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
@page "/admin/websites/servers"
|
||||
@page "/admin/webspaces/servers"
|
||||
|
||||
@using Moonlight.Shared.Components.Navigations
|
||||
@using Moonlight.App.Services
|
||||
|
@ -7,38 +7,39 @@
|
|||
@using BlazorTable
|
||||
|
||||
@inject SmartTranslateService SmartTranslateService
|
||||
@inject PleskServerRepository PleskServerRepository
|
||||
@inject WebsiteService WebsiteService
|
||||
@inject Repository<CloudPanel> CloudPanelRepository
|
||||
@inject WebSpaceService WebSpaceService
|
||||
|
||||
<OnlyAdmin>
|
||||
<AdminWebsitesNavigation Index="1"/>
|
||||
<AdminWebspacesNavigation Index="1"/>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header border-0 pt-5">
|
||||
<h3 class="card-title align-items-start flex-column">
|
||||
<span class="card-label fw-bold fs-3 mb-1">
|
||||
<TL>Plesk servers</TL>
|
||||
<TL>Cloud panels</TL>
|
||||
</span>
|
||||
</h3>
|
||||
<div class="card-toolbar">
|
||||
<a href="/admin/websites/servers/new" class="btn btn-sm btn-light-success">
|
||||
<a href="/admin/webspaces/servers/new" class="btn btn-sm btn-light-success">
|
||||
<i class="bx bx-user-plus"></i>
|
||||
<TL>New plesk server</TL>
|
||||
<TL>New cloud panel</TL>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<LazyLoader @ref="LazyLoader" Load="Load">
|
||||
<div class="table-responsive">
|
||||
<Table TableItem="PleskServer" Items="PleskServers" PageSize="25" TableClass="table table-row-bordered table-row-gray-100 align-middle gs-0 gy-3" TableHeadClass="fw-bold text-muted">
|
||||
<Column TableItem="PleskServer" Title="@(SmartTranslateService.Translate("Id"))" Field="@(x => x.Id)" Sortable="true" Filterable="true"/>
|
||||
<Column TableItem="PleskServer" Title="@(SmartTranslateService.Translate("Name"))" Field="@(x => x.Name)" Sortable="true" Filterable="true"/>
|
||||
<Column TableItem="PleskServer" Title="@(SmartTranslateService.Translate("Api url"))" Field="@(x => x.ApiUrl)" Sortable="true" Filterable="true"/>
|
||||
<Column TableItem="PleskServer" Title="@(SmartTranslateService.Translate("Status"))" Field="@(x => x.Id)" Sortable="false" Filterable="false">
|
||||
<Table TableItem="CloudPanel" Items="CloudPanels" PageSize="25" TableClass="table table-row-bordered table-row-gray-100 align-middle gs-0 gy-3" TableHeadClass="fw-bold text-muted">
|
||||
<Column TableItem="CloudPanel" Title="@(SmartTranslateService.Translate("Id"))" Field="@(x => x.Id)" Sortable="true" Filterable="true"/>
|
||||
<Column TableItem="CloudPanel" Title="@(SmartTranslateService.Translate("Name"))" Field="@(x => x.Name)" Sortable="true" Filterable="true"/>
|
||||
<Column TableItem="CloudPanel" Title="@(SmartTranslateService.Translate("Name"))" Field="@(x => x.Host)" Sortable="true" Filterable="true"/>
|
||||
<Column TableItem="CloudPanel" Title="@(SmartTranslateService.Translate("Api url"))" Field="@(x => x.ApiUrl)" Sortable="true" Filterable="true"/>
|
||||
<Column TableItem="CloudPanel" Title="@(SmartTranslateService.Translate("Status"))" Field="@(x => x.Id)" Sortable="false" Filterable="false">
|
||||
<Template>
|
||||
@if (OnlineCache.ContainsKey(context))
|
||||
@if (OnlineCache.TryGetValue(context, out var value))
|
||||
{
|
||||
if (OnlineCache[context])
|
||||
if (value)
|
||||
{
|
||||
<span class="text-success">
|
||||
<TL>Online</TL>
|
||||
|
@ -59,14 +60,14 @@
|
|||
}
|
||||
</Template>
|
||||
</Column>
|
||||
<Column TableItem="PleskServer" Title="@(SmartTranslateService.Translate("Edit"))" Field="@(x => x.Id)" Sortable="false" Filterable="false">
|
||||
<Column TableItem="CloudPanel" Title="@(SmartTranslateService.Translate("Edit"))" Field="@(x => x.Id)" Sortable="false" Filterable="false">
|
||||
<Template>
|
||||
<a href="/admin/websites/servers/edit/@(context.Id)/">
|
||||
<a href="/admin/webspaces/servers/edit/@(context.Id)/">
|
||||
<TL>Manage</TL>
|
||||
</a>
|
||||
</Template>
|
||||
</Column>
|
||||
<Column TableItem="PleskServer" Title="@(SmartTranslateService.Translate("Manage"))" Field="@(x => x.Id)" Sortable="false" Filterable="false">
|
||||
<Column TableItem="CloudPanel" Title="@(SmartTranslateService.Translate("Manage"))" Field="@(x => x.Id)" Sortable="false" Filterable="false">
|
||||
<Template>
|
||||
<DeleteButton Confirm="true" OnClick="() => OnClick(context)">
|
||||
</DeleteButton>
|
||||
|
@ -82,22 +83,22 @@
|
|||
|
||||
@code
|
||||
{
|
||||
private PleskServer[] PleskServers;
|
||||
private CloudPanel[] CloudPanels;
|
||||
|
||||
private LazyLoader LazyLoader;
|
||||
private Dictionary<PleskServer, bool> OnlineCache = new();
|
||||
private Dictionary<CloudPanel, bool> OnlineCache = new();
|
||||
|
||||
private Task Load(LazyLoader arg)
|
||||
{
|
||||
PleskServers = PleskServerRepository
|
||||
CloudPanels = CloudPanelRepository
|
||||
.Get()
|
||||
.ToArray();
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
foreach (var pleskServer in PleskServers)
|
||||
foreach (var cloudPanel in CloudPanels)
|
||||
{
|
||||
OnlineCache.Add(pleskServer, await WebsiteService.IsHostUp(pleskServer));
|
||||
OnlineCache.Add(cloudPanel, await WebSpaceService.IsHostUp(cloudPanel));
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
@ -106,9 +107,9 @@
|
|||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task OnClick(PleskServer pleskServer)
|
||||
private async Task OnClick(CloudPanel pleskServer)
|
||||
{
|
||||
PleskServerRepository.Delete(pleskServer);
|
||||
CloudPanelRepository.Delete(pleskServer);
|
||||
|
||||
await LazyLoader.Reload();
|
||||
}
|
|
@ -1,8 +1,10 @@
|
|||
@page "/admin/websites/servers/new"
|
||||
@page "/admin/webspaces/servers/new"
|
||||
@using Moonlight.App.Repositories
|
||||
@using Moonlight.App.Models.Forms
|
||||
@using Moonlight.App.Database.Entities
|
||||
@using Mappy.Net
|
||||
|
||||
@inject PleskServerRepository PleskServerRepository
|
||||
@inject Repository<CloudPanel> CloudPanelRepository
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<OnlyAdmin>
|
||||
|
@ -14,6 +16,12 @@
|
|||
<div class="input-group mb-5">
|
||||
<InputText @bind-Value="Model.Name" class="form-control"></InputText>
|
||||
</div>
|
||||
<label class="form-label">
|
||||
<TL>Host</TL>
|
||||
</label>
|
||||
<div class="input-group mb-5">
|
||||
<InputText @bind-Value="Model.Host" class="form-control"></InputText>
|
||||
</div>
|
||||
<label class="form-label">
|
||||
<TL>Api Url</TL>
|
||||
</label>
|
||||
|
@ -37,18 +45,13 @@
|
|||
|
||||
@code
|
||||
{
|
||||
private PleskServerDataModel Model = new();
|
||||
private CloudPanelDataModel Model = new();
|
||||
|
||||
private Task OnValidSubmit()
|
||||
{
|
||||
PleskServerRepository.Add(new()
|
||||
{
|
||||
Name = Model.Name,
|
||||
ApiUrl = Model.ApiUrl,
|
||||
ApiKey = Model.ApiKey
|
||||
});
|
||||
|
||||
NavigationManager.NavigateTo("/admin/websites/servers");
|
||||
CloudPanelRepository.Add(Mapper.Map<CloudPanel>(Model));
|
||||
|
||||
NavigationManager.NavigateTo("/admin/webspaces/servers");
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
@page "/website/{Id:int}/{Route?}"
|
||||
@page "/webspace/{Id:int}/{Route?}"
|
||||
@using Moonlight.App.Database.Entities
|
||||
@using Moonlight.App.Repositories
|
||||
@using Moonlight.App.Services
|
||||
|
@ -6,22 +6,22 @@
|
|||
@using Microsoft.EntityFrameworkCore
|
||||
@using Moonlight.App.Services.Interop
|
||||
|
||||
@inject WebsiteRepository WebsiteRepository
|
||||
@inject WebsiteService WebsiteService
|
||||
@inject Repository<WebSpace> WebSpaceRepository
|
||||
@inject WebSpaceService WebSpaceService
|
||||
@inject ToastService ToastService
|
||||
|
||||
<LazyLoader Load="Load">
|
||||
@if (CurrentWebsite == null)
|
||||
@if (CurrentWebspace == null)
|
||||
{
|
||||
<div class="d-flex justify-content-center flex-center">
|
||||
<div class="card">
|
||||
<img src="/assets/media/svg/nodata.svg" class="card-img-top w-50 mx-auto pt-5" alt="Not found image"/>
|
||||
<div class="card-body text-center">
|
||||
<h1 class="card-title">
|
||||
<TL>Website not found</TL>
|
||||
<TL>Webspace not found</TL>
|
||||
</h1>
|
||||
<p class="card-text fs-4">
|
||||
<TL>A website with that id cannot be found or you have no access for this server</TL>
|
||||
<TL>A webspace with that id cannot be found or you have no access for this webspace</TL>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -31,7 +31,7 @@
|
|||
{
|
||||
if (HostOnline)
|
||||
{
|
||||
<CascadingValue Value="CurrentWebsite">
|
||||
<CascadingValue Value="CurrentWebspace">
|
||||
@{
|
||||
var index = 0;
|
||||
|
||||
|
@ -51,21 +51,21 @@
|
|||
break;
|
||||
}
|
||||
|
||||
<WebsiteNavigation Index="index" Website="CurrentWebsite" />
|
||||
<WebSpaceNavigation Index="index" WebSpace="CurrentWebspace" />
|
||||
|
||||
@switch (Route)
|
||||
{
|
||||
case "files":
|
||||
<WebsiteFiles />
|
||||
<WebSpaceFiles />
|
||||
break;
|
||||
case "ftp":
|
||||
<WebsiteFtp />
|
||||
<WebSpaceFtp />
|
||||
break;
|
||||
case "databases":
|
||||
<WebsiteDatabases />
|
||||
<WebSpaceDatabases />
|
||||
break;
|
||||
default:
|
||||
<WebsiteDashboard />
|
||||
<WebSpaceDashboard />
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -101,32 +101,28 @@
|
|||
[CascadingParameter]
|
||||
public User User { get; set; }
|
||||
|
||||
private Website? CurrentWebsite;
|
||||
private WebSpace? CurrentWebspace;
|
||||
private bool HostOnline = false;
|
||||
|
||||
private async Task Load(LazyLoader lazyLoader)
|
||||
{
|
||||
CurrentWebsite = WebsiteRepository
|
||||
CurrentWebspace = WebSpaceRepository
|
||||
.Get()
|
||||
.Include(x => x.PleskServer)
|
||||
.Include(x => x.CloudPanel)
|
||||
.Include(x => x.Owner)
|
||||
.FirstOrDefault(x => x.Id == Id);
|
||||
|
||||
if (CurrentWebsite != null)
|
||||
if (CurrentWebspace != null)
|
||||
{
|
||||
if (CurrentWebsite.Owner.Id != User!.Id && !User.Admin)
|
||||
CurrentWebsite = null;
|
||||
if (CurrentWebspace.Owner.Id != User!.Id && !User.Admin)
|
||||
CurrentWebspace = null;
|
||||
}
|
||||
|
||||
if (CurrentWebsite != null)
|
||||
if (CurrentWebspace != null)
|
||||
{
|
||||
await lazyLoader.SetText("Checking host system online status");
|
||||
|
||||
HostOnline = await WebsiteService.IsHostUp(CurrentWebsite);
|
||||
|
||||
if (HostOnline)
|
||||
{
|
||||
}
|
||||
HostOnline = await WebSpaceService.IsHostUp(CurrentWebspace);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
@page "/websites/create"
|
||||
@page "/webspaces/create"
|
||||
@using Moonlight.App.Services
|
||||
@using Moonlight.App.Database.Entities
|
||||
@using Moonlight.App.Models.Forms
|
||||
|
@ -6,7 +6,7 @@
|
|||
@using Microsoft.EntityFrameworkCore
|
||||
|
||||
@inject SubscriptionService SubscriptionService
|
||||
@inject WebsiteService WebsiteService
|
||||
@inject WebSpaceService WebSpaceService
|
||||
@inject WebsiteRepository WebsiteRepository
|
||||
@inject SmartDeployService SmartDeployService
|
||||
@inject SmartTranslateService SmartTranslateService
|
||||
|
@ -137,9 +137,9 @@
|
|||
.Include(x => x.Owner)
|
||||
.Count(x => x.Owner.Id == User.Id) < (await SubscriptionService.GetLimit("websites")).Amount)
|
||||
{
|
||||
var website = await WebsiteService.Create(Model.BaseDomain, User, PleskServer);
|
||||
//var website = await WebsiteService.Create(Model.BaseDomain, User, PleskServer);
|
||||
|
||||
NavigationManager.NavigateTo($"/website/{website.Id}");
|
||||
//NavigationManager.NavigateTo($"/website/{website.Id}");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +1,17 @@
|
|||
@page "/websites"
|
||||
@page "/webspaces"
|
||||
@using Moonlight.App.Database.Entities
|
||||
@using Moonlight.App.Repositories
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
|
||||
@inject WebsiteRepository WebsiteRepository
|
||||
@inject Repository<WebSpace> WebSpaceRepository
|
||||
|
||||
<LazyLoader Load="Load">
|
||||
@if (Websites.Any())
|
||||
@if (WebSpaces.Any())
|
||||
{
|
||||
foreach (var website in Websites)
|
||||
foreach (var webSpace in WebSpaces)
|
||||
{
|
||||
<div class="row px-5 mb-5">
|
||||
<a class="card card-body" href="/website/@(website.Id)">
|
||||
<a class="card card-body" href="/webspace/@(webSpace.Id)">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="d-flex align-items-center">
|
||||
|
@ -20,10 +20,10 @@
|
|||
</div>
|
||||
<div class="d-flex justify-content-start flex-column">
|
||||
<span class="text-gray-800 text-hover-primary mb-1 fs-5">
|
||||
@(website.BaseDomain)
|
||||
@(webSpace.Domain)
|
||||
</span>
|
||||
<span class="text-gray-400 fw-semibold d-block fs-6">
|
||||
<span class="text-gray-700">@(website.PleskServer.Name)</span>
|
||||
<span class="text-gray-700">@(webSpace.CloudPanel.Name)</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -38,10 +38,10 @@
|
|||
<div class="alert bg-info d-flex flex-column flex-sm-row w-100 p-5">
|
||||
<div class="d-flex flex-column pe-0 pe-sm-10">
|
||||
<h4 class="fw-semibold">
|
||||
<TL>You have no websites</TL>
|
||||
<TL>You have no webspaces</TL>
|
||||
</h4>
|
||||
<span>
|
||||
<TL>We were not able to find any websites associated with your account</TL>
|
||||
<TL>We were not able to find any webspaces associated with your account</TL>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -53,14 +53,14 @@
|
|||
[CascadingParameter]
|
||||
public User User { get; set; }
|
||||
|
||||
private Website[] Websites;
|
||||
private WebSpace[] WebSpaces;
|
||||
|
||||
private Task Load(LazyLoader lazyLoader)
|
||||
{
|
||||
Websites = WebsiteRepository
|
||||
WebSpaces = WebSpaceRepository
|
||||
.Get()
|
||||
.Include(x => x.Owner)
|
||||
.Include(x => x.PleskServer)
|
||||
.Include(x => x.CloudPanel)
|
||||
.Where(x => x.Owner.Id == User.Id)
|
||||
.ToArray();
|
||||
|
Loading…
Add table
Reference in a new issue