Implemented databases
This commit is contained in:
17
Moonlight/App/Models/Forms/DatabaseDataModel.cs
Normal file
17
Moonlight/App/Models/Forms/DatabaseDataModel.cs
Normal file
@@ -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; } = "";
|
||||||
|
}
|
||||||
23
Moonlight/App/Models/Plesk/Requests/CreateDatabase.cs
Normal file
23
Moonlight/App/Models/Plesk/Requests/CreateDatabase.cs
Normal file
@@ -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; }
|
||||||
|
}
|
||||||
|
}
|
||||||
15
Moonlight/App/Models/Plesk/Requests/CreateDatabaseUser.cs
Normal file
15
Moonlight/App/Models/Plesk/Requests/CreateDatabaseUser.cs
Normal file
@@ -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; }
|
||||||
|
}
|
||||||
15
Moonlight/App/Models/Plesk/Resources/Database.cs
Normal file
15
Moonlight/App/Models/Plesk/Resources/Database.cs
Normal file
@@ -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; }
|
||||||
|
}
|
||||||
30
Moonlight/App/Models/Plesk/Resources/DatabaseServer.cs
Normal file
30
Moonlight/App/Models/Plesk/Resources/DatabaseServer.cs
Normal file
@@ -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; }
|
||||||
|
}
|
||||||
15
Moonlight/App/Models/Plesk/Resources/DatabaseUser.cs
Normal file
15
Moonlight/App/Models/Plesk/Resources/DatabaseUser.cs
Normal file
@@ -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; }
|
||||||
|
}
|
||||||
@@ -130,11 +130,22 @@ public class WebsiteService
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Get host
|
||||||
|
|
||||||
public async Task<string> GetHost(PleskServer pleskServer)
|
public async Task<string> GetHost(PleskServer pleskServer)
|
||||||
{
|
{
|
||||||
return (await PleskApiHelper.Get<ServerStatus>(pleskServer, "server")).Hostname;
|
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)
|
private async Task<int> GetAdminAccount(PleskServer pleskServer)
|
||||||
{
|
{
|
||||||
var users = await PleskApiHelper.Get<Client[]>(pleskServer, "clients");
|
var users = await PleskApiHelper.Get<Client[]>(pleskServer, "clients");
|
||||||
@@ -147,6 +158,7 @@ public class WebsiteService
|
|||||||
return user.Id;
|
return user.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region SSL
|
||||||
public async Task<string[]> GetSslCertificates(Website w)
|
public async Task<string[]> GetSslCertificates(Website w)
|
||||||
{
|
{
|
||||||
var website = EnsureData(w);
|
var website = EnsureData(w);
|
||||||
@@ -242,6 +254,90 @@ public class WebsiteService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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)
|
public async Task<FileAccess> CreateFileAccess(Website w)
|
||||||
{
|
{
|
||||||
var website = EnsureData(w);
|
var website = EnsureData(w);
|
||||||
|
|||||||
@@ -43,6 +43,10 @@
|
|||||||
pleskException.Message
|
pleskException.Message
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
else if (exception is NotImplementedException)
|
||||||
|
{
|
||||||
|
await AlertService.Error(SmartTranslateService.Translate("This function is not implemented"));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw exception;
|
throw exception;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
<div class="card card-flush h-xl-100">
|
||||||
|
<LazyLoader @ref="LazyLoader" Load="Load">
|
||||||
|
<div class="card-header">
|
||||||
|
<span class="card-toolbar">
|
||||||
|
<div class="mt-4">
|
||||||
|
<SmartForm Model="Model" OnValidSubmit="OnValidSubmit">
|
||||||
|
<div class="input-group">
|
||||||
|
<InputText @bind-Value="Model.Name" type="text" class="form-control" placeholder="@(SmartTranslateService.Translate("Name"))"></InputText>
|
||||||
|
<InputText @bind-Value="Model.Password" type="password" class="form-control" placeholder="@(SmartTranslateService.Translate("Password"))"></InputText>
|
||||||
|
<button class="btn btn-primary" type="submit">
|
||||||
|
<TL>Create</TL>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</SmartForm>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-body pt-2">
|
||||||
|
@if (Databases.Any())
|
||||||
|
{
|
||||||
|
<div class="accordion" id="databases">
|
||||||
|
@foreach (var database in Databases)
|
||||||
|
{
|
||||||
|
<div class="accordion-item">
|
||||||
|
<h2 class="accordion-header" id="databases_header_@(database.Id)">
|
||||||
|
<button class="accordion-button fs-4 fw-semibold" type="button" data-bs-toggle="collapse" data-bs-target="#databases_body_@(database.Id)">
|
||||||
|
@(database.Name) - @(database.Type.ToUpper())
|
||||||
|
</button>
|
||||||
|
</h2>
|
||||||
|
<div id="databases_body_@(database.Id)" class="accordion-collapse collapse" data-bs-parent="#databases">
|
||||||
|
<div class="accordion-body">
|
||||||
|
<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>Host</TL>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<input type="text" class="form-control form-control-solid disabled" disabled="disabled" value="@(Host)">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<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>Port</TL>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<input type="text" class="form-control form-control-solid disabled" disabled="disabled" value="@(DatabaseServer.Port)">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<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>Username</TL>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<input type="text" class="form-control form-control-solid disabled" disabled="disabled" value="@(database.Name)">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<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>Database</TL>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<input type="text" class="form-control form-control-solid disabled" disabled="disabled" value="@(database.Name)">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-end">
|
||||||
|
<DeleteButton Confirm="true" OnClick="() => DeleteDatabase(database)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
<TL>No databases found for this website</TL>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</LazyLoader>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -62,6 +62,7 @@
|
|||||||
<WebsiteFtp />
|
<WebsiteFtp />
|
||||||
break;
|
break;
|
||||||
case "databases":
|
case "databases":
|
||||||
|
<WebsiteDatabases />
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
<WebsiteDashboard />
|
<WebsiteDashboard />
|
||||||
|
|||||||
@@ -506,3 +506,9 @@ Api url;Api url
|
|||||||
Host system offline;Host system offline
|
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
|
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 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
|
||||||
|
|||||||
21
Moonlight/resources/lang/en_us.lang
Normal file
21
Moonlight/resources/lang/en_us.lang
Normal file
@@ -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
|
||||||
Reference in New Issue
Block a user