Merge branch 'main' into Register
This commit is contained in:
@@ -58,7 +58,7 @@ public class OAuth2Controller : Controller
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
token = await UserService.GenerateToken(user);
|
token = await UserService.GenerateToken(user, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Response.Cookies.Append("token", token, new ()
|
Response.Cookies.Append("token", token, new ()
|
||||||
@@ -116,7 +116,7 @@ public class OAuth2Controller : Controller
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
token = await UserService.GenerateToken(user);
|
token = await UserService.GenerateToken(user, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Response.Cookies.Append("token", token, new ()
|
Response.Cookies.Append("token", token, new ()
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ public class ServerService
|
|||||||
ServerRepository.Update(serverData);
|
ServerRepository.Update(serverData);
|
||||||
|
|
||||||
await MessageService.Emit("wings.backups.delete", backup);
|
await MessageService.Emit("wings.backups.delete", backup);
|
||||||
|
|
||||||
await AuditLogService.Log(AuditLogType.DeleteBackup,
|
await AuditLogService.Log(AuditLogType.DeleteBackup,
|
||||||
x =>
|
x =>
|
||||||
{
|
{
|
||||||
@@ -213,7 +213,7 @@ public class ServerService
|
|||||||
claims.Add("server_uuid", server.Uuid.ToString());
|
claims.Add("server_uuid", server.Uuid.ToString());
|
||||||
claims.Add("backup_uuid", serverBackup.Uuid.ToString());
|
claims.Add("backup_uuid", serverBackup.Uuid.ToString());
|
||||||
});
|
});
|
||||||
|
|
||||||
await AuditLogService.Log(AuditLogType.DownloadBackup,
|
await AuditLogService.Log(AuditLogType.DownloadBackup,
|
||||||
x =>
|
x =>
|
||||||
{
|
{
|
||||||
@@ -221,7 +221,10 @@ public class ServerService
|
|||||||
x.Add<ServerBackup>(serverBackup.Uuid);
|
x.Add<ServerBackup>(serverBackup.Uuid);
|
||||||
});
|
});
|
||||||
|
|
||||||
return $"https://{server.Node.Fqdn}:{server.Node.HttpPort}/download/backup?token={token}";
|
if (server.Node.Ssl)
|
||||||
|
return $"https://{server.Node.Fqdn}:{server.Node.HttpPort}/download/backup?token={token}";
|
||||||
|
else
|
||||||
|
return $"http://{server.Node.Fqdn}:{server.Node.HttpPort}/download/backup?token={token}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<FileAccess> CreateFileAccess(Server s, User user) // We need the user to create the launch url
|
public Task<FileAccess> CreateFileAccess(Server s, User user) // We need the user to create the launch url
|
||||||
@@ -240,7 +243,7 @@ public class ServerService
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Server> Create(string name, int cpu, long memory, long disk, User u, Image i, Node? n = null,
|
public async Task<Server> Create(string name, int cpu, long memory, long disk, User u, Image i, Node? n = null,
|
||||||
Action<Server>? modifyDetails = null)
|
Action<Server>? modifyDetails = null, int allocations = 1)
|
||||||
{
|
{
|
||||||
var user = UserRepository
|
var user = UserRepository
|
||||||
.Get()
|
.Get()
|
||||||
@@ -264,22 +267,27 @@ public class ServerService
|
|||||||
else
|
else
|
||||||
node = n;
|
node = n;
|
||||||
|
|
||||||
NodeAllocation freeAllo;
|
NodeAllocation[] freeAllocations;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
freeAllo = node.Allocations.First(a => !ServerRepository.Get()
|
freeAllocations = node.Allocations
|
||||||
.SelectMany(s => s.Allocations)
|
.Where(a => !ServerRepository.Get()
|
||||||
.Any(b => b.Id == a.Id)); // Thank you ChatGPT <3
|
.SelectMany(s => s.Allocations)
|
||||||
|
.Any(b => b.Id == a.Id))
|
||||||
|
.Take(allocations).ToArray();
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
throw new DisplayException("No allocation found");
|
throw new DisplayException("No allocation found");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (freeAllo == null)
|
if (!freeAllocations.Any())
|
||||||
throw new DisplayException("No allocation found");
|
throw new DisplayException("No allocation found");
|
||||||
|
|
||||||
|
if (freeAllocations.Length != allocations)
|
||||||
|
throw new DisplayException("Not enough allocations found");
|
||||||
|
|
||||||
var server = new Server()
|
var server = new Server()
|
||||||
{
|
{
|
||||||
Cpu = cpu,
|
Cpu = cpu,
|
||||||
@@ -290,11 +298,8 @@ public class ServerService
|
|||||||
Owner = user,
|
Owner = user,
|
||||||
Node = node,
|
Node = node,
|
||||||
Uuid = Guid.NewGuid(),
|
Uuid = Guid.NewGuid(),
|
||||||
MainAllocation = freeAllo,
|
MainAllocation = freeAllocations.First(),
|
||||||
Allocations = new()
|
Allocations = freeAllocations.ToList(),
|
||||||
{
|
|
||||||
freeAllo
|
|
||||||
},
|
|
||||||
Backups = new(),
|
Backups = new(),
|
||||||
OverrideStartup = "",
|
OverrideStartup = "",
|
||||||
DockerImageIndex = image.DockerImages.FindIndex(x => x.Default)
|
DockerImageIndex = image.DockerImages.FindIndex(x => x.Default)
|
||||||
@@ -322,10 +327,7 @@ public class ServerService
|
|||||||
StartOnCompletion = false
|
StartOnCompletion = false
|
||||||
});
|
});
|
||||||
|
|
||||||
await AuditLogService.Log(AuditLogType.CreateServer, x =>
|
await AuditLogService.Log(AuditLogType.CreateServer, x => { x.Add<Server>(newServerData.Uuid); });
|
||||||
{
|
|
||||||
x.Add<Server>(newServerData.Uuid);
|
|
||||||
});
|
|
||||||
|
|
||||||
return newServerData;
|
return newServerData;
|
||||||
}
|
}
|
||||||
@@ -333,7 +335,7 @@ public class ServerService
|
|||||||
{
|
{
|
||||||
await ErrorLogService.Log(e, x =>
|
await ErrorLogService.Log(e, x =>
|
||||||
{
|
{
|
||||||
x.Add<Server>(newServerData.Uuid);
|
x.Add<Server>(newServerData.Uuid);
|
||||||
x.Add<Node>(node.Id);
|
x.Add<Node>(node.Id);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -349,10 +351,7 @@ public class ServerService
|
|||||||
|
|
||||||
await WingsApiHelper.Post(server.Node, $"api/servers/{server.Uuid}/reinstall", null);
|
await WingsApiHelper.Post(server.Node, $"api/servers/{server.Uuid}/reinstall", null);
|
||||||
|
|
||||||
await AuditLogService.Log(AuditLogType.ReinstallServer, x =>
|
await AuditLogService.Log(AuditLogType.ReinstallServer, x => { x.Add<Server>(server.Uuid); });
|
||||||
{
|
|
||||||
x.Add<Server>(server.Uuid);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Server> SftpServerLogin(int serverId, int id, string password)
|
public async Task<Server> SftpServerLogin(int serverId, int id, string password)
|
||||||
@@ -361,10 +360,7 @@ public class ServerService
|
|||||||
|
|
||||||
if (server == null)
|
if (server == null)
|
||||||
{
|
{
|
||||||
await SecurityLogService.LogSystem(SecurityLogType.SftpBruteForce, x =>
|
await SecurityLogService.LogSystem(SecurityLogType.SftpBruteForce, x => { x.Add<int>(id); });
|
||||||
{
|
|
||||||
x.Add<int>(id);
|
|
||||||
});
|
|
||||||
throw new Exception("Server not found");
|
throw new Exception("Server not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,7 +389,7 @@ public class ServerService
|
|||||||
var server = EnsureNodeData(s);
|
var server = EnsureNodeData(s);
|
||||||
|
|
||||||
await WingsApiHelper.Delete(server.Node, $"api/servers/{server.Uuid}", null);
|
await WingsApiHelper.Delete(server.Node, $"api/servers/{server.Uuid}", null);
|
||||||
|
|
||||||
ServerRepository.Delete(s);
|
ServerRepository.Delete(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ public class UserService
|
|||||||
private readonly AuditLogService AuditLogService;
|
private readonly AuditLogService AuditLogService;
|
||||||
private readonly MailService MailService;
|
private readonly MailService MailService;
|
||||||
private readonly IdentityService IdentityService;
|
private readonly IdentityService IdentityService;
|
||||||
|
private readonly IpLocateService IpLocateService;
|
||||||
|
|
||||||
private readonly string JwtSecret;
|
private readonly string JwtSecret;
|
||||||
|
|
||||||
@@ -28,7 +29,7 @@ public class UserService
|
|||||||
SecurityLogService securityLogService,
|
SecurityLogService securityLogService,
|
||||||
AuditLogService auditLogService,
|
AuditLogService auditLogService,
|
||||||
MailService mailService,
|
MailService mailService,
|
||||||
IdentityService identityService)
|
IdentityService identityService, IpLocateService ipLocateService)
|
||||||
{
|
{
|
||||||
UserRepository = userRepository;
|
UserRepository = userRepository;
|
||||||
TotpService = totpService;
|
TotpService = totpService;
|
||||||
@@ -36,6 +37,7 @@ public class UserService
|
|||||||
AuditLogService = auditLogService;
|
AuditLogService = auditLogService;
|
||||||
MailService = mailService;
|
MailService = mailService;
|
||||||
IdentityService = identityService;
|
IdentityService = identityService;
|
||||||
|
IpLocateService = ipLocateService;
|
||||||
|
|
||||||
JwtSecret = configService
|
JwtSecret = configService
|
||||||
.GetSection("Moonlight")
|
.GetSection("Moonlight")
|
||||||
@@ -77,6 +79,7 @@ public class UserService
|
|||||||
});
|
});
|
||||||
|
|
||||||
await MailService.SendMail(user!, "register", values => {});
|
await MailService.SendMail(user!, "register", values => {});
|
||||||
|
|
||||||
await AuditLogService.Log(AuditLogType.Register, x =>
|
await AuditLogService.Log(AuditLogType.Register, x =>
|
||||||
{
|
{
|
||||||
x.Add<User>(user.Email);
|
x.Add<User>(user.Email);
|
||||||
@@ -177,11 +180,13 @@ public class UserService
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
var location = await IpLocateService.GetLocation();
|
||||||
|
|
||||||
await MailService.SendMail(user!, "passwordChange", values =>
|
await MailService.SendMail(user!, "passwordChange", values =>
|
||||||
{
|
{
|
||||||
values.Add("Ip", IdentityService.GetIp());
|
values.Add("Ip", IdentityService.GetIp());
|
||||||
values.Add("Device", IdentityService.GetDevice());
|
values.Add("Device", IdentityService.GetDevice());
|
||||||
values.Add("Location", "In your walls");
|
values.Add("Location", location);
|
||||||
});
|
});
|
||||||
|
|
||||||
await AuditLogService.Log(AuditLogType.ChangePassword, x =>
|
await AuditLogService.Log(AuditLogType.ChangePassword, x =>
|
||||||
@@ -201,6 +206,7 @@ public class UserService
|
|||||||
{
|
{
|
||||||
x.Add<int>(id);
|
x.Add<int>(id);
|
||||||
});
|
});
|
||||||
|
|
||||||
throw new Exception("Invalid username");
|
throw new Exception("Invalid username");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,12 +229,17 @@ public class UserService
|
|||||||
|
|
||||||
public async Task<string> GenerateToken(User user, bool sendMail = false)
|
public async Task<string> GenerateToken(User user, bool sendMail = false)
|
||||||
{
|
{
|
||||||
await MailService.SendMail(user!, "login", values =>
|
var location = await IpLocateService.GetLocation();
|
||||||
|
|
||||||
|
if (sendMail)
|
||||||
{
|
{
|
||||||
values.Add("Ip", IdentityService.GetIp());
|
await MailService.SendMail(user!, "login", values =>
|
||||||
values.Add("Device", IdentityService.GetDevice());
|
{
|
||||||
values.Add("Location", "In your walls");
|
values.Add("Ip", IdentityService.GetIp());
|
||||||
});
|
values.Add("Device", IdentityService.GetDevice());
|
||||||
|
values.Add("Location", location);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var token = JwtBuilder.Create()
|
var token = JwtBuilder.Create()
|
||||||
.WithAlgorithm(new HMACSHA256Algorithm())
|
.WithAlgorithm(new HMACSHA256Algorithm())
|
||||||
@@ -257,11 +268,13 @@ public class UserService
|
|||||||
|
|
||||||
await AuditLogService.Log(AuditLogType.PasswordReset, x => {});
|
await AuditLogService.Log(AuditLogType.PasswordReset, x => {});
|
||||||
|
|
||||||
|
var location = await IpLocateService.GetLocation();
|
||||||
|
|
||||||
await MailService.SendMail(user, "passwordReset", values =>
|
await MailService.SendMail(user, "passwordReset", values =>
|
||||||
{
|
{
|
||||||
values.Add("Ip", IdentityService.GetIp());
|
values.Add("Ip", IdentityService.GetIp());
|
||||||
values.Add("Device", IdentityService.GetDevice());
|
values.Add("Device", IdentityService.GetDevice());
|
||||||
values.Add("Location", "In your walls");
|
values.Add("Location", location);
|
||||||
values.Add("Password", newPassword);
|
values.Add("Password", newPassword);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,35 @@
|
|||||||
@inject AlertService AlertService
|
@inject AlertService AlertService
|
||||||
@inject SmartTranslateService SmartTranslateService
|
@inject SmartTranslateService SmartTranslateService
|
||||||
|
|
||||||
@ChildContent
|
@if (Crashed)
|
||||||
|
{
|
||||||
|
<div class="card card-flush h-md-100">
|
||||||
|
<div class="card-body d-flex flex-column justify-content-between mt-9 bgi-no-repeat bgi-size-cover bgi-position-x-center pb-0">
|
||||||
|
<div class="mb-10">
|
||||||
|
<div class="fs-2hx fw-bold text-gray-800 text-center mb-13">
|
||||||
|
<span class="me-2">
|
||||||
|
<TL>Ooops. This page is crashed</TL>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-center">
|
||||||
|
<TL>This page is crashed. The error has been reported to the moonlight team. Meanwhile you can try reloading the page</TL>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@ChildContent
|
||||||
|
}
|
||||||
|
|
||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
|
private bool Crashed = false;
|
||||||
|
|
||||||
protected override async Task OnErrorAsync(Exception exception)
|
protected override async Task OnErrorAsync(Exception exception)
|
||||||
{
|
{
|
||||||
Logger.Debug(exception);
|
Logger.Warn(exception);
|
||||||
|
|
||||||
if (exception is DisplayException displayException)
|
if (exception is DisplayException displayException)
|
||||||
{
|
{
|
||||||
@@ -49,7 +71,8 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw exception;
|
Crashed = true;
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,57 +59,55 @@
|
|||||||
<div id="kt_app_content" class="app-content flex-column-fluid">
|
<div id="kt_app_content" class="app-content flex-column-fluid">
|
||||||
<div id="kt_app_content_container" class="app-container container-fluid">
|
<div id="kt_app_content_container" class="app-container container-fluid">
|
||||||
<div class="mt-10">
|
<div class="mt-10">
|
||||||
<PageErrorBoundary>
|
<SoftErrorBoundary>
|
||||||
<SoftErrorBoundary>
|
@if (uri.LocalPath != "/login" &&
|
||||||
@if (uri.LocalPath != "/login" &&
|
uri.LocalPath != "/passwordreset" &&
|
||||||
uri.LocalPath != "/passwordreset" &&
|
uri.LocalPath != "/register")
|
||||||
uri.LocalPath != "/register")
|
{
|
||||||
|
if (User == null)
|
||||||
{
|
{
|
||||||
if (User == null)
|
<Login></Login>
|
||||||
{
|
|
||||||
<Login></Login>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (User.Status == UserStatus.Banned)
|
|
||||||
{
|
|
||||||
<BannedAlert></BannedAlert>
|
|
||||||
}
|
|
||||||
else if (User.Status == UserStatus.Disabled)
|
|
||||||
{
|
|
||||||
<DisabledAlert></DisabledAlert>
|
|
||||||
}
|
|
||||||
else if (User.Status == UserStatus.PasswordPending)
|
|
||||||
{
|
|
||||||
<PasswordChangeView></PasswordChangeView>
|
|
||||||
}
|
|
||||||
else if (User.Status == UserStatus.DataPending)
|
|
||||||
{
|
|
||||||
<UserDataSetView></UserDataSetView>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
@Body
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (uri.LocalPath == "/login")
|
if (User.Status == UserStatus.Banned)
|
||||||
{
|
{
|
||||||
<Login></Login>
|
<BannedAlert></BannedAlert>
|
||||||
}
|
}
|
||||||
else if (uri.LocalPath == "/register")
|
else if (User.Status == UserStatus.Disabled)
|
||||||
{
|
{
|
||||||
<Register></Register>
|
<DisabledAlert></DisabledAlert>
|
||||||
}
|
}
|
||||||
else if (uri.LocalPath == "/passwordreset")
|
else if (User.Status == UserStatus.PasswordPending)
|
||||||
{
|
{
|
||||||
<PasswordReset></PasswordReset>
|
<PasswordChangeView></PasswordChangeView>
|
||||||
|
}
|
||||||
|
else if (User.Status == UserStatus.DataPending)
|
||||||
|
{
|
||||||
|
<UserDataSetView></UserDataSetView>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@Body
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</SoftErrorBoundary>
|
}
|
||||||
</PageErrorBoundary>
|
else
|
||||||
|
{
|
||||||
|
if (uri.LocalPath == "/login")
|
||||||
|
{
|
||||||
|
<Login></Login>
|
||||||
|
}
|
||||||
|
else if (uri.LocalPath == "/register")
|
||||||
|
{
|
||||||
|
<Register></Register>
|
||||||
|
}
|
||||||
|
else if (uri.LocalPath == "/passwordreset")
|
||||||
|
{
|
||||||
|
<PasswordReset></PasswordReset>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</SoftErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -161,8 +159,8 @@
|
|||||||
await JsRuntime.InvokeVoidAsync("document.body.removeAttribute", "data-kt-app-page-loading");
|
await JsRuntime.InvokeVoidAsync("document.body.removeAttribute", "data-kt-app-page-loading");
|
||||||
await JsRuntime.InvokeVoidAsync("KTMenu.createInstances");
|
await JsRuntime.InvokeVoidAsync("KTMenu.createInstances");
|
||||||
await JsRuntime.InvokeVoidAsync("KTDrawer.createInstances");
|
await JsRuntime.InvokeVoidAsync("KTDrawer.createInstances");
|
||||||
|
|
||||||
//await JsRuntime.InvokeVoidAsync("createSnow");
|
//await JsRuntime.InvokeVoidAsync("createSnow");
|
||||||
|
|
||||||
await SessionService.Register();
|
await SessionService.Register();
|
||||||
|
|
||||||
|
|||||||
@@ -209,7 +209,9 @@
|
|||||||
disk,
|
disk,
|
||||||
User,
|
User,
|
||||||
Model.Image,
|
Model.Image,
|
||||||
DeployNode
|
DeployNode,
|
||||||
|
null,
|
||||||
|
Model.Image.Allocations
|
||||||
);
|
);
|
||||||
|
|
||||||
NavigationManager.NavigateTo($"/server/{server.Uuid}");
|
NavigationManager.NavigateTo($"/server/{server.Uuid}");
|
||||||
|
|||||||
@@ -1,39 +1,12 @@
|
|||||||
@page "/test"
|
@page "/test"
|
||||||
@using Moonlight.App.Repositories
|
|
||||||
@using Moonlight.App.Database.Entities
|
|
||||||
@using Moonlight.App.Models.Forms
|
|
||||||
|
|
||||||
@inject UserRepository UserRepository
|
|
||||||
|
|
||||||
<LazyLoader Load="Load">
|
<LazyLoader Load="Load">
|
||||||
<SmartForm Model="Model" OnValidSubmit="OnValidSubmit">
|
|
||||||
<div class="mb-3">
|
|
||||||
<SmartDropdown
|
|
||||||
Items="Users"
|
|
||||||
@bind-Value="Model.User"
|
|
||||||
DisplayFunc="@(x => x.Email)"
|
|
||||||
SearchProp="@(x => x.Email)" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<button class="btn btn-primary" type="submit">Submit</button>
|
|
||||||
</div>
|
|
||||||
</SmartForm>
|
|
||||||
</LazyLoader>
|
</LazyLoader>
|
||||||
|
|
||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
private User[] Users;
|
|
||||||
private TestDataModel Model = new();
|
|
||||||
|
|
||||||
private Task Load(LazyLoader arg)
|
private Task Load(LazyLoader arg)
|
||||||
{
|
{
|
||||||
Users = UserRepository.Get().ToArray();
|
throw new Exception("Nein");
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task OnValidSubmit()
|
|
||||||
{
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -540,3 +540,13 @@ The Email field is required.;The Email field is required.
|
|||||||
The Password field is required.;The Password field is required.
|
The Password field is required.;The Password field is required.
|
||||||
The ConfirmPassword field is required.;The ConfirmPassword field is required.
|
The ConfirmPassword field is required.;The ConfirmPassword field is required.
|
||||||
Passwords need to match;Passwords need to match
|
Passwords need to match;Passwords need to match
|
||||||
|
Cleanup exception;Cleanup exception
|
||||||
|
No shared domain found;No shared domain found
|
||||||
|
Searching for deploy plesk server;Searching for deploy plesk server
|
||||||
|
No plesk server found;No plesk server found
|
||||||
|
No plesk server found to deploy to;No plesk server found to deploy to
|
||||||
|
No node found to deploy to;No node found to deploy to
|
||||||
|
Website details;Website details
|
||||||
|
Configure your website;Configure your website
|
||||||
|
The name cannot be longer that 32 characters;The name cannot be longer that 32 characters
|
||||||
|
The name should only consist of lower case characters;The name should only consist of lower case characters
|
||||||
@@ -83,3 +83,4 @@ Welcome to the support chat. Ask your question here and we will help you;Welcome
|
|||||||
less than a minute ago;less than a minute ago
|
less than a minute ago;less than a minute ago
|
||||||
The support team has been notified. Please be patient;The support team has been notified. Please be patient
|
The support team has been notified. Please be patient;The support team has been notified. Please be patient
|
||||||
is typing;is typing
|
is typing;is typing
|
||||||
|
Proccessing;Proccessing
|
||||||
|
|||||||
Reference in New Issue
Block a user