Files
Moonlight/Moonlight/App/Services/WebSpaceService.cs
2023-06-21 19:15:30 +02:00

247 lines
7.4 KiB
C#

using DnsClient;
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.Repositories;
using FileAccess = Moonlight.App.Helpers.Files.FileAccess;
namespace Moonlight.App.Services;
public class WebSpaceService
{
private readonly Repository<CloudPanel> CloudPanelRepository;
private readonly Repository<WebSpace> WebSpaceRepository;
private readonly Repository<MySqlDatabase> DatabaseRepository;
private readonly CloudPanelApiHelper CloudPanelApiHelper;
public WebSpaceService(Repository<CloudPanel> cloudPanelRepository, Repository<WebSpace> webSpaceRepository,
CloudPanelApiHelper cloudPanelApiHelper, Repository<MySqlDatabase> databaseRepository)
{
CloudPanelRepository = cloudPanelRepository;
WebSpaceRepository = webSpaceRepository;
CloudPanelApiHelper = cloudPanelApiHelper;
DatabaseRepository = databaseRepository;
}
public async Task<WebSpace> Create(string domain, User owner, CloudPanel? ps = null)
{
if (WebSpaceRepository.Get().Any(x => x.Domain == domain))
throw new DisplayException("A website with this domain does already exist");
var cloudPanel = ps ?? CloudPanelRepository.Get().First();
var ftpLogin = domain.Replace(".", "_");
var ftpPassword = StringHelper.GenerateString(16);
var phpVersion = "8.1"; // TODO: Add config option or smth
var w = new WebSpace()
{
CloudPanel = cloudPanel,
Owner = owner,
Domain = domain,
UserName = ftpLogin,
Password = ftpPassword,
VHostTemplate = "Generic" //TODO: Implement as select option
};
var webSpace = WebSpaceRepository.Add(w);
try
{
await CloudPanelApiHelper.Post(cloudPanel, "site/php", new AddPhpSite()
{
VHostTemplate = w.VHostTemplate,
DomainName = w.Domain,
PhpVersion = phpVersion,
SiteUser = w.UserName,
SiteUserPassword = w.Password
});
}
catch (Exception)
{
WebSpaceRepository.Delete(webSpace);
throw;
}
return webSpace;
}
public async Task Delete(WebSpace w)
{
var webSpace = WebSpaceRepository
.Get()
.Include(x => x.Databases)
.Include(x => x.CloudPanel)
.Include(x => x.Owner)
.First(x => x.Id == w.Id);
foreach (var database in webSpace.Databases.ToArray())
{
await DeleteDatabase(webSpace, database);
}
await CloudPanelApiHelper.Delete(webSpace.CloudPanel, $"site/{webSpace.Domain}", null);
WebSpaceRepository.Delete(webSpace);
}
public async Task<bool> IsHostUp(CloudPanel cloudPanel)
{
try
{
await CloudPanelApiHelper.Post(cloudPanel, "", null);
return true;
}
catch (CloudPanelException e)
{
if (e.StatusCode == 404)
return true;
}
catch (Exception)
{
// ignored
}
return false;
}
public async Task<bool> IsHostUp(WebSpace w)
{
var webSpace = EnsureData(w);
return await IsHostUp(webSpace.CloudPanel);
}
public async Task IssueSslCertificate(WebSpace w)
{
var webspace = EnsureData(w);
var dns = new LookupClient(new LookupClientOptions()
{
CacheFailedResults = false,
UseCache = false
});
var ipOfWebspaceQuery = await dns.QueryAsync(
webspace.Domain,
QueryType.A
);
var ipOfWebspaceWwwQuery = await dns.QueryAsync(
"www." + webspace.Domain,
QueryType.CNAME
);
var ipOfWebspace = ipOfWebspaceQuery.Answers.ARecords().FirstOrDefault();
var ipOfWebspaceWww = ipOfWebspaceWwwQuery.Answers.CnameRecords().FirstOrDefault();
if (ipOfWebspace == null)
throw new DisplayException($"Unable to find any a records for {webspace.Domain}", true);
if (ipOfWebspaceWww == null)
throw new DisplayException($"Unable to find any cname records for www.{webspace.Domain}", true);
var ipOfHostQuery = await dns.QueryAsync(
webspace.CloudPanel.Host,
QueryType.A
);
var ipOfHost = ipOfHostQuery.Answers.ARecords().FirstOrDefault();
if (ipOfHost == null)
throw new DisplayException("Unable to find a record of host system");
if (ipOfHost.Address.ToString() != ipOfWebspace.Address.ToString())
throw new DisplayException("The dns records of your webspace do not point to the host system");
Logger.Debug($"{ipOfWebspaceWww.CanonicalName.Value}");
if (ipOfWebspaceWww.CanonicalName.Value != webspace.CloudPanel.Host + ".")
throw new DisplayException(
$"The dns record www.{webspace.Domain} does not point to {webspace.CloudPanel.Host}", true);
await CloudPanelApiHelper.Post(webspace.CloudPanel, "letsencrypt/install/certificate", new InstallLetsEncrypt()
{
DomainName = webspace.Domain
});
}
#region Databases
public Task<MySqlDatabase[]> GetDatabases(WebSpace w)
{
return Task.FromResult(WebSpaceRepository
.Get()
.Include(x => x.Databases)
.First(x => x.Id == w.Id)
.Databases.ToArray());
}
public async Task CreateDatabase(WebSpace w, string name, string password)
{
if (DatabaseRepository.Get().Any(x => x.UserName == name))
throw new DisplayException("A database with this name does already exist");
var webspace = EnsureData(w);
var database = new MySqlDatabase()
{
UserName = name,
Password = password
};
await CloudPanelApiHelper.Post(webspace.CloudPanel, "db", new AddDatabase()
{
DomainName = webspace.Domain,
DatabaseName = database.UserName,
DatabaseUserName = database.UserName,
DatabaseUserPassword = database.Password
});
webspace.Databases.Add(database);
WebSpaceRepository.Update(webspace);
}
public async Task DeleteDatabase(WebSpace w, MySqlDatabase database)
{
var webspace = EnsureData(w);
await CloudPanelApiHelper.Delete(webspace.CloudPanel, $"db/{database.UserName}", null);
webspace.Databases.Remove(database);
WebSpaceRepository.Update(webspace);
}
#endregion
public Task<FileAccess> CreateFileAccess(WebSpace w)
{
var webspace = EnsureData(w);
return Task.FromResult<FileAccess>(
new SftpFileAccess(webspace.CloudPanel.Host, webspace.UserName, webspace.Password, 22, true,
$"/htdocs/{webspace.Domain}")
);
}
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;
}
}