diff --git a/Moonlight/App/Models/Forms/DatabaseDataModel.cs b/Moonlight/App/Models/Forms/DatabaseDataModel.cs new file mode 100644 index 00000000..b298512d --- /dev/null +++ b/Moonlight/App/Models/Forms/DatabaseDataModel.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; + +namespace Moonlight.App.Models.Forms; + +public class DatabaseDataModel +{ + [Required(ErrorMessage = "You need to enter a name")] + [MinLength(8, ErrorMessage = "The name should be at least 8 characters long")] + [MaxLength(32, ErrorMessage = "The database name should be maximal 32 characters")] + [RegularExpression(@"^[a-z0-9]+$", ErrorMessage = "The name should only contain of lower case characters and numbers")] + public string Name { get; set; } = ""; + + [Required(ErrorMessage = "You need to enter a password")] + [MinLength(8, ErrorMessage = "The password should be at least 8 characters long")] + [MaxLength(32, ErrorMessage = "The password name should be maximal 32 characters")] + public string Password { get; set; } = ""; +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Requests/CreateDatabase.cs b/Moonlight/App/Models/Plesk/Requests/CreateDatabase.cs new file mode 100644 index 00000000..11c9a0cb --- /dev/null +++ b/Moonlight/App/Models/Plesk/Requests/CreateDatabase.cs @@ -0,0 +1,23 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Requests; + +public class CreateDatabase +{ + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("parent_domain")] public ParentDomainModel ParentDomain { get; set; } = new(); + + [JsonProperty("server_id")] + public int ServerId { get; set; } + + public class ParentDomainModel + { + [JsonProperty("name")] + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Requests/CreateDatabaseUser.cs b/Moonlight/App/Models/Plesk/Requests/CreateDatabaseUser.cs new file mode 100644 index 00000000..eca5dd38 --- /dev/null +++ b/Moonlight/App/Models/Plesk/Requests/CreateDatabaseUser.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Requests; + +public class CreateDatabaseUser +{ + [JsonProperty("login")] + public string Login { get; set; } + + [JsonProperty("password")] + public string Password { get; set; } + + [JsonProperty("database_id")] + public int DatabaseId { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Resources/Database.cs b/Moonlight/App/Models/Plesk/Resources/Database.cs new file mode 100644 index 00000000..27a78993 --- /dev/null +++ b/Moonlight/App/Models/Plesk/Resources/Database.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Resources; + +public class Database +{ + [JsonProperty("id")] + public int Id { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Resources/DatabaseServer.cs b/Moonlight/App/Models/Plesk/Resources/DatabaseServer.cs new file mode 100644 index 00000000..a16ceabd --- /dev/null +++ b/Moonlight/App/Models/Plesk/Resources/DatabaseServer.cs @@ -0,0 +1,30 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Resources; + +public class DatabaseServer +{ + [JsonProperty("id")] + public int Id { get; set; } + + [JsonProperty("host")] + public string Host { get; set; } + + [JsonProperty("port")] + public int Port { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("status")] + public string Status { get; set; } + + [JsonProperty("db_count")] + public int DbCount { get; set; } + + [JsonProperty("is_default")] + public bool IsDefault { get; set; } + + [JsonProperty("is_local")] + public bool IsLocal { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Models/Plesk/Resources/DatabaseUser.cs b/Moonlight/App/Models/Plesk/Resources/DatabaseUser.cs new file mode 100644 index 00000000..033a763b --- /dev/null +++ b/Moonlight/App/Models/Plesk/Resources/DatabaseUser.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json; + +namespace Moonlight.App.Models.Plesk.Resources; + +public class DatabaseUser +{ + [JsonProperty("id")] + public int Id { get; set; } + + [JsonProperty("login")] + public string Login { get; set; } + + [JsonProperty("database_id")] + public int DatabaseId { get; set; } +} \ No newline at end of file diff --git a/Moonlight/App/Services/WebsiteService.cs b/Moonlight/App/Services/WebsiteService.cs index ad85d488..11906034 100644 --- a/Moonlight/App/Services/WebsiteService.cs +++ b/Moonlight/App/Services/WebsiteService.cs @@ -130,10 +130,21 @@ public class WebsiteService return false; } + #region Get host + public async Task GetHost(PleskServer pleskServer) { return (await PleskApiHelper.Get(pleskServer, "server")).Hostname; } + + public async Task GetHost(Website w) + { + var website = EnsureData(w); + + return await GetHost(website.PleskServer); + } + + #endregion private async Task GetAdminAccount(PleskServer pleskServer) { @@ -147,6 +158,7 @@ public class WebsiteService return user.Id; } + #region SSL public async Task GetSslCertificates(Website w) { var website = EnsureData(w); @@ -241,6 +253,90 @@ public class WebsiteService throw new DisplayException("An unknown error occured while disabling ssl certificate"); } } + + #endregion + + #region Databases + + public async Task GetDatabases(Website w) + { + var website = EnsureData(w); + + var dbs = await PleskApiHelper.Get( + 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(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( + 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 GetDefaultDatabaseServer(PleskServer pleskServer) + { + var dbServers = await PleskApiHelper.Get(pleskServer, "dbservers"); + + return dbServers.FirstOrDefault(x => x.IsDefault); + } + + public async Task GetDefaultDatabaseServer(Website w) + { + var website = EnsureData(w); + + return await GetDefaultDatabaseServer(website.PleskServer); + } + + #endregion public async Task CreateFileAccess(Website w) { diff --git a/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor b/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor index cbad558c..7f561201 100644 --- a/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor +++ b/Moonlight/Shared/Components/ErrorBoundaries/SoftErrorBoundary.razor @@ -43,6 +43,10 @@ pleskException.Message ); } + else if (exception is NotImplementedException) + { + await AlertService.Error(SmartTranslateService.Translate("This function is not implemented")); + } else { throw exception; diff --git a/Moonlight/Shared/Components/WebsiteControl/WebsiteDatabases.razor b/Moonlight/Shared/Components/WebsiteControl/WebsiteDatabases.razor new file mode 100644 index 00000000..3679564f --- /dev/null +++ b/Moonlight/Shared/Components/WebsiteControl/WebsiteDatabases.razor @@ -0,0 +1,135 @@ +@using Moonlight.App.Database.Entities +@using Moonlight.App.Models.Forms +@using Moonlight.App.Models.Plesk.Resources +@using Moonlight.App.Services + +@inject SmartTranslateService SmartTranslateService +@inject WebsiteService WebsiteService + +
+ +
+ +
+ +
+ + + +
+
+
+
+
+
+ @if (Databases.Any()) + { +
+ @foreach (var database in Databases) + { +
+

+ +

+
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+ +
+
+
+
+ } +
+ } + else + { +
+ No databases found for this website +
+ } +
+
+ +
+ +@code +{ + [CascadingParameter] + public Website CurrentWebsite { get; set; } + + private LazyLoader LazyLoader; + private Database[] Databases; + private DatabaseServer DatabaseServer; + private string Host; + + private DatabaseDataModel Model = new(); + + private async Task Load(LazyLoader arg) + { + Databases = await WebsiteService.GetDatabases(CurrentWebsite); + + if (Databases.Any()) + { + DatabaseServer = (await WebsiteService.GetDefaultDatabaseServer(CurrentWebsite))!; + Host = await WebsiteService.GetHost(CurrentWebsite); + } + } + + private async Task OnValidSubmit() + { + await WebsiteService.CreateDatabase(CurrentWebsite, Model.Name, Model.Password); + Model = new(); + await LazyLoader.Reload(); + } + + private async Task DeleteDatabase(Database database) + { + await WebsiteService.DeleteDatabase(CurrentWebsite, database); + await LazyLoader.Reload(); + } +} \ No newline at end of file diff --git a/Moonlight/Shared/Views/Website/Index.razor b/Moonlight/Shared/Views/Website/Index.razor index cc21a6b8..7b1a8cd3 100644 --- a/Moonlight/Shared/Views/Website/Index.razor +++ b/Moonlight/Shared/Views/Website/Index.razor @@ -62,6 +62,7 @@ break; case "databases": + break; default: diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/resources/lang/de_de.lang index a4f49ceb..1d9fd9c9 100644 --- a/Moonlight/resources/lang/de_de.lang +++ b/Moonlight/resources/lang/de_de.lang @@ -506,3 +506,9 @@ Api url;Api url Host system offline;Host system offline The host system the website is running on is currently offline;The host system the website is running on is currently offline No SSL certificates found;No SSL certificates found +No databases found for this website;No databases found for this website +The name should be at least 8 characters long;The name should be at least 8 characters long +The name should only contain of lower case characters and numbers;The name should only contain of lower case characters and numbers +Error from plesk;Error from plesk +Host;Host +Username;Username diff --git a/Moonlight/resources/lang/en_us.lang b/Moonlight/resources/lang/en_us.lang new file mode 100644 index 00000000..75bac759 --- /dev/null +++ b/Moonlight/resources/lang/en_us.lang @@ -0,0 +1,21 @@ +Open support;Open support +About us;About us +Imprint;Imprint +Privacy;Privacy +Create;Create +Server;Server +Domain;Domain +Website;Website +Login;Login +Register;Register +Email;Email +Password;Password +Sign In;Sign In +Sign in to start with moonlight;Sign in to start with moonlight +Sign in with Discord;Sign in with Discord +Sign in with Google;Sign in with Google +Or with email;Or with email +Forgot password?;Forgot password? +Sign-in;Sign-in +Not registered yet?;Not registered yet? +Sign up;Sign up