Added new file manager, renamed websites to webspaces. Added cloudpanel integration partialy. Added generic repos and more stuff
This commit is contained in:
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; } = "";
|
||||||
|
}
|
||||||
@@ -44,6 +44,10 @@ public class DataContext : DbContext
|
|||||||
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; }
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
{
|
{
|
||||||
if (!optionsBuilder.IsConfigured)
|
if (!optionsBuilder.IsConfigured)
|
||||||
|
|||||||
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("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")
|
||||||
@@ -281,6 +308,30 @@ 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")
|
||||||
@@ -748,6 +799,43 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
b.ToTable("Users");
|
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 =>
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -828,6 +916,17 @@ 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)
|
||||||
@@ -932,6 +1031,25 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
b.Navigation("CurrentSubscription");
|
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 =>
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Moonlight.App.Database.Entities.User", "Owner")
|
b.HasOne("Moonlight.App.Database.Entities.User", "Owner")
|
||||||
@@ -971,6 +1089,11 @@ 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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 NodeRepository NodeRepository;
|
||||||
private readonly PleskServerRepository PleskServerRepository;
|
private readonly PleskServerRepository PleskServerRepository;
|
||||||
private readonly WebsiteService WebsiteService;
|
private readonly WebSpaceService WebSpaceService;
|
||||||
private readonly NodeService NodeService;
|
private readonly NodeService NodeService;
|
||||||
|
|
||||||
public SmartDeployService(
|
public SmartDeployService(
|
||||||
NodeRepository nodeRepository,
|
NodeRepository nodeRepository,
|
||||||
NodeService nodeService, PleskServerRepository pleskServerRepository, WebsiteService websiteService)
|
NodeService nodeService, PleskServerRepository pleskServerRepository, WebSpaceService webSpaceService)
|
||||||
{
|
{
|
||||||
NodeRepository = nodeRepository;
|
NodeRepository = nodeRepository;
|
||||||
NodeService = nodeService;
|
NodeService = nodeService;
|
||||||
PleskServerRepository = pleskServerRepository;
|
PleskServerRepository = pleskServerRepository;
|
||||||
WebsiteService = websiteService;
|
WebSpaceService = webSpaceService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Node?> GetNode()
|
public async Task<Node?> GetNode()
|
||||||
@@ -43,12 +43,9 @@ public class SmartDeployService
|
|||||||
var result = new List<PleskServer>();
|
var result = new List<PleskServer>();
|
||||||
|
|
||||||
foreach (var pleskServer in PleskServerRepository.Get().ToArray())
|
foreach (var pleskServer in PleskServerRepository.Get().ToArray())
|
||||||
{
|
|
||||||
if (await WebsiteService.IsHostUp(pleskServer))
|
|
||||||
{
|
{
|
||||||
result.Add(pleskServer);
|
result.Add(pleskServer);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return result.FirstOrDefault();
|
return result.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ public class StatisticsCaptureService
|
|||||||
private readonly ConfigService ConfigService;
|
private readonly ConfigService ConfigService;
|
||||||
private readonly StatisticsRepository StatisticsRepository;
|
private readonly StatisticsRepository StatisticsRepository;
|
||||||
private readonly IServiceScopeFactory ServiceScopeFactory;
|
private readonly IServiceScopeFactory ServiceScopeFactory;
|
||||||
private readonly WebsiteService WebsiteService;
|
private readonly WebSpaceService WebSpaceService;
|
||||||
private readonly PleskServerRepository PleskServerRepository;
|
private readonly PleskServerRepository PleskServerRepository;
|
||||||
private PeriodicTimer Timer;
|
private PeriodicTimer Timer;
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ public class StatisticsCaptureService
|
|||||||
DataContext = provider.GetRequiredService<DataContext>();
|
DataContext = provider.GetRequiredService<DataContext>();
|
||||||
ConfigService = configService;
|
ConfigService = configService;
|
||||||
StatisticsRepository = provider.GetRequiredService<StatisticsRepository>();
|
StatisticsRepository = provider.GetRequiredService<StatisticsRepository>();
|
||||||
WebsiteService = provider.GetRequiredService<WebsiteService>();
|
WebSpaceService = provider.GetRequiredService<WebSpaceService>();
|
||||||
PleskServerRepository = provider.GetRequiredService<PleskServerRepository>();
|
PleskServerRepository = provider.GetRequiredService<PleskServerRepository>();
|
||||||
|
|
||||||
var config = ConfigService.GetSection("Moonlight").GetSection("Statistics");
|
var config = ConfigService.GetSection("Moonlight").GetSection("Statistics");
|
||||||
@@ -48,7 +48,7 @@ public class StatisticsCaptureService
|
|||||||
|
|
||||||
await foreach (var pleskServer in PleskServerRepository.Get())
|
await foreach (var pleskServer in PleskServerRepository.Get())
|
||||||
{
|
{
|
||||||
databases += (await WebsiteService.GetDefaultDatabaseServer(pleskServer)).DbCount;
|
//databases += (await WebsiteService.GetDefaultDatabaseServer(pleskServer)).DbCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
StatisticsRepository.Add("statistics.databasesCount", databases);
|
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="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>
|
||||||
@@ -67,6 +68,7 @@
|
|||||||
</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" />
|
||||||
|
|||||||
@@ -2,6 +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.Helpers;
|
using Moonlight.App.Helpers;
|
||||||
using Moonlight.App.LogMigrator;
|
using Moonlight.App.LogMigrator;
|
||||||
@@ -76,6 +77,7 @@ namespace Moonlight
|
|||||||
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>();
|
||||||
@@ -102,7 +104,7 @@ 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<WebsiteService>();
|
builder.Services.AddScoped<WebSpaceService>();
|
||||||
builder.Services.AddScoped<StatisticsViewService>();
|
builder.Services.AddScoped<StatisticsViewService>();
|
||||||
|
|
||||||
builder.Services.AddScoped<GoogleOAuth2Service>();
|
builder.Services.AddScoped<GoogleOAuth2Service>();
|
||||||
@@ -136,6 +138,7 @@ namespace Moonlight
|
|||||||
builder.Services.AddSingleton<HostSystemHelper>();
|
builder.Services.AddSingleton<HostSystemHelper>();
|
||||||
builder.Services.AddScoped<DaemonApiHelper>();
|
builder.Services.AddScoped<DaemonApiHelper>();
|
||||||
builder.Services.AddScoped<PleskApiHelper>();
|
builder.Services.AddScoped<PleskApiHelper>();
|
||||||
|
builder.Services.AddScoped<CloudPanelApiHelper>();
|
||||||
|
|
||||||
// Background services
|
// Background services
|
||||||
builder.Services.AddSingleton<DiscordBotService>();
|
builder.Services.AddSingleton<DiscordBotService>();
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
@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">
|
||||||
|
|||||||
@@ -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/websites">
|
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 0 ? "active" : "")" href="/admin/webspaces">
|
||||||
<TL>Websites</TL>
|
<TL>Webspaces</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/websites/servers">
|
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 1 ? "active" : "")" href="/admin/webspaces/servers">
|
||||||
<TL>Plesk servers</TL>
|
<TL>Cloud panels</TL>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
@using Moonlight.App.Database.Entities
|
@using Moonlight.App.Database.Entities
|
||||||
@using Moonlight.App.Services
|
@using Moonlight.App.Services
|
||||||
|
|
||||||
@inject WebsiteService WebsiteService
|
@inject WebSpaceService WebSpaceService
|
||||||
@inject SmartTranslateService SmartTranslateService
|
@inject SmartTranslateService SmartTranslateService
|
||||||
|
|
||||||
<div class="row gy-5 g-xl-10">
|
<div class="row gy-5 g-xl-10">
|
||||||
<div class="col-xl-4 mb-xl-10">
|
<div class="col-xl-4 mb-xl-10">
|
||||||
<div class="card h-md-100">
|
<div class="card h-md-100">
|
||||||
<div class="card-body d-flex flex-column flex-center">
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
public Website CurrentWebsite { get; set; }
|
public WebSpace CurrentWebSpace { get; set; }
|
||||||
|
|
||||||
private string[] Certs;
|
private string[] Certs;
|
||||||
|
|
||||||
@@ -94,18 +94,18 @@
|
|||||||
private async Task Load(LazyLoader lazyLoader)
|
private async Task Load(LazyLoader lazyLoader)
|
||||||
{
|
{
|
||||||
await lazyLoader.SetText("Loading certificates");
|
await lazyLoader.SetText("Loading certificates");
|
||||||
Certs = await WebsiteService.GetSslCertificates(CurrentWebsite);
|
Certs = await WebSpaceService.GetSslCertificates(CurrentWebSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CreateCertificate()
|
private async Task CreateCertificate()
|
||||||
{
|
{
|
||||||
await WebsiteService.CreateSslCertificate(CurrentWebsite);
|
await WebSpaceService.CreateSslCertificate(CurrentWebSpace);
|
||||||
await LazyLoader.Reload();
|
await LazyLoader.Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DeleteCertificate(string name)
|
private async Task DeleteCertificate(string name)
|
||||||
{
|
{
|
||||||
await WebsiteService.DeleteSslCertificate(CurrentWebsite, name);
|
await WebSpaceService.DeleteSslCertificate(CurrentWebSpace, name);
|
||||||
await LazyLoader.Reload();
|
await LazyLoader.Reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
@using Moonlight.App.Services
|
@using Moonlight.App.Services
|
||||||
|
|
||||||
@inject SmartTranslateService SmartTranslateService
|
@inject SmartTranslateService SmartTranslateService
|
||||||
@inject WebsiteService WebsiteService
|
@inject WebSpaceService WebSpaceService
|
||||||
|
|
||||||
<LazyLoader @ref="LazyLoader" Load="Load">
|
<LazyLoader @ref="LazyLoader" Load="Load">
|
||||||
<div class="card w-100 mb-4">
|
<div class="card w-100 mb-4">
|
||||||
@@ -93,7 +93,7 @@
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="alert alert-warning">
|
<div class="alert alert-warning">
|
||||||
<TL>No databases found for this website</TL>
|
<TL>No databases found for this webspace</TL>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</LazyLoader>
|
</LazyLoader>
|
||||||
@@ -101,7 +101,7 @@
|
|||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
public Website CurrentWebsite { get; set; }
|
public WebSpace CurrentWebSpace { get; set; }
|
||||||
|
|
||||||
private LazyLoader LazyLoader;
|
private LazyLoader LazyLoader;
|
||||||
private Database[] Databases;
|
private Database[] Databases;
|
||||||
@@ -112,25 +112,19 @@
|
|||||||
|
|
||||||
private async Task Load(LazyLoader arg)
|
private async Task Load(LazyLoader arg)
|
||||||
{
|
{
|
||||||
Databases = await WebsiteService.GetDatabases(CurrentWebsite);
|
Databases = await WebSpaceService.GetDatabases(CurrentWebSpace);
|
||||||
|
|
||||||
if (Databases.Any())
|
|
||||||
{
|
|
||||||
DatabaseServer = (await WebsiteService.GetDefaultDatabaseServer(CurrentWebsite))!;
|
|
||||||
Host = await WebsiteService.GetHost(CurrentWebsite);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnValidSubmit()
|
private async Task OnValidSubmit()
|
||||||
{
|
{
|
||||||
await WebsiteService.CreateDatabase(CurrentWebsite, Model.Name, Model.Password);
|
await WebSpaceService.CreateDatabase(CurrentWebSpace, Model.Name, Model.Password);
|
||||||
Model = new();
|
Model = new();
|
||||||
await LazyLoader.Reload();
|
await LazyLoader.Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DeleteDatabase(Database database)
|
private async Task DeleteDatabase(Database database)
|
||||||
{
|
{
|
||||||
await WebsiteService.DeleteDatabase(CurrentWebsite, database);
|
await WebSpaceService.DeleteDatabase(CurrentWebSpace, database);
|
||||||
await LazyLoader.Reload();
|
await LazyLoader.Reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
@using Moonlight.App.Services
|
@using Moonlight.App.Services
|
||||||
@using Moonlight.Shared.Components.FileManagerPartials
|
@using Moonlight.Shared.Components.FileManagerPartials
|
||||||
|
|
||||||
@inject WebsiteService WebsiteService
|
@inject WebSpaceService WebSpaceService
|
||||||
|
|
||||||
<LazyLoader Load="Load">
|
<LazyLoader Load="Load">
|
||||||
<FileManager Access="Access">
|
<FileManager Access="Access">
|
||||||
@@ -13,12 +13,12 @@
|
|||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
public Website CurrentWebsite { get; set; }
|
public WebSpace CurrentWebSpace { get; set; }
|
||||||
|
|
||||||
private FileAccess Access;
|
private FileAccess Access;
|
||||||
|
|
||||||
private async Task Load(LazyLoader arg)
|
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>
|
</div>
|
||||||
<div class="d-flex flex-column">
|
<div class="d-flex flex-column">
|
||||||
<div class="mb-1 fs-4">@(Website.BaseDomain)</div>
|
<div class="mb-1 fs-4">@(WebSpace.Domain)</div>
|
||||||
<div class="text-muted fs-5">@(Website.PleskServer.Name)</div>
|
<div class="text-muted fs-5">@(WebSpace.CloudPanel.Name)</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -24,22 +24,22 @@
|
|||||||
<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="/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>
|
<TL>Dashboard</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="/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>
|
<TL>Files</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 == 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>
|
<TL>Ftp</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 == 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>
|
<TL>Databases</TL>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -53,5 +53,5 @@
|
|||||||
public int Index { get; set; }
|
public int Index { get; set; }
|
||||||
|
|
||||||
[Parameter]
|
[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>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-lg-6 col-xl">
|
<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="card-body">
|
||||||
<div class="row align-items-center gx-0">
|
<div class="row align-items-center gx-0">
|
||||||
<div class="col">
|
<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.Shared.Components.Navigations
|
||||||
@using Moonlight.App.Services
|
@using Moonlight.App.Services
|
||||||
@@ -8,53 +8,53 @@
|
|||||||
@using BlazorTable
|
@using BlazorTable
|
||||||
|
|
||||||
@inject SmartTranslateService SmartTranslateService
|
@inject SmartTranslateService SmartTranslateService
|
||||||
@inject WebsiteRepository WebsiteRepository
|
@inject Repository<WebSpace> WebSpaceRepository
|
||||||
@inject WebsiteService WebsiteService
|
@inject WebSpaceService WebSpaceService
|
||||||
|
|
||||||
<OnlyAdmin>
|
<OnlyAdmin>
|
||||||
<AdminWebsitesNavigation Index="0"/>
|
<AdminWebspacesNavigation Index="0"/>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header border-0 pt-5">
|
<div class="card-header border-0 pt-5">
|
||||||
<h3 class="card-title align-items-start flex-column">
|
<h3 class="card-title align-items-start flex-column">
|
||||||
<span class="card-label fw-bold fs-3 mb-1">
|
<span class="card-label fw-bold fs-3 mb-1">
|
||||||
<TL>Websites</TL>
|
<TL>Webspaces</TL>
|
||||||
</span>
|
</span>
|
||||||
</h3>
|
</h3>
|
||||||
<div class="card-toolbar">
|
<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>
|
<i class="bx bx-user-plus"></i>
|
||||||
<TL>New website</TL>
|
<TL>New webspace</TL>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<LazyLoader @ref="LazyLoader" Load="Load">
|
<LazyLoader @ref="LazyLoader" Load="Load">
|
||||||
<div class="table-responsive">
|
<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">
|
<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="Website" Title="@(SmartTranslateService.Translate("Id"))" Field="@(x => x.Id)" Sortable="true" Filterable="true"/>
|
<Column TableItem="WebSpace" 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">
|
<Column TableItem="WebSpace" Title="@(SmartTranslateService.Translate("Domain"))" Field="@(x => x.Domain)" Sortable="true" Filterable="true">
|
||||||
<Template>
|
<Template>
|
||||||
<a href="/website/@(context.Id)/">
|
<a href="/webspace/@(context.Id)/">
|
||||||
@(context.BaseDomain)
|
@(context.Domain)
|
||||||
</a>
|
</a>
|
||||||
</Template>
|
</Template>
|
||||||
</Column>
|
</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>
|
<Template>
|
||||||
<a href="/admin/users/view/@(context.Owner.Id)">
|
<a href="/admin/users/view/@(context.Owner.Id)">
|
||||||
@(context.Owner.Email)
|
@(context.Owner.Email)
|
||||||
</a>
|
</a>
|
||||||
</Template>
|
</Template>
|
||||||
</Column>
|
</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>
|
<Template>
|
||||||
<a href="/admin/websites/servers/edit/@(context.PleskServer.Id)/">
|
<a href="/admin/webspaces/servers/edit/@(context.CloudPanel.Id)/">
|
||||||
@(context.PleskServer.Name)
|
@(context.CloudPanel.Name)
|
||||||
</a>
|
</a>
|
||||||
</Template>
|
</Template>
|
||||||
</Column>
|
</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>
|
<Template>
|
||||||
<DeleteButton Confirm="true" OnClick="() => Delete(context)">
|
<DeleteButton Confirm="true" OnClick="() => Delete(context)">
|
||||||
</DeleteButton>
|
</DeleteButton>
|
||||||
@@ -72,22 +72,21 @@
|
|||||||
{
|
{
|
||||||
private LazyLoader LazyLoader;
|
private LazyLoader LazyLoader;
|
||||||
|
|
||||||
private Website[] Websites;
|
private WebSpace[] WebSpaces;
|
||||||
|
|
||||||
private Task Load(LazyLoader lazyLoader)
|
private Task Load(LazyLoader lazyLoader)
|
||||||
{
|
{
|
||||||
Websites = WebsiteRepository
|
WebSpaces = WebSpaceRepository
|
||||||
.Get()
|
.Get()
|
||||||
.Include(x => x.Owner)
|
.Include(x => x.CloudPanel)
|
||||||
.Include(x => x.PleskServer)
|
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
return Task.CompletedTask;
|
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();
|
await LazyLoader.Reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
@page "/admin/websites/new"
|
@page "/admin/webspaces/new"
|
||||||
|
|
||||||
@using Moonlight.App.Models.Forms
|
@using Moonlight.App.Models.Forms
|
||||||
@using Moonlight.App.Services
|
@using Moonlight.App.Services
|
||||||
@using Moonlight.App.Database.Entities
|
@using Moonlight.App.Database.Entities
|
||||||
@using Moonlight.App.Repositories
|
@using Moonlight.App.Repositories
|
||||||
|
|
||||||
@inject WebsiteService WebsiteService
|
@inject WebSpaceService WebSpaceService
|
||||||
@inject UserRepository UserRepository
|
@inject UserRepository UserRepository
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
|
|
||||||
@@ -46,9 +46,9 @@
|
|||||||
|
|
||||||
private async Task OnValidSubmit()
|
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)
|
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.Shared.Components.Navigations
|
||||||
@using Moonlight.App.Services
|
@using Moonlight.App.Services
|
||||||
@@ -7,38 +7,39 @@
|
|||||||
@using BlazorTable
|
@using BlazorTable
|
||||||
|
|
||||||
@inject SmartTranslateService SmartTranslateService
|
@inject SmartTranslateService SmartTranslateService
|
||||||
@inject PleskServerRepository PleskServerRepository
|
@inject Repository<CloudPanel> CloudPanelRepository
|
||||||
@inject WebsiteService WebsiteService
|
@inject WebSpaceService WebSpaceService
|
||||||
|
|
||||||
<OnlyAdmin>
|
<OnlyAdmin>
|
||||||
<AdminWebsitesNavigation Index="1"/>
|
<AdminWebspacesNavigation Index="1"/>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header border-0 pt-5">
|
<div class="card-header border-0 pt-5">
|
||||||
<h3 class="card-title align-items-start flex-column">
|
<h3 class="card-title align-items-start flex-column">
|
||||||
<span class="card-label fw-bold fs-3 mb-1">
|
<span class="card-label fw-bold fs-3 mb-1">
|
||||||
<TL>Plesk servers</TL>
|
<TL>Cloud panels</TL>
|
||||||
</span>
|
</span>
|
||||||
</h3>
|
</h3>
|
||||||
<div class="card-toolbar">
|
<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>
|
<i class="bx bx-user-plus"></i>
|
||||||
<TL>New plesk server</TL>
|
<TL>New cloud panel</TL>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<LazyLoader @ref="LazyLoader" Load="Load">
|
<LazyLoader @ref="LazyLoader" Load="Load">
|
||||||
<div class="table-responsive">
|
<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">
|
<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="PleskServer" Title="@(SmartTranslateService.Translate("Id"))" Field="@(x => x.Id)" Sortable="true" Filterable="true"/>
|
<Column TableItem="CloudPanel" 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="CloudPanel" 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="CloudPanel" Title="@(SmartTranslateService.Translate("Name"))" Field="@(x => x.Host)" Sortable="true" Filterable="true"/>
|
||||||
<Column TableItem="PleskServer" Title="@(SmartTranslateService.Translate("Status"))" Field="@(x => x.Id)" Sortable="false" Filterable="false">
|
<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>
|
<Template>
|
||||||
@if (OnlineCache.ContainsKey(context))
|
@if (OnlineCache.TryGetValue(context, out var value))
|
||||||
{
|
{
|
||||||
if (OnlineCache[context])
|
if (value)
|
||||||
{
|
{
|
||||||
<span class="text-success">
|
<span class="text-success">
|
||||||
<TL>Online</TL>
|
<TL>Online</TL>
|
||||||
@@ -59,14 +60,14 @@
|
|||||||
}
|
}
|
||||||
</Template>
|
</Template>
|
||||||
</Column>
|
</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>
|
<Template>
|
||||||
<a href="/admin/websites/servers/edit/@(context.Id)/">
|
<a href="/admin/webspaces/servers/edit/@(context.Id)/">
|
||||||
<TL>Manage</TL>
|
<TL>Manage</TL>
|
||||||
</a>
|
</a>
|
||||||
</Template>
|
</Template>
|
||||||
</Column>
|
</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>
|
<Template>
|
||||||
<DeleteButton Confirm="true" OnClick="() => OnClick(context)">
|
<DeleteButton Confirm="true" OnClick="() => OnClick(context)">
|
||||||
</DeleteButton>
|
</DeleteButton>
|
||||||
@@ -82,22 +83,22 @@
|
|||||||
|
|
||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
private PleskServer[] PleskServers;
|
private CloudPanel[] CloudPanels;
|
||||||
|
|
||||||
private LazyLoader LazyLoader;
|
private LazyLoader LazyLoader;
|
||||||
private Dictionary<PleskServer, bool> OnlineCache = new();
|
private Dictionary<CloudPanel, bool> OnlineCache = new();
|
||||||
|
|
||||||
private Task Load(LazyLoader arg)
|
private Task Load(LazyLoader arg)
|
||||||
{
|
{
|
||||||
PleskServers = PleskServerRepository
|
CloudPanels = CloudPanelRepository
|
||||||
.Get()
|
.Get()
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
Task.Run(async () =>
|
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);
|
await InvokeAsync(StateHasChanged);
|
||||||
}
|
}
|
||||||
@@ -106,9 +107,9 @@
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnClick(PleskServer pleskServer)
|
private async Task OnClick(CloudPanel pleskServer)
|
||||||
{
|
{
|
||||||
PleskServerRepository.Delete(pleskServer);
|
CloudPanelRepository.Delete(pleskServer);
|
||||||
|
|
||||||
await LazyLoader.Reload();
|
await LazyLoader.Reload();
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
@page "/admin/websites/servers/new"
|
@page "/admin/webspaces/servers/new"
|
||||||
@using Moonlight.App.Repositories
|
@using Moonlight.App.Repositories
|
||||||
@using Moonlight.App.Models.Forms
|
@using Moonlight.App.Models.Forms
|
||||||
|
@using Moonlight.App.Database.Entities
|
||||||
|
@using Mappy.Net
|
||||||
|
|
||||||
@inject PleskServerRepository PleskServerRepository
|
@inject Repository<CloudPanel> CloudPanelRepository
|
||||||
@inject NavigationManager NavigationManager
|
@inject NavigationManager NavigationManager
|
||||||
|
|
||||||
<OnlyAdmin>
|
<OnlyAdmin>
|
||||||
@@ -14,6 +16,12 @@
|
|||||||
<div class="input-group mb-5">
|
<div class="input-group mb-5">
|
||||||
<InputText @bind-Value="Model.Name" class="form-control"></InputText>
|
<InputText @bind-Value="Model.Name" class="form-control"></InputText>
|
||||||
</div>
|
</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">
|
<label class="form-label">
|
||||||
<TL>Api Url</TL>
|
<TL>Api Url</TL>
|
||||||
</label>
|
</label>
|
||||||
@@ -37,18 +45,13 @@
|
|||||||
|
|
||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
private PleskServerDataModel Model = new();
|
private CloudPanelDataModel Model = new();
|
||||||
|
|
||||||
private Task OnValidSubmit()
|
private Task OnValidSubmit()
|
||||||
{
|
{
|
||||||
PleskServerRepository.Add(new()
|
CloudPanelRepository.Add(Mapper.Map<CloudPanel>(Model));
|
||||||
{
|
|
||||||
Name = Model.Name,
|
|
||||||
ApiUrl = Model.ApiUrl,
|
|
||||||
ApiKey = Model.ApiKey
|
|
||||||
});
|
|
||||||
|
|
||||||
NavigationManager.NavigateTo("/admin/websites/servers");
|
NavigationManager.NavigateTo("/admin/webspaces/servers");
|
||||||
|
|
||||||
return Task.CompletedTask;
|
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.Database.Entities
|
||||||
@using Moonlight.App.Repositories
|
@using Moonlight.App.Repositories
|
||||||
@using Moonlight.App.Services
|
@using Moonlight.App.Services
|
||||||
@@ -6,22 +6,22 @@
|
|||||||
@using Microsoft.EntityFrameworkCore
|
@using Microsoft.EntityFrameworkCore
|
||||||
@using Moonlight.App.Services.Interop
|
@using Moonlight.App.Services.Interop
|
||||||
|
|
||||||
@inject WebsiteRepository WebsiteRepository
|
@inject Repository<WebSpace> WebSpaceRepository
|
||||||
@inject WebsiteService WebsiteService
|
@inject WebSpaceService WebSpaceService
|
||||||
@inject ToastService ToastService
|
@inject ToastService ToastService
|
||||||
|
|
||||||
<LazyLoader Load="Load">
|
<LazyLoader Load="Load">
|
||||||
@if (CurrentWebsite == null)
|
@if (CurrentWebspace == null)
|
||||||
{
|
{
|
||||||
<div class="d-flex justify-content-center flex-center">
|
<div class="d-flex justify-content-center flex-center">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<img src="/assets/media/svg/nodata.svg" class="card-img-top w-50 mx-auto pt-5" alt="Not found image"/>
|
<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">
|
<div class="card-body text-center">
|
||||||
<h1 class="card-title">
|
<h1 class="card-title">
|
||||||
<TL>Website not found</TL>
|
<TL>Webspace not found</TL>
|
||||||
</h1>
|
</h1>
|
||||||
<p class="card-text fs-4">
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
{
|
{
|
||||||
if (HostOnline)
|
if (HostOnline)
|
||||||
{
|
{
|
||||||
<CascadingValue Value="CurrentWebsite">
|
<CascadingValue Value="CurrentWebspace">
|
||||||
@{
|
@{
|
||||||
var index = 0;
|
var index = 0;
|
||||||
|
|
||||||
@@ -51,21 +51,21 @@
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
<WebsiteNavigation Index="index" Website="CurrentWebsite" />
|
<WebSpaceNavigation Index="index" WebSpace="CurrentWebspace" />
|
||||||
|
|
||||||
@switch (Route)
|
@switch (Route)
|
||||||
{
|
{
|
||||||
case "files":
|
case "files":
|
||||||
<WebsiteFiles />
|
<WebSpaceFiles />
|
||||||
break;
|
break;
|
||||||
case "ftp":
|
case "ftp":
|
||||||
<WebsiteFtp />
|
<WebSpaceFtp />
|
||||||
break;
|
break;
|
||||||
case "databases":
|
case "databases":
|
||||||
<WebsiteDatabases />
|
<WebSpaceDatabases />
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
<WebsiteDashboard />
|
<WebSpaceDashboard />
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,32 +101,28 @@
|
|||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
public User User { get; set; }
|
public User User { get; set; }
|
||||||
|
|
||||||
private Website? CurrentWebsite;
|
private WebSpace? CurrentWebspace;
|
||||||
private bool HostOnline = false;
|
private bool HostOnline = false;
|
||||||
|
|
||||||
private async Task Load(LazyLoader lazyLoader)
|
private async Task Load(LazyLoader lazyLoader)
|
||||||
{
|
{
|
||||||
CurrentWebsite = WebsiteRepository
|
CurrentWebspace = WebSpaceRepository
|
||||||
.Get()
|
.Get()
|
||||||
.Include(x => x.PleskServer)
|
.Include(x => x.CloudPanel)
|
||||||
.Include(x => x.Owner)
|
.Include(x => x.Owner)
|
||||||
.FirstOrDefault(x => x.Id == Id);
|
.FirstOrDefault(x => x.Id == Id);
|
||||||
|
|
||||||
if (CurrentWebsite != null)
|
if (CurrentWebspace != null)
|
||||||
{
|
{
|
||||||
if (CurrentWebsite.Owner.Id != User!.Id && !User.Admin)
|
if (CurrentWebspace.Owner.Id != User!.Id && !User.Admin)
|
||||||
CurrentWebsite = null;
|
CurrentWebspace = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CurrentWebsite != null)
|
if (CurrentWebspace != null)
|
||||||
{
|
{
|
||||||
await lazyLoader.SetText("Checking host system online status");
|
await lazyLoader.SetText("Checking host system online status");
|
||||||
|
|
||||||
HostOnline = await WebsiteService.IsHostUp(CurrentWebsite);
|
HostOnline = await WebSpaceService.IsHostUp(CurrentWebspace);
|
||||||
|
|
||||||
if (HostOnline)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
@page "/websites/create"
|
@page "/webspaces/create"
|
||||||
@using Moonlight.App.Services
|
@using Moonlight.App.Services
|
||||||
@using Moonlight.App.Database.Entities
|
@using Moonlight.App.Database.Entities
|
||||||
@using Moonlight.App.Models.Forms
|
@using Moonlight.App.Models.Forms
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
@using Microsoft.EntityFrameworkCore
|
@using Microsoft.EntityFrameworkCore
|
||||||
|
|
||||||
@inject SubscriptionService SubscriptionService
|
@inject SubscriptionService SubscriptionService
|
||||||
@inject WebsiteService WebsiteService
|
@inject WebSpaceService WebSpaceService
|
||||||
@inject WebsiteRepository WebsiteRepository
|
@inject WebsiteRepository WebsiteRepository
|
||||||
@inject SmartDeployService SmartDeployService
|
@inject SmartDeployService SmartDeployService
|
||||||
@inject SmartTranslateService SmartTranslateService
|
@inject SmartTranslateService SmartTranslateService
|
||||||
@@ -137,9 +137,9 @@
|
|||||||
.Include(x => x.Owner)
|
.Include(x => x.Owner)
|
||||||
.Count(x => x.Owner.Id == User.Id) < (await SubscriptionService.GetLimit("websites")).Amount)
|
.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.Database.Entities
|
||||||
@using Moonlight.App.Repositories
|
@using Moonlight.App.Repositories
|
||||||
@using Microsoft.EntityFrameworkCore
|
@using Microsoft.EntityFrameworkCore
|
||||||
|
|
||||||
@inject WebsiteRepository WebsiteRepository
|
@inject Repository<WebSpace> WebSpaceRepository
|
||||||
|
|
||||||
<LazyLoader Load="Load">
|
<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">
|
<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="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
@@ -20,10 +20,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-start flex-column">
|
<div class="d-flex justify-content-start flex-column">
|
||||||
<span class="text-gray-800 text-hover-primary mb-1 fs-5">
|
<span class="text-gray-800 text-hover-primary mb-1 fs-5">
|
||||||
@(website.BaseDomain)
|
@(webSpace.Domain)
|
||||||
</span>
|
</span>
|
||||||
<span class="text-gray-400 fw-semibold d-block fs-6">
|
<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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -38,10 +38,10 @@
|
|||||||
<div class="alert bg-info d-flex flex-column flex-sm-row w-100 p-5">
|
<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">
|
<div class="d-flex flex-column pe-0 pe-sm-10">
|
||||||
<h4 class="fw-semibold">
|
<h4 class="fw-semibold">
|
||||||
<TL>You have no websites</TL>
|
<TL>You have no webspaces</TL>
|
||||||
</h4>
|
</h4>
|
||||||
<span>
|
<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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -53,14 +53,14 @@
|
|||||||
[CascadingParameter]
|
[CascadingParameter]
|
||||||
public User User { get; set; }
|
public User User { get; set; }
|
||||||
|
|
||||||
private Website[] Websites;
|
private WebSpace[] WebSpaces;
|
||||||
|
|
||||||
private Task Load(LazyLoader lazyLoader)
|
private Task Load(LazyLoader lazyLoader)
|
||||||
{
|
{
|
||||||
Websites = WebsiteRepository
|
WebSpaces = WebSpaceRepository
|
||||||
.Get()
|
.Get()
|
||||||
.Include(x => x.Owner)
|
.Include(x => x.Owner)
|
||||||
.Include(x => x.PleskServer)
|
.Include(x => x.CloudPanel)
|
||||||
.Where(x => x.Owner.Id == User.Id)
|
.Where(x => x.Owner.Id == User.Id)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
Reference in New Issue
Block a user