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,10 +130,21 @@ public class WebsiteService
|
||||
return false;
|
||||
}
|
||||
|
||||
#region Get host
|
||||
|
||||
public async Task<string> GetHost(PleskServer pleskServer)
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -147,6 +158,7 @@ public class WebsiteService
|
||||
return user.Id;
|
||||
}
|
||||
|
||||
#region SSL
|
||||
public async Task<string[]> 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<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)
|
||||
{
|
||||
|
||||
@@ -43,6 +43,10 @@
|
||||
pleskException.Message
|
||||
);
|
||||
}
|
||||
else if (exception is NotImplementedException)
|
||||
{
|
||||
await AlertService.Error(SmartTranslateService.Translate("This function is not implemented"));
|
||||
}
|
||||
else
|
||||
{
|
||||
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 />
|
||||
break;
|
||||
case "databases":
|
||||
<WebsiteDatabases />
|
||||
break;
|
||||
default:
|
||||
<WebsiteDashboard />
|
||||
|
||||
@@ -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
|
||||
|
||||
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