Implemented databases. Fixed nav bars. Some fixes

This commit is contained in:
Marcel Baumgartner
2023-04-19 22:35:18 +02:00
parent fd008e56aa
commit 746b4112bd
15 changed files with 181 additions and 166 deletions

View File

@@ -0,0 +1,18 @@
using Newtonsoft.Json;
namespace Moonlight.App.ApiClients.CloudPanel.Requests;
public class AddDatabase
{
[JsonProperty("domainName")]
public string DomainName { get; set; }
[JsonProperty("databaseName")]
public string DatabaseName { get; set; }
[JsonProperty("databaseUserName")]
public string DatabaseUserName { get; set; }
[JsonProperty("databaseUserPassword")]
public string DatabaseUserPassword { get; set; }
}

View File

@@ -0,0 +1,9 @@
using Newtonsoft.Json;
namespace Moonlight.App.ApiClients.CloudPanel.Requests;
public class InstallLetsEncrypt
{
[JsonProperty("domainName")]
public string DomainName { get; set; }
}

View File

@@ -7,7 +7,7 @@ public class DomainOrderDataModel
{
[Required(ErrorMessage = "You need to specify a name")]
[MaxLength(32, ErrorMessage = "The max lenght for the name is 32 characters")]
[RegularExpression(@"^[a-z]+$", ErrorMessage = "The name should only consist of lower case characters")]
[RegularExpression(@"^[a-z0-9]+$", ErrorMessage = "The name should only consist of lower case characters or numbers")]
public string Name { get; set; } = "";
[Required(ErrorMessage = "You need to specify a shared domain")]

View File

@@ -6,18 +6,20 @@ namespace Moonlight.App.Services;
public class SmartDeployService
{
private readonly NodeRepository NodeRepository;
private readonly PleskServerRepository PleskServerRepository;
private readonly Repository<CloudPanel> CloudPanelRepository;
private readonly WebSpaceService WebSpaceService;
private readonly NodeService NodeService;
public SmartDeployService(
NodeRepository nodeRepository,
NodeService nodeService, PleskServerRepository pleskServerRepository, WebSpaceService webSpaceService)
NodeService nodeService,
WebSpaceService webSpaceService,
Repository<CloudPanel> cloudPanelRepository)
{
NodeRepository = nodeRepository;
NodeService = nodeService;
PleskServerRepository = pleskServerRepository;
WebSpaceService = webSpaceService;
CloudPanelRepository = cloudPanelRepository;
}
public async Task<Node?> GetNode()
@@ -38,13 +40,14 @@ public class SmartDeployService
return data.MaxBy(x => x.Value).Key;
}
public async Task<PleskServer?> GetPleskServer()
public async Task<CloudPanel?> GetCloudPanel()
{
var result = new List<PleskServer>();
var result = new List<CloudPanel>();
foreach (var pleskServer in PleskServerRepository.Get().ToArray())
foreach (var cloudPanel in CloudPanelRepository.Get().ToArray())
{
result.Add(pleskServer);
if (await WebSpaceService.IsHostUp(cloudPanel))
result.Add(cloudPanel);
}
return result.FirstOrDefault();

View File

@@ -17,13 +17,16 @@ public class WebSpaceService
{
private readonly Repository<CloudPanel> CloudPanelRepository;
private readonly Repository<WebSpace> WebSpaceRepository;
private readonly Repository<MySqlDatabase> DatabaseRepository;
private readonly CloudPanelApiHelper CloudPanelApiHelper;
public WebSpaceService(Repository<CloudPanel> cloudPanelRepository, Repository<WebSpace> webSpaceRepository, CloudPanelApiHelper cloudPanelApiHelper)
public WebSpaceService(Repository<CloudPanel> cloudPanelRepository, Repository<WebSpace> webSpaceRepository, CloudPanelApiHelper cloudPanelApiHelper, Repository<MySqlDatabase> databaseRepository)
{
CloudPanelRepository = cloudPanelRepository;
WebSpaceRepository = webSpaceRepository;
CloudPanelApiHelper = cloudPanelApiHelper;
DatabaseRepository = databaseRepository;
}
public async Task<WebSpace> Create(string domain, User owner, CloudPanel? ps = null)
@@ -83,14 +86,16 @@ public class WebSpaceService
{
try
{
//var res = await PleskApiHelper.Get<ServerStatus>(pleskServer, "server");
await CloudPanelApiHelper.Post(cloudPanel, "", null);
return true;
//if (res != null)
// return true;
}
catch (Exception e)
catch (CloudPanelException e)
{
if (e.StatusCode == 404)
return true;
}
catch (Exception)
{
// ignored
}
@@ -105,40 +110,60 @@ public class WebSpaceService
return await IsHostUp(webSpace.CloudPanel);
}
#region SSL
public async Task<string[]> GetSslCertificates(WebSpace w)
public async Task IssueSslCertificate(WebSpace w)
{
var certs = new List<string>();
return certs.ToArray();
}
var webspace = EnsureData(w);
public async Task CreateSslCertificate(WebSpace w)
await CloudPanelApiHelper.Post(webspace.CloudPanel, "letsencrypt/install/certificate", new InstallLetsEncrypt()
{
DomainName = webspace.Domain
});
}
public async Task DeleteSslCertificate(WebSpace w, string name)
{
}
#endregion
#region Databases
public async Task<Models.Plesk.Resources.Database[]> GetDatabases(WebSpace w)
public Task<MySqlDatabase[]> GetDatabases(WebSpace w)
{
return Array.Empty<Models.Plesk.Resources.Database>();
return Task.FromResult(WebSpaceRepository
.Get()
.Include(x => x.Databases)
.First(x => x.Id == w.Id)
.Databases.ToArray());
}
public async Task CreateDatabase(WebSpace w, string name, string password)
{
if (DatabaseRepository.Get().Any(x => x.UserName == name))
throw new DisplayException("A database with this name does already exist");
var webspace = EnsureData(w);
var database = new MySqlDatabase()
{
UserName = name,
Password = password
};
await CloudPanelApiHelper.Post(webspace.CloudPanel, "db", new AddDatabase()
{
DomainName = webspace.Domain,
DatabaseName = database.UserName,
DatabaseUserName = database.UserName,
DatabaseUserPassword = database.Password
});
webspace.Databases.Add(database);
WebSpaceRepository.Update(webspace);
}
public async Task DeleteDatabase(WebSpace w, Models.Plesk.Resources.Database database)
public async Task DeleteDatabase(WebSpace w, MySqlDatabase database)
{
var webspace = EnsureData(w);
await CloudPanelApiHelper.Delete(webspace.CloudPanel, $"db/{database.UserName}", null);
webspace.Databases.Remove(database);
WebSpaceRepository.Update(webspace);
}
#endregion

View File

@@ -9,7 +9,9 @@
@inject NavigationManager NavigationManager
@inject CookieService CookieService
<div class="menu menu-column justify-content-center"
@if (User != null)
{
<div class="menu menu-column justify-content-center"
data-kt-menu="true">
<div class="menu-item">
<div class="dropdown">
@@ -28,14 +30,15 @@
</a>
</li>
<li>
<a class="dropdown-item py-2" href="/websites/create">
<TL>Website</TL>
<a class="dropdown-item py-2" href="/webspaces/create">
<TL>Webspace</TL>
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
}
<div class="app-navbar flex-shrink-0">
<div class="app-navbar-item ms-1 ms-lg-3">

View File

@@ -45,11 +45,11 @@ else
</a>
</div>
<div class="menu-item">
<a class="menu-link" href="/websites">
<a class="menu-link" href="/webspaces">
<span class="menu-icon">
<i class="bx bx-globe"></i>
</span>
<span class="menu-title"><TL>Websites</TL></span>
<span class="menu-title"><TL>Webspaces</TL></span>
</a>
</div>
<div class="menu-item">
@@ -148,11 +148,11 @@ else
</div>
</div>
<div class="menu-item">
<a class="menu-link" href="/admin/websites">
<a class="menu-link" href="/admin/webspaces">
<span class="menu-icon">
<i class="bx bx-globe"></i>
</span>
<span class="menu-title"><TL>Websites</TL></span>
<span class="menu-title"><TL>Webspaces</TL></span>
</a>
</div>
<div class="menu-item">

View File

@@ -1,8 +1,10 @@
@using Moonlight.App.Database.Entities
@using Moonlight.App.Services
@using Moonlight.App.Services.Interop
@inject WebSpaceService WebSpaceService
@inject SmartTranslateService SmartTranslateService
@inject AlertService AlertService
<div class="row gy-5 g-xl-10">
<div class="col-xl-4 mb-xl-10">
@@ -26,54 +28,10 @@
<WButton Text="@(SmartTranslateService.Translate("Issue certificate"))"
WorkingText="@(SmartTranslateService.Translate("Working"))"
CssClasses="btn-success"
OnClick="CreateCertificate">
OnClick="IssueCertificate">
</WButton>
</div>
</div>
<div class="card-body">
@if (Certs.Any())
{
<table class="table align-middle gs-0 gy-3">
<thead>
<tr>
<th class="p-0 w-50px"></th>
<th class="p-0 min-w-150px"></th>
<th class="p-0 min-w-120px"></th>
</tr>
</thead>
<tbody>
@foreach (var cert in Certs)
{
<tr>
<td>
<div class="symbol symbol-50px me-2">
<span class="symbol-label">
<i class="bx bx-md bx-receipt text-dark"></i>
</span>
</div>
</td>
<td>
<span class="text-dark fw-bold fs-6">@(cert)</span>
</td>
<td class="text-end">
<WButton Text="@(SmartTranslateService.Translate("Delete"))"
WorkingText="@(SmartTranslateService.Translate("Working"))"
CssClasses="btn btn-danger"
OnClick="() => DeleteCertificate(cert)">
</WButton>
</td>
</tr>
}
</tbody>
</table>
}
else
{
<div class="alert alert-warning">
<TL>No SSL certificates found</TL>
</div>
}
</div>
</div>
</div>
</LazyLoader>
@@ -87,25 +45,16 @@
[CascadingParameter]
public WebSpace CurrentWebSpace { get; set; }
private string[] Certs;
private LazyLoader LazyLoader;
private async Task Load(LazyLoader lazyLoader)
private Task Load(LazyLoader lazyLoader)
{
await lazyLoader.SetText("Loading certificates");
Certs = await WebSpaceService.GetSslCertificates(CurrentWebSpace);
return Task.CompletedTask;
}
private async Task CreateCertificate()
private async Task IssueCertificate()
{
await WebSpaceService.CreateSslCertificate(CurrentWebSpace);
await LazyLoader.Reload();
}
private async Task DeleteCertificate(string name)
{
await WebSpaceService.DeleteSslCertificate(CurrentWebSpace, name);
await LazyLoader.Reload();
await WebSpaceService.IssueSslCertificate(CurrentWebSpace);
await AlertService.Success(SmartTranslateService.Translate("Lets Encrypt certificate successfully issued"));
}
}

View File

@@ -34,7 +34,7 @@
<div class="card">
<div class="card-header">
<div class="card-title">
<span>@(database.Name) - @(database.Type.ToUpper().Replace("MYSQL", "MySQL"))</span>
<span>@(database.UserName) - @(database.UserName.ToUpper().Replace("MYSQL", "MySQL"))</span>
</div>
</div>
<div class="card-body me-1">
@@ -46,7 +46,7 @@
</label>
</td>
<td class="pb-2">
<input type="text" class="form-control form-control-solid disabled" disabled="disabled" value="@(Host)">
<input type="text" class="form-control form-control-solid disabled" disabled="disabled" value="@(CurrentWebSpace.CloudPanel.Host)">
</td>
</tr>
<tr>
@@ -56,7 +56,7 @@
</label>
</td>
<td class="pb-2">
<input type="text" class="form-control form-control-solid disabled" disabled="disabled" value="@(DatabaseServer.Port)">
<input type="text" class="form-control form-control-solid disabled" disabled="disabled" value="3306">
</td>
</tr>
<tr>
@@ -66,7 +66,17 @@
</label>
</td>
<td class="pb-2">
<input type="text" class="form-control form-control-solid disabled" disabled="disabled" value="@(database.Name)">
<input type="text" class="form-control form-control-solid disabled" disabled="disabled" value="@(database.UserName)">
</td>
</tr>
<tr>
<td>
<label class="fs-6 fw-semibold form-label mt-3">
<TL>Password</TL>
</label>
</td>
<td class="pb-2">
<input type="text" class="form-control form-control-solid disabled blur-unless-hover" disabled="disabled" value="@(database.Password)">
</td>
</tr>
<tr>
@@ -76,7 +86,7 @@
</label>
</td>
<td class="pb-2">
<input type="text" class="form-control form-control-solid disabled" disabled="disabled" value="@(database.Name)">
<input type="text" class="form-control form-control-solid disabled" disabled="disabled" value="@(database.UserName)">
</td>
</tr>
</table>
@@ -104,9 +114,7 @@
public WebSpace CurrentWebSpace { get; set; }
private LazyLoader LazyLoader;
private Database[] Databases;
private DatabaseServer DatabaseServer;
private string Host;
private MySqlDatabase[] Databases;
private DatabaseDataModel Model = new();
@@ -122,7 +130,7 @@
await LazyLoader.Reload();
}
private async Task DeleteDatabase(Database database)
private async Task DeleteDatabase(MySqlDatabase database)
{
await WebSpaceService.DeleteDatabase(CurrentWebSpace, database);
await LazyLoader.Reload();

View File

@@ -34,8 +34,8 @@
</a>
</li>
<li class="nav-item mt-2">
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 2 ? "active" : "")" href="/webspace/@(WebSpace.Id)/ftp">
<TL>Ftp</TL>
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 2 ? "active" : "")" href="/webspace/@(WebSpace.Id)/sftp">
<TL>Sftp</TL>
</a>
</li>
<li class="nav-item mt-2">

View File

@@ -8,7 +8,7 @@
<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>
<TL>Sftp Host</TL>
</label>
</div>
<div class="col-md-9">
@@ -18,7 +18,7 @@
<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>
<TL>Sftp Port</TL>
</label>
</div>
<div class="col-md-9">
@@ -28,7 +28,7 @@
<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>
<TL>Sftp Username</TL>
</label>
</div>
<div class="col-md-9">
@@ -38,7 +38,7 @@
<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>
<TL>Sftp Password</TL>
</label>
</div>
<div class="col-md-9">

View File

@@ -2,17 +2,18 @@
@using Moonlight.App.Repositories.Servers
@using Moonlight.App.Repositories
@using Moonlight.App.Repositories.Domains
@using Moonlight.App.Database.Entities
@inject ServerRepository ServerRepository
@inject UserRepository UserRepository
@inject WebsiteRepository WebsiteRepository
@inject Repository<WebSpace> WebSpaceRepository
@inject DomainRepository DomainRepository
<OnlyAdmin>
<LazyLoader Load="Load">
<div class="row mb-5">
<div class="col-12 col-lg-6 col-xl">
<a class="mt-4 card" href="/servers">
<a class="mt-4 card" href="/admin/servers">
<div class="card-body">
<div class="row align-items-center gx-0">
<div class="col">
@@ -38,10 +39,10 @@
<div class="row align-items-center gx-0">
<div class="col">
<h6 class="text-uppercase text-muted mb-2">
<TL>Websites</TL>
<TL>Webspaces</TL>
</h6>
<span class="h2 mb-0">
@(WebsiteCount)
@(WebSpaceCount)
</span>
</div>
<div class="col-auto">
@@ -54,7 +55,7 @@
</a>
</div>
<div class="col-12 col-lg-6 col-xl">
<a class="mt-4 card" href="/domains">
<a class="mt-4 card" href="/admin/domains">
<div class="card-body">
<div class="row align-items-center gx-0">
<div class="col">
@@ -75,7 +76,7 @@
</a>
</div>
<div class="col-12 col-lg-6 col-xl">
<a class="mt-4 card" href="/domains">
<a class="mt-4 card" href="/admin/users">
<div class="card-body">
<div class="row align-items-center gx-0">
<div class="col">
@@ -104,14 +105,14 @@
private int ServerCount = 0;
private int UserCount = 0;
private int DomainCount = 0;
private int WebsiteCount = 0;
private int WebSpaceCount = 0;
private Task Load(LazyLoader lazyLoader)
{
ServerCount = ServerRepository.Get().Count();
UserCount = UserRepository.Get().Count();
DomainCount = DomainRepository.Get().Count();
WebsiteCount = WebsiteRepository.Get().Count();
WebSpaceCount = WebSpaceRepository.Get().Count();
return Task.CompletedTask;
}

View File

@@ -82,12 +82,12 @@
</a>
</div>
<div class="col-12 col-lg-6 col-xl">
<a class="mt-4 card" href="/websites">
<a class="mt-4 card" href="/webspaces">
<div class="card-body">
<div class="row align-items-center gx-0">
<div class="col">
<h6 class="text-uppercase text-muted mb-2">
<TL>Websites</TL>
<TL>Webspaces</TL>
</h6>
<span class="h2 mb-0">
@(WebsiteCount)

View File

@@ -8,7 +8,6 @@
@inject Repository<WebSpace> WebSpaceRepository
@inject WebSpaceService WebSpaceService
@inject ToastService ToastService
<LazyLoader Load="Load">
@if (CurrentWebspace == null)
@@ -40,7 +39,7 @@
case "files":
index = 1;
break;
case "ftp":
case "sftp":
index = 2;
break;
case "databases":
@@ -58,8 +57,8 @@
case "files":
<WebSpaceFiles />
break;
case "ftp":
<WebSpaceFtp />
case "sftp":
<WebSpaceSftp />
break;
case "databases":
<WebSpaceDatabases />

View File

@@ -7,23 +7,23 @@
@inject SubscriptionService SubscriptionService
@inject WebSpaceService WebSpaceService
@inject WebsiteRepository WebsiteRepository
@inject Repository<WebSpace> WebSpaceRepository
@inject SmartDeployService SmartDeployService
@inject SmartTranslateService SmartTranslateService
@inject NavigationManager NavigationManager
<LazyLoader Load="Load">
@if (PleskServer == null)
@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-25 mx-auto pt-5" alt="Not found image"/>
<div class="card-body text-center">
<h4 class="card-title">
<TL>No plesk server found</TL>
<TL>No web host found</TL>
</h4>
<p class="card-text">
<TL>No plesk server found to deploy to</TL>
<TL>No web host found to deploy to</TL>
</p>
</div>
</div>
@@ -37,7 +37,7 @@
<div class="card-header">
<div class="card-title">
<h2>
<TL>Website details</TL>
<TL>Webspace details</TL>
</h2>
</div>
</div>
@@ -45,9 +45,9 @@
<div class="d-flex flex-column gap-10">
<div class="fv-row">
<label class="form-label">
<TL>Plesk server</TL>
<TL>Web host</TL>
</label>
<div class="fw-bold fs-3">@(PleskServer.Name)</div>
<div class="fw-bold fs-3">@(CloudPanel.Name)</div>
</div>
@if (AllowOrder)
{
@@ -67,7 +67,7 @@
<div class="card-header">
<div class="card-title">
<h2>
<TL>Configure your website</TL>
<TL>Configure your webspaces</TL>
</h2>
</div>
</div>
@@ -90,7 +90,7 @@
{
<div class="alert alert-warning d-flex align-items-center p-5 mb-10">
<span>
<TL>You reached the maximum amount of websites in your subscription</TL>: @(Subscription == null ? SmartTranslateService.Translate("Default") : Subscription.Name)
<TL>You reached the maximum amount of webspaces in your subscription</TL>: @(Subscription == null ? SmartTranslateService.Translate("Default") : Subscription.Name)
</span>
</div>
}
@@ -108,7 +108,7 @@
public User User { get; set; }
private Subscription? Subscription;
private PleskServer? PleskServer;
private CloudPanel? CloudPanel;
private bool AllowOrder = false;
private WebsiteOrderDataModel Model = new();
@@ -121,10 +121,10 @@
await lazyLoader.SetText(SmartTranslateService.Translate("Loading your subscription"));
Subscription = await SubscriptionService.GetCurrent();
await lazyLoader.SetText(SmartTranslateService.Translate("Searching for deploy plesk server"));
PleskServer = await SmartDeployService.GetPleskServer();
await lazyLoader.SetText(SmartTranslateService.Translate("Searching for deploy web host"));
CloudPanel = await SmartDeployService.GetCloudPanel();
AllowOrder = WebsiteRepository
AllowOrder = WebSpaceRepository
.Get()
.Include(x => x.Owner)
.Count(x => x.Owner.Id == User.Id) < (await SubscriptionService.GetLimit("websites")).Amount;
@@ -132,14 +132,14 @@
private async Task OnValidSubmit()
{
if (WebsiteRepository
if (WebSpaceRepository
.Get()
.Include(x => x.Owner)
.Count(x => x.Owner.Id == User.Id) < (await SubscriptionService.GetLimit("websites")).Amount)
{
//var website = await WebsiteService.Create(Model.BaseDomain, User, PleskServer);
var webSpace = await WebSpaceService.Create(Model.BaseDomain, User, CloudPanel);
//NavigationManager.NavigateTo($"/website/{website.Id}");
NavigationManager.NavigateTo($"/webspace/{webSpace.Id}");
}
}
}