Revert "Merge branch 'PleskIntegration' into main"

This reverts commit 18c205d8a0, reversing
changes made to c5be86b695.
This commit is contained in:
Marcel Baumgartner 2023-03-24 20:11:18 +01:00
parent 18c205d8a0
commit 3611b745ff
28 changed files with 75 additions and 5394 deletions

View file

@ -42,7 +42,6 @@ public class DataContext : DbContext
public DbSet<AaPanel> AaPanels { get; set; }
public DbSet<Website> Websites { get; set; }
public DbSet<DdosAttack> DdosAttacks { get; set; }
public DbSet<PleskServer> PleskServers { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{

View file

@ -1,9 +0,0 @@
namespace Moonlight.App.Database.Entities;
public class PleskServer
{
public int Id { get; set; }
public string BaseUrl { get; set; } = "";
public string ApiKey { get; set; } = "";
public string Name { get; set; } = "";
}

View file

@ -3,8 +3,11 @@
public class Website
{
public int Id { get; set; }
public int PleskId { get; set; }
public int InternalAaPanelId { get; set; }
public AaPanel AaPanel { get; set; }
public User Owner { get; set; }
public PleskServer PleskServer { get; set; }
public string BaseDomain { get; set; }
public string DomainName { get; set; }
public string PhpVersion { get; set; }
public string FtpUsername { get; set; }
public string FtpPassword { get; set; }
}

File diff suppressed because it is too large Load diff

View file

@ -1,39 +0,0 @@
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
/// <inheritdoc />
public partial class AddedPleskServer : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "PleskServers",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
BaseUrl = 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_PleskServers", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "PleskServers");
}
}
}

View file

@ -1,29 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
/// <inheritdoc />
public partial class AddedNameToPleskServer : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Name",
table: "PleskServers",
type: "longtext",
nullable: false)
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Name",
table: "PleskServers");
}
}
}

View file

@ -1,124 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
/// <inheritdoc />
public partial class SwitchedToPleskModel : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Websites_AaPanels_AaPanelId",
table: "Websites");
migrationBuilder.DropIndex(
name: "IX_Websites_AaPanelId",
table: "Websites");
migrationBuilder.DropColumn(
name: "DomainName",
table: "Websites");
migrationBuilder.DropColumn(
name: "FtpPassword",
table: "Websites");
migrationBuilder.DropColumn(
name: "FtpUsername",
table: "Websites");
migrationBuilder.DropColumn(
name: "PhpVersion",
table: "Websites");
migrationBuilder.RenameColumn(
name: "InternalAaPanelId",
table: "Websites",
newName: "PleskServerId");
migrationBuilder.RenameColumn(
name: "AaPanelId",
table: "Websites",
newName: "PleskId");
migrationBuilder.CreateIndex(
name: "IX_Websites_PleskServerId",
table: "Websites",
column: "PleskServerId");
migrationBuilder.AddForeignKey(
name: "FK_Websites_PleskServers_PleskServerId",
table: "Websites",
column: "PleskServerId",
principalTable: "PleskServers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Websites_PleskServers_PleskServerId",
table: "Websites");
migrationBuilder.DropIndex(
name: "IX_Websites_PleskServerId",
table: "Websites");
migrationBuilder.RenameColumn(
name: "PleskServerId",
table: "Websites",
newName: "InternalAaPanelId");
migrationBuilder.RenameColumn(
name: "PleskId",
table: "Websites",
newName: "AaPanelId");
migrationBuilder.AddColumn<string>(
name: "DomainName",
table: "Websites",
type: "longtext",
nullable: false)
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AddColumn<string>(
name: "FtpPassword",
table: "Websites",
type: "longtext",
nullable: false)
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AddColumn<string>(
name: "FtpUsername",
table: "Websites",
type: "longtext",
nullable: false)
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.AddColumn<string>(
name: "PhpVersion",
table: "Websites",
type: "longtext",
nullable: false)
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_Websites_AaPanelId",
table: "Websites",
column: "AaPanelId");
migrationBuilder.AddForeignKey(
name: "FK_Websites_AaPanels_AaPanelId",
table: "Websites",
column: "AaPanelId",
principalTable: "AaPanels",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
}
}

View file

@ -1,29 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Moonlight.App.Database.Migrations
{
/// <inheritdoc />
public partial class AddedBaseDomainToWebsite : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "BaseDomain",
table: "Websites",
type: "longtext",
nullable: false)
.Annotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "BaseDomain",
table: "Websites");
}
}
}

View file

@ -446,29 +446,6 @@ namespace Moonlight.App.Database.Migrations
b.ToTable("NotificationClients");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.PleskServer", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("ApiKey")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("BaseUrl")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("PleskServers");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Revoke", b =>
{
b.Property<int>("Id")
@ -810,24 +787,36 @@ namespace Moonlight.App.Database.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("BaseDomain")
b.Property<int>("AaPanelId")
.HasColumnType("int");
b.Property<string>("DomainName")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("FtpPassword")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("FtpUsername")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("InternalAaPanelId")
.HasColumnType("int");
b.Property<int>("OwnerId")
.HasColumnType("int");
b.Property<int>("PleskId")
.HasColumnType("int");
b.Property<int>("PleskServerId")
.HasColumnType("int");
b.Property<string>("PhpVersion")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.HasIndex("OwnerId");
b.HasIndex("AaPanelId");
b.HasIndex("PleskServerId");
b.HasIndex("OwnerId");
b.ToTable("Websites");
});
@ -1018,21 +1007,21 @@ namespace Moonlight.App.Database.Migrations
modelBuilder.Entity("Moonlight.App.Database.Entities.Website", b =>
{
b.HasOne("Moonlight.App.Database.Entities.AaPanel", "AaPanel")
.WithMany()
.HasForeignKey("AaPanelId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.User", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Moonlight.App.Database.Entities.PleskServer", "PleskServer")
.WithMany()
.HasForeignKey("PleskServerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("AaPanel");
b.Navigation("Owner");
b.Navigation("PleskServer");
});
modelBuilder.Entity("Moonlight.App.Database.Entities.Image", b =>

View file

@ -1,32 +0,0 @@
using System.Runtime.Serialization;
namespace Moonlight.App.Exceptions;
[Serializable]
public class PleskException : Exception
{
public int StatusCode { private get; set; }
public PleskException()
{
}
public PleskException(string message, int statusCode) : base(message)
{
StatusCode = statusCode;
}
public PleskException(string message) : base(message)
{
}
public PleskException(string message, Exception inner) : base(message, inner)
{
}
protected PleskException(
SerializationInfo info,
StreamingContext context) : base(info, context)
{
}
}

View file

@ -1,120 +0,0 @@
using System.Text;
using Moonlight.App.Database.Entities;
using Moonlight.App.Exceptions;
using Newtonsoft.Json;
using RestSharp;
namespace Moonlight.App.Helpers;
public class PleskApiHelper
{
private readonly RestClient Client;
public PleskApiHelper()
{
Client = new();
}
public async Task<T> Get<T>(PleskServer server, string resource)
{
var request = CreateRequest(server, resource);
var response = await Client.GetAsync(request);
if (!response.IsSuccessful)
{
if (response.StatusCode != 0)
{
throw new PleskException(
$"An error occured: ({response.StatusCode}) {response.Content}",
(int)response.StatusCode
);
}
else
{
throw new Exception($"An internal error occured: {response.ErrorMessage}");
}
}
return JsonConvert.DeserializeObject<T>(response.Content!)!;
}
public async Task<T> Post<T>(PleskServer server, string resource, object? body)
{
var request = CreateRequest(server, resource);
request.AddParameter("text/plain",
JsonConvert.SerializeObject(body),
ParameterType.RequestBody
);
var response = await Client.PostAsync(request);
if (!response.IsSuccessful)
{
if (response.StatusCode != 0)
{
throw new PleskException(
$"An error occured: ({response.StatusCode}) {response.Content}",
(int)response.StatusCode
);
}
else
{
throw new Exception($"An internal error occured: {response.ErrorMessage}");
}
}
return JsonConvert.DeserializeObject<T>(response.Content!)!;
}
public async Task Delete(PleskServer server, string resource, object? body)
{
var request = CreateRequest(server, resource);
if(body != null)
request.AddParameter("text/plain", JsonConvert.SerializeObject(body), ParameterType.RequestBody);
var response = await Client.DeleteAsync(request);
if (!response.IsSuccessful)
{
if (response.StatusCode != 0)
{
throw new PleskException(
$"An error occured: ({response.StatusCode}) {response.Content}",
(int)response.StatusCode
);
}
else
{
throw new Exception($"An internal error occured: {response.ErrorMessage}");
}
}
}
private RestRequest CreateRequest(PleskServer server, string resource)
{
RestRequest request = new(server.BaseUrl + "/" + resource);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Accept", "application/json");
// Implementation of auth method using auth header and api key
// https://docs.plesk.com/en-US/obsidian/api-rpc/about-rest-api.79359/#authentication-methods
if (server.ApiKey.Contains(":"))
{
var base64 = Convert.ToBase64String(
Encoding.ASCII.GetBytes(server.ApiKey)
);
request.AddHeader("Authorization", "Basic " + base64);
}
else
request.AddHeader("X-API-Key", server.ApiKey);
return request;
}
}

View file

@ -1,42 +0,0 @@
using Newtonsoft.Json;
namespace Moonlight.App.Models.Plesk.Requests;
public class CreateDomain
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("hosting_type")]
public string HostingType { get; set; }
[JsonProperty("hosting_settings")] public HostingSettings HostingSettings { get; set; } = new();
[JsonProperty("owner_client")] public OwnerClient OwnerClient { get; set; } = new();
[JsonProperty("plan")] public Plan Plan { get; set; } = new();
}
public partial class HostingSettings
{
[JsonProperty("ftp_login")]
public string FtpLogin { get; set; }
[JsonProperty("ftp_password")]
public string FtpPassword { get; set; }
}
public partial class OwnerClient
{
[JsonProperty("id")]
public long Id { get; set; }
}
public partial class Plan
{
[JsonProperty("name")]
public string Name { get; set; }
}

View file

@ -1,39 +0,0 @@
using Newtonsoft.Json;
namespace Moonlight.App.Models.Plesk.Resources;
public class Client
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("created")]
public DateTimeOffset Created { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("company")]
public string Company { get; set; }
[JsonProperty("login")]
public string Login { get; set; }
[JsonProperty("status")]
public long Status { get; set; }
[JsonProperty("email")]
public string Email { get; set; }
[JsonProperty("locale")]
public string Locale { get; set; }
[JsonProperty("guid")]
public Guid Guid { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
}

View file

@ -1,12 +0,0 @@
using Newtonsoft.Json;
namespace Moonlight.App.Models.Plesk.Resources;
public class Identifier
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("guid")]
public Guid Guid { get; set; }
}

View file

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

View file

@ -1,9 +1,7 @@
using Microsoft.EntityFrameworkCore;
using aaPanelSharp;
using Microsoft.EntityFrameworkCore;
using Moonlight.App.Database.Entities;
using Moonlight.App.Exceptions;
using Moonlight.App.Helpers;
using Moonlight.App.Models.Plesk.Requests;
using Moonlight.App.Models.Plesk.Resources;
using Moonlight.App.Repositories;
namespace Moonlight.App.Services;
@ -11,100 +9,31 @@ namespace Moonlight.App.Services;
public class WebsiteService
{
private readonly WebsiteRepository WebsiteRepository;
private readonly PleskServerRepository PleskServerRepository;
private readonly PleskApiHelper PleskApiHelper;
public WebsiteService(WebsiteRepository websiteRepository, PleskServerRepository pleskServerRepository,
PleskApiHelper pleskApiHelper)
public WebsiteService(WebsiteRepository websiteRepository)
{
WebsiteRepository = websiteRepository;
PleskServerRepository = pleskServerRepository;
PleskApiHelper = pleskApiHelper;
}
public async Task<Website> Create(User owner, string baseDomain, string ftpPassword, PleskServer? ps = null)
public Website Create(AaPanel aaPanel, User user, string name)
{
var pleskServer = ps ?? PleskServerRepository.Get().FirstOrDefault();
if (WebsiteRepository.Get().Any(x => x.DomainName == name))
throw new DisplayException("A website with this domain has already been created");
if (pleskServer == null)
throw new DisplayException("No plesk server found to deploy the website");
var website = WebsiteRepository.Add(new Website()
var access = new aaPanel(aaPanel.Url, aaPanel.Key);
return null;
}
private aaPanel CreateApiAccess(Website website)
{
if (website.AaPanel == null)
{
PleskServer = pleskServer,
Owner = owner,
BaseDomain = baseDomain
});
try
{
var userId = await GetAdminAccountForDeploy(pleskServer);
string ftpUsername = baseDomain.Replace(".", "_");
var createDomain = new CreateDomain()
{
Description = "Moonlight website " + website.Id,
Name = baseDomain,
OwnerClient = new()
{
Id = userId
},
Plan = new()
{
Name = "Unlimited"
},
HostingType = "virtual",
HostingSettings = new()
{
FtpLogin = ftpUsername,
FtpPassword = ftpPassword
}
};
var identifier = await PleskApiHelper.Post<Identifier>(
pleskServer,
"domains",
createDomain
);
website.PleskId = identifier.Id;
WebsiteRepository.Update(website);
return website;
website = WebsiteRepository
.Get()
.Include(x => x.AaPanel)
.First(x => x.Id == website.Id);
}
catch (Exception e)
{
WebsiteRepository.Delete(website);
throw;
}
}
public async Task Delete(Website w)
{
var website = WebsiteRepository
.Get()
.Include(x => x.PleskServer)
.FirstOrDefault(x => x.Id == w.Id);
if (website == null)
throw new DisplayException("Website not found");
await PleskApiHelper.Delete(website.PleskServer, $"domains/{website.PleskId}", null);
WebsiteRepository.Delete(website);
}
private async Task<int> GetAdminAccountForDeploy(PleskServer pleskServer)
{
var clients = await PleskApiHelper.Get<Client[]>(pleskServer, "clients");
var adminClient = clients.FirstOrDefault(x => x.Type == "admin");
if (adminClient == null)
throw new DisplayException("Unable to deploy website. Plesk admin account is missing");
return adminClient.Id;
return new aaPanel(website.AaPanel.Url, website.AaPanel.Key);
}
}

View file

@ -65,7 +65,6 @@ namespace Moonlight
builder.Services.AddScoped<AaPanelRepository>();
builder.Services.AddScoped<WebsiteRepository>();
builder.Services.AddScoped<DdosAttackRepository>();
builder.Services.AddScoped<PleskServerRepository>();
builder.Services.AddScoped<AuditLogEntryRepository>();
builder.Services.AddScoped<ErrorLogEntryRepository>();
@ -121,7 +120,6 @@ namespace Moonlight
builder.Services.AddSingleton<PaperApiHelper>();
builder.Services.AddSingleton<HostSystemHelper>();
builder.Services.AddScoped<DaemonApiHelper>();
builder.Services.AddScoped<PleskApiHelper>();
// Background services
builder.Services.AddSingleton<DiscordBotService>();

View file

@ -1,43 +0,0 @@
@typeparam TItem
<select class="form-select" @bind="Binding">
@foreach(var item in Items)
{
<option value="@(item!.GetHashCode())">@(DisplayField(item))</option>
}
</select>
@code
{
[Parameter]
public TItem[] Items { get; set; }
[Parameter]
public Func<TItem, string> DisplayField { get; set; }
[Parameter]
public Action<TItem> OnChange { get; set; }
private int Binding
{
get
{
if (Value == null)
return -1;
return Value.GetHashCode();
}
set
{
var i = Items.FirstOrDefault(x => x!.GetHashCode() == value);
if (i != null)
{
Value = i;
OnChange?.Invoke(Value);
}
}
}
private TItem Value;
}

View file

@ -1,22 +0,0 @@
<div class="card mb-5 mb-xl-10">
<div class="card-body pt-0 pb-0">
<ul class="nav nav-stretch nav-line-tabs nav-line-tabs-2x border-transparent fs-5 fw-bold">
<li class="nav-item mt-2">
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 0 ? "active" : "")" href="/admin/websites">
<TL>Websites</TL>
</a>
</li>
<li class="nav-item mt-2">
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 1 ? "active" : "")" href="/admin/websites/servers">
<TL>Plesk servers</TL>
</a>
</li>
</ul>
</div>
</div>
@code
{
[Parameter]
public int Index { get; set; } = 0;
}

View file

@ -155,13 +155,32 @@ else
</div>
</div>
</div>
<div class="menu-item">
<a class="menu-link" href="/admin/websites">
<div data-kt-menu-trigger="click" class="menu-item menu-accordion">
<span class="menu-link">
<span class="menu-icon">
<i class="bx bx-globe"></i>
<i class="bx bx-cube"></i>
</span>
<span class="menu-title"><TL>Websites</TL></span>
</a>
<span class="menu-title"><TL>aaPanel</TL></span>
<span class="menu-arrow"></span>
</span>
<div class="menu-sub menu-sub-accordion">
<div class="menu-item">
<a class="menu-link" href="/admin/aapanel/">
<span class="menu-bullet">
<span class="bullet bullet-dot"></span>
</span>
<span class="menu-title"><TL>Overview</TL></span>
</a>
</div>
<div class="menu-item">
<a class="menu-link" href="/admin/aapanel/databases">
<span class="menu-bullet">
<span class="bullet bullet-dot"></span>
</span>
<span class="menu-title"><TL>Databases</TL></span>
</a>
</div>
</div>
</div>
<div class="menu-item">
<a class="menu-link" href="/admin/users">

View file

@ -1,93 +0,0 @@
@page "/admin/websites/"
@using Moonlight.Shared.Components.Navigations
@using BlazorTable
@using Microsoft.EntityFrameworkCore
@using Moonlight.App.Database.Entities
@using Moonlight.App.Repositories
@using Moonlight.App.Services
@using Moonlight.App.Services.Interop
@inject WebsiteRepository WebsiteRepository
@inject SmartTranslateService SmartTranslateService
@inject ToastService ToastService
@inject WebsiteService WebsiteService
<OnlyAdmin>
<AdminWebsiteNavigation Index="0"/>
<LazyLoader @ref="LazyLoader" Load="Load">
<div class="card">
<div class="card-header border-0 pt-5">
<h3 class="card-title align-items-start flex-column">
<span class="card-label fw-bold fs-3 mb-1">
<TL>Websites</TL>
</span>
</h3>
<div class="card-toolbar">
<a href="/admin/websites/new" class="btn btn-sm btn-light-success">
<i class="bx bx-layer-plus"></i>
<TL>Add new website</TL>
</a>
</div>
</div>
<div class="card-body pt-0">
<div class="table-responsive">
<Table TableItem="Website" Items="Websites" PageSize="25" TableClass="table table-row-bordered table-row-gray-100 align-middle gs-0 gy-3" TableHeadClass="fw-bold text-muted">
<Column TableItem="Website" Title="@(SmartTranslateService.Translate("Id"))" Field="@(x => x.Id)" Sortable="true" Filterable="true">
<Template>
<a href="/website/@(context.Id)">@(context.Id)</a>
</Template>
</Column>
<Column TableItem="Website" Title="@(SmartTranslateService.Translate("Plesk id"))" Field="@(x => x.PleskId)" Sortable="true" Filterable="true"/>
<Column TableItem="Website" Title="@(SmartTranslateService.Translate("Owner"))" Field="@(x => x.Owner)" Sortable="true" Filterable="true">
<Template>
<a href="/admin/users/view/@(context.Owner.Id)">@(context.Owner.Email)</a>
</Template>
</Column>
<Column TableItem="Website" Title="@(SmartTranslateService.Translate("Plesk server"))" Field="@(x => x.PleskServer)" Sortable="true" Filterable="true">
<Template>
<a href="/admin/websites/servers/edit/@(context.PleskServer.Id)">@(context.PleskServer.Name)</a>
</Template>
</Column>
<Column TableItem="Website" Title="" Field="@(x => x.Id)" Sortable="false" Filterable="false">
<Template>
<WButton Text="@(SmartTranslateService.Translate("Delete"))"
WorkingText="@(SmartTranslateService.Translate("Deleting"))"
CssClasses="btn-danger"
OnClick="() => Delete(context)">
</WButton>
</Template>
</Column>
<Pager ShowPageNumber="true" ShowTotalCount="true"/>
</Table>
</div>
</div>
</div>
</LazyLoader>
</OnlyAdmin>
@code
{
private LazyLoader LazyLoader;
private Website[] Websites;
private Task Load(LazyLoader lazyLoader)
{
Websites = WebsiteRepository
.Get()
.Include(x => x.Owner)
.Include(x => x.PleskServer)
.ToArray();
return Task.CompletedTask;
}
private async Task Delete(Website website)
{
await WebsiteService.Delete(website);
await ToastService.Success(SmartTranslateService.Translate("Successfully deleted website"));
await LazyLoader.Reload();
}
}

View file

@ -1,122 +0,0 @@
@page "/admin/websites/new"
@using Blazored.Typeahead
@using Logging.Net
@using Moonlight.App.Database.Entities
@using Moonlight.App.Repositories
@using Moonlight.App.Services
@using Moonlight.App.Services.Interop
@inject UserRepository UserRepository
@inject PleskServerRepository PleskServerRepository
@inject SmartTranslateService SmartTranslateService
@inject WebsiteService WebsiteService
@inject NavigationManager NavigationManager
@inject ToastService ToastService
<OnlyAdmin>
<div class="d-flex flex-center">
<LazyLoader Load="Load">
<div class="card rounded-3 w-md-550px">
<div class="card-body">
<div class="d-flex flex-center flex-column-fluid">
<div class="form w-100 fv-plugins-bootstrap5 fv-plugins-framework">
<div class="fv-row mb-8">
<label class="form-label">
<TL>Base domain</TL>
</label>
<input @bind="BaseDomain" type="text" class="form-control"/>
</div>
<div class="fv-row mb-8">
<label class="form-label">
<TL>Ftp password</TL>
</label>
<input @bind="FtpPassword" type="text" class="form-control"/>
</div>
<div class="fv-row mb-8">
<div class="form-select">
<BlazoredTypeahead SearchMethod="SearchUsers"
@bind-Value="User">
<SelectedTemplate>
@(context.Email)
</SelectedTemplate>
<ResultTemplate>
@(context.Email)
</ResultTemplate>
</BlazoredTypeahead>
</div>
</div>
<div class="fv-row mb-8">
<label class="form-label">
<TL>Plesk server</TL>
</label>
<WSelect TItem="PleskServer"
Items="PleskServers"
DisplayField="@(x => x.Name)"
OnChange="@(x => PleskServer = x)">
</WSelect>
</div>
<div class="fv-row mb-9">
<WButton Text="@(SmartTranslateService.Translate("Create"))"
WorkingText="@(SmartTranslateService.Translate("Creating"))"
CssClasses="btn-success"
OnClick="Create">
</WButton>
<a href="/admin/websites/servers" class="btn btn-primary">
<TL>Back</TL>
</a>
</div>
</div>
</div>
</div>
</div>
</LazyLoader>
</div>
</OnlyAdmin>
@code
{
// Cached data
private User[] Users;
private PleskServer[] PleskServers;
private User User;
private PleskServer? PleskServer;
private string BaseDomain = "";
private string FtpPassword = "";
private Task Load(LazyLoader lazyLoader)
{
Users = UserRepository
.Get()
.ToArray();
User = Users.First();
PleskServers = PleskServerRepository
.Get()
.ToArray();
return Task.CompletedTask;
}
private Task<IEnumerable<User>> SearchUsers(string input)
{
if (string.IsNullOrEmpty(input))
{
return Task.FromResult(Array.Empty<User>().Cast<User>());
}
else
{
return Task.FromResult(Users.Where(x => x.Email.ToLower().StartsWith(input)));
}
}
private async Task Create()
{
await WebsiteService.Create(User, BaseDomain, FtpPassword, PleskServer);
await ToastService.Success(SmartTranslateService.Translate("Website successfully created"));
NavigationManager.NavigateTo("/admin/websites");
}
}

View file

@ -1,58 +0,0 @@
@page "/admin/websites/servers"
@using Moonlight.App.Repositories
@using Moonlight.App.Services
@using BlazorTable
@using Moonlight.App.Database.Entities
@using Moonlight.Shared.Components.Navigations
@inject PleskServerRepository PleskServerRepository
@inject SmartTranslateService SmartTranslateService
<OnlyAdmin>
<AdminWebsiteNavigation Index="1" />
<LazyLoader @ref="LazyLoader" Load="Load">
<div class="card">
<div class="card-header border-0 pt-5">
<h3 class="card-title align-items-start flex-column">
<span class="card-label fw-bold fs-3 mb-1">
<TL>Plesk servers</TL>
</span>
</h3>
<div class="card-toolbar">
<a href="/admin/websites/servers/new" class="btn btn-sm btn-light-success">
<i class="bx bx-layer-plus"></i>
<TL>Add new plesk server</TL>
</a>
</div>
</div>
<div class="card-body pt-0">
<div class="table-responsive">
<Table TableItem="PleskServer" Items="PleskServers" PageSize="25" TableClass="table table-row-bordered table-row-gray-100 align-middle gs-0 gy-3" TableHeadClass="fw-bold text-muted">
<Column TableItem="PleskServer" Title="@(SmartTranslateService.Translate("Id"))" Field="@(x => x.Id)" Sortable="true" Filterable="true"/>
<Column TableItem="PleskServer" Title="@(SmartTranslateService.Translate("Name"))" Field="@(x => x.Name)" Sortable="true" Filterable="true"/>
<Column TableItem="PleskServer" Title="@(SmartTranslateService.Translate("Base url"))" Field="@(x => x.BaseUrl)" Sortable="true" Filterable="true"/>
<Pager ShowPageNumber="true" ShowTotalCount="true"/>
</Table>
</div>
</div>
</div>
</LazyLoader>
</OnlyAdmin>
@code
{
private LazyLoader LazyLoader;
private PleskServer[] PleskServers;
private Task Load(LazyLoader lazyLoader)
{
PleskServers = PleskServerRepository
.Get()
.ToArray();
return Task.CompletedTask;
}
}

View file

@ -1,64 +0,0 @@
@page "/admin/websites/servers/new"
@using Moonlight.App.Repositories
@using Moonlight.App.Services
@using Microsoft.AspNetCore.Components
@using Moonlight.App.Database.Entities
@inject PleskServerRepository PleskServerRepository
@inject SmartTranslateService SmartTranslateService
@inject NavigationManager NavigationManager
<OnlyAdmin>
<div class="d-flex flex-center">
<div class="card rounded-3 w-md-550px">
<div class="card-body">
<div class="d-flex flex-center flex-column-fluid">
<div class="form w-100 fv-plugins-bootstrap5 fv-plugins-framework">
<div class="fv-row mb-8">
<label class="form-label">
<TL>Name</TL>
</label>
<input @bind="NewServer.Name" type="text" placeholder="@(SmartTranslateService.Translate("Name"))" class="form-control bg-transparent">
</div>
<div class="fv-row mb-8">
<label class="form-label">
<TL>Base url</TL>
</label>
<input @bind="NewServer.BaseUrl" type="text" placeholder="@(SmartTranslateService.Translate("Base url"))" class="form-control bg-transparent">
</div>
<div class="fv-row mb-8">
<label class="form-label">
<TL>Api key</TL>
</label>
<input @bind="NewServer.ApiKey" type="text" placeholder="@(SmartTranslateService.Translate("Api key"))" class="form-control bg-transparent">
</div>
<div class="fv-row mb-9">
<WButton Text="@(SmartTranslateService.Translate("Create"))"
WorkingText="@(SmartTranslateService.Translate("Creating"))"
CssClasses="btn-success"
OnClick="Create">
</WButton>
<a href="/admin/websites/servers" class="btn btn-primary">
<TL>Back</TL>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</OnlyAdmin>
@code
{
private PleskServer NewServer = new();
private Task Create()
{
PleskServerRepository.Add(NewServer);
NavigationManager.NavigateTo("/admin/websites/servers");
return Task.CompletedTask;
}
}

View file

@ -394,14 +394,3 @@ Discord id;Discord id
Discord username;Discord username
Discord discriminator;Discord discriminator
The Name field is required.;The Name field is required.
Plesk servers;Plesk servers
Add new plesk server;Add new plesk server
Base url;Base url
Api key;Api key
Add new website;Add new website
Plesk id;Plesk id
Plesk server;Plesk server
Base domain;Base domain
Ftp password;Ftp password
Website successfully created;Website successfully created
Successfully deleted website;Successfully deleted website