Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25b47d8b6c | ||
|
|
85f5b8a7da | ||
|
|
ab9333f99a | ||
|
|
d60f8fc905 | ||
|
|
fe1f4412d8 | ||
|
|
f191533410 | ||
|
|
a894707536 | ||
|
|
d2ccc84286 | ||
|
|
f2ec43f2d2 | ||
|
|
7feccc8d9f | ||
|
|
a8bd1193ce | ||
|
|
366d1a9205 | ||
|
|
df9ed95c6b | ||
|
|
23a211362e | ||
|
|
cf91d44902 | ||
|
|
35633e21a9 | ||
|
|
ce8b8f6798 | ||
|
|
c28c80ba25 | ||
|
|
da17b1df93 | ||
|
|
f9f5865ef9 | ||
|
|
389ded9b77 | ||
|
|
faebaa59dd | ||
|
|
6b7dc2ad05 | ||
|
|
e356c9d0c8 |
@@ -24,6 +24,8 @@ public class User
|
||||
|
||||
public string Country { get; set; } = "";
|
||||
|
||||
public string ServerListLayoutJson { get; set; } = "";
|
||||
|
||||
// States
|
||||
|
||||
public UserStatus Status { get; set; } = UserStatus.Unverified;
|
||||
@@ -31,6 +33,7 @@ public class User
|
||||
public bool SupportPending { get; set; } = false;
|
||||
public bool HasRated { get; set; } = false;
|
||||
public int Rating { get; set; } = 0;
|
||||
public bool StreamerMode { get; set; } = false;
|
||||
|
||||
// Security
|
||||
public bool TotpEnabled { get; set; } = false;
|
||||
|
||||
1077
Moonlight/App/Database/Migrations/20230623235512_AddedServerListLayoutToUser.Designer.cs
generated
Normal file
1077
Moonlight/App/Database/Migrations/20230623235512_AddedServerListLayoutToUser.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Moonlight.App.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddedServerListLayoutToUser : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "ServerListLayoutJson",
|
||||
table: "Users",
|
||||
type: "longtext",
|
||||
nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ServerListLayoutJson",
|
||||
table: "Users");
|
||||
}
|
||||
}
|
||||
}
|
||||
1080
Moonlight/App/Database/Migrations/20230625190428_AddedStreamerMode.Designer.cs
generated
Normal file
1080
Moonlight/App/Database/Migrations/20230625190428_AddedStreamerMode.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Moonlight.App.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddedStreamerMode : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "StreamerMode",
|
||||
table: "Users",
|
||||
type: "tinyint(1)",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "StreamerMode",
|
||||
table: "Users");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -780,6 +780,10 @@ namespace Moonlight.App.Database.Migrations
|
||||
b.Property<int>("Rating")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ServerListLayoutJson")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("State")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
@@ -787,6 +791,9 @@ namespace Moonlight.App.Database.Migrations
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<bool>("StreamerMode")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<int>("SubscriptionDuration")
|
||||
.HasColumnType("int");
|
||||
|
||||
|
||||
66
Moonlight/App/Helpers/Retry.cs
Normal file
66
Moonlight/App/Helpers/Retry.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
namespace Moonlight.App.Helpers;
|
||||
|
||||
public class Retry
|
||||
{
|
||||
private List<Type> RetryExceptionTypes;
|
||||
private List<Func<Exception, bool>> RetryFilters;
|
||||
private int RetryTimes = 1;
|
||||
|
||||
public Retry()
|
||||
{
|
||||
RetryExceptionTypes = new();
|
||||
RetryFilters = new();
|
||||
}
|
||||
|
||||
public Retry Times(int times)
|
||||
{
|
||||
RetryTimes = times;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Retry At(Func<Exception, bool> filter)
|
||||
{
|
||||
RetryFilters.Add(filter);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Retry At<T>()
|
||||
{
|
||||
RetryExceptionTypes.Add(typeof(T));
|
||||
return this;
|
||||
}
|
||||
|
||||
public async Task Call(Func<Task> method)
|
||||
{
|
||||
int triesLeft = RetryTimes;
|
||||
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
await method.Invoke();
|
||||
return;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if(triesLeft < 1) // Throw if no tries left
|
||||
throw;
|
||||
|
||||
if (RetryExceptionTypes.Any(x => x.FullName == e.GetType().FullName))
|
||||
{
|
||||
triesLeft--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (RetryFilters.Any(x => x.Invoke(e)))
|
||||
{
|
||||
triesLeft--;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Throw if not filtered -> unknown/unhandled
|
||||
throw;
|
||||
}
|
||||
} while (triesLeft >= 0);
|
||||
}
|
||||
}
|
||||
6
Moonlight/App/Models/Forms/UserPreferencesDataModel.cs
Normal file
6
Moonlight/App/Models/Forms/UserPreferencesDataModel.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Moonlight.App.Models.Forms;
|
||||
|
||||
public class UserPreferencesDataModel
|
||||
{
|
||||
public bool StreamerMode { get; set; } = false;
|
||||
}
|
||||
7
Moonlight/App/Models/Misc/ServerGroup.cs
Normal file
7
Moonlight/App/Models/Misc/ServerGroup.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Moonlight.App.Models.Misc;
|
||||
|
||||
public class ServerGroup
|
||||
{
|
||||
public string Name { get; set; } = "";
|
||||
public List<string> Servers { get; set; } = new();
|
||||
}
|
||||
@@ -86,6 +86,13 @@ public class DiscordOAuth2Provider : OAuth2Provider
|
||||
|
||||
var email = getData.GetValue<string>("email");
|
||||
var id = getData.GetValue<ulong>("id");
|
||||
var verified = getData.GetValue<bool>("verified");
|
||||
|
||||
if (!verified)
|
||||
{
|
||||
Logger.Warn("A user tried to use an unverified discord account to login", "security");
|
||||
throw new DisplayException("You can only use verified discord accounts for oauth signin");
|
||||
}
|
||||
|
||||
// Handle data
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using CloudFlare.Client;
|
||||
using CloudFlare.Client.Api.Authentication;
|
||||
using CloudFlare.Client.Api.Display;
|
||||
using CloudFlare.Client.Api.Parameters.Data;
|
||||
using CloudFlare.Client.Api.Result;
|
||||
using CloudFlare.Client.Api.Zones;
|
||||
@@ -12,6 +13,7 @@ using Moonlight.App.Helpers;
|
||||
using Moonlight.App.Models.Misc;
|
||||
using Moonlight.App.Repositories.Domains;
|
||||
using DnsRecord = Moonlight.App.Models.Misc.DnsRecord;
|
||||
using MatchType = CloudFlare.Client.Enumerators.MatchType;
|
||||
|
||||
namespace Moonlight.App.Services;
|
||||
|
||||
@@ -93,9 +95,36 @@ public class DomainService
|
||||
{
|
||||
var domain = EnsureData(d);
|
||||
|
||||
var records = GetData(
|
||||
await Client.Zones.DnsRecords.GetAsync(domain.SharedDomain.CloudflareId)
|
||||
var records = new List<CloudFlare.Client.Api.Zones.DnsRecord.DnsRecord>();
|
||||
|
||||
// Load paginated
|
||||
// TODO: Find an alternative option. This way to load the records is NOT optimal
|
||||
// and can result in long loading time when there are many dns records.
|
||||
// As cloudflare does not offer a way to search dns records which starts
|
||||
// with a specific string we are not able to filter it using the api (client)
|
||||
var initialResponse = await Client.Zones.DnsRecords.GetAsync(domain.SharedDomain.CloudflareId);
|
||||
|
||||
records.AddRange(GetData(initialResponse));
|
||||
|
||||
// Check if there are more pages
|
||||
while (initialResponse.ResultInfo.Page < initialResponse.ResultInfo.TotalPage)
|
||||
{
|
||||
// Get the next page of data
|
||||
var nextPageResponse = await Client.Zones.DnsRecords.GetAsync(
|
||||
domain.SharedDomain.CloudflareId,
|
||||
displayOptions: new()
|
||||
{
|
||||
Page = initialResponse.ResultInfo.Page + 1
|
||||
}
|
||||
);
|
||||
var nextPageRecords = GetData(nextPageResponse);
|
||||
|
||||
// Append the records from the next page to the existing records
|
||||
records.AddRange(nextPageRecords);
|
||||
|
||||
// Update the initial response to the next page response
|
||||
initialResponse = nextPageResponse;
|
||||
}
|
||||
|
||||
var rname = $"{domain.Name}.{domain.SharedDomain.Name}";
|
||||
var dname = $".{rname}";
|
||||
@@ -145,7 +174,11 @@ public class DomainService
|
||||
if (dnsRecord.Type == DnsRecordType.Srv)
|
||||
{
|
||||
var parts = dnsRecord.Name.Split(".");
|
||||
Enum.TryParse(parts[1], out Protocol protocol);
|
||||
|
||||
Protocol protocol = Protocol.Tcp;
|
||||
|
||||
if (parts[1].Contains("udp"))
|
||||
protocol = Protocol.Udp;
|
||||
|
||||
var valueParts = dnsRecord.Content.Split(" ");
|
||||
|
||||
|
||||
@@ -79,10 +79,20 @@ public class ServerService
|
||||
{
|
||||
Server server = EnsureNodeData(s);
|
||||
|
||||
return await WingsApiHelper.Get<ServerDetails>(
|
||||
ServerDetails result = null!;
|
||||
|
||||
await new Retry()
|
||||
.Times(3)
|
||||
.At(x => x.Message.Contains("A task was canceled"))
|
||||
.Call(async () =>
|
||||
{
|
||||
result = await WingsApiHelper.Get<ServerDetails>(
|
||||
server.Node,
|
||||
$"api/servers/{server.Uuid}"
|
||||
);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task SetPowerState(Server s, PowerSignal signal)
|
||||
@@ -109,7 +119,8 @@ public class ServerService
|
||||
|
||||
var backup = new ServerBackup()
|
||||
{
|
||||
Name = $"Created at {DateTimeService.GetCurrent().ToShortDateString()} {DateTimeService.GetCurrent().ToShortTimeString()}",
|
||||
Name =
|
||||
$"Created at {DateTimeService.GetCurrent().ToShortDateString()} {DateTimeService.GetCurrent().ToShortTimeString()}",
|
||||
Uuid = Guid.NewGuid(),
|
||||
CreatedAt = DateTimeService.GetCurrent(),
|
||||
Created = false
|
||||
@@ -175,8 +186,15 @@ public class ServerService
|
||||
|
||||
try
|
||||
{
|
||||
await WingsApiHelper.Delete(serverData.Node, $"api/servers/{serverData.Uuid}/backup/{serverBackup.Uuid}",
|
||||
await new Retry()
|
||||
.Times(3)
|
||||
.At(x => x.Message.Contains("A task was canceled"))
|
||||
.Call(async () =>
|
||||
{
|
||||
await WingsApiHelper.Delete(serverData.Node,
|
||||
$"api/servers/{serverData.Uuid}/backup/{serverBackup.Uuid}",
|
||||
null);
|
||||
});
|
||||
}
|
||||
catch (WingsException e)
|
||||
{
|
||||
@@ -257,7 +275,8 @@ public class ServerService
|
||||
|
||||
freeAllocations = NodeAllocationRepository
|
||||
.Get()
|
||||
.FromSqlRaw($"SELECT * FROM `NodeAllocations` WHERE ServerId IS NULL AND NodeId={node.Id} LIMIT {allocations}")
|
||||
.FromSqlRaw(
|
||||
$"SELECT * FROM `NodeAllocations` WHERE ServerId IS NULL AND NodeId={node.Id} LIMIT {allocations}")
|
||||
.ToArray();
|
||||
}
|
||||
catch (Exception)
|
||||
@@ -285,7 +304,8 @@ public class ServerService
|
||||
Allocations = freeAllocations.ToList(),
|
||||
Backups = new(),
|
||||
OverrideStartup = "",
|
||||
DockerImageIndex = image.DockerImages.FindIndex(x => x.Default)
|
||||
DockerImageIndex = image.DockerImages.FindIndex(x => x.Default),
|
||||
Installing = true
|
||||
};
|
||||
|
||||
foreach (var imageVariable in image.Variables)
|
||||
@@ -303,12 +323,18 @@ public class ServerService
|
||||
var newServerData = ServerRepository.Add(server);
|
||||
|
||||
try
|
||||
{
|
||||
await new Retry()
|
||||
.Times(3)
|
||||
.At(x => x.Message.Contains("A task was canceled"))
|
||||
.Call(async () =>
|
||||
{
|
||||
await WingsApiHelper.Post(node, $"api/servers", new CreateServer()
|
||||
{
|
||||
Uuid = newServerData.Uuid,
|
||||
StartOnCompletion = false
|
||||
});
|
||||
});
|
||||
|
||||
//TODO: AuditLog
|
||||
|
||||
@@ -369,8 +395,6 @@ public class ServerService
|
||||
|
||||
public async Task Delete(Server s)
|
||||
{
|
||||
throw new DisplayException("Deleting servers is currently disabled");
|
||||
|
||||
var backups = await GetBackups(s);
|
||||
|
||||
foreach (var backup in backups)
|
||||
@@ -391,7 +415,21 @@ public class ServerService
|
||||
.Include(x => x.Node)
|
||||
.First(x => x.Id == s.Id);
|
||||
|
||||
try
|
||||
{
|
||||
await new Retry()
|
||||
.Times(3)
|
||||
.At(x => x.Message.Contains("A task was canceled"))
|
||||
.Call(async () =>
|
||||
{
|
||||
await WingsApiHelper.Delete(server.Node, $"api/servers/{server.Uuid}", null);
|
||||
});
|
||||
}
|
||||
catch (WingsException e)
|
||||
{
|
||||
if (e.StatusCode != 404)
|
||||
throw;
|
||||
}
|
||||
|
||||
foreach (var variable in server.Variables.ToArray())
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Moonlight.App.Repositories;
|
||||
using Moonlight.App.Exceptions;
|
||||
using Moonlight.App.Repositories;
|
||||
using Moonlight.App.Services.Sessions;
|
||||
using OtpNet;
|
||||
|
||||
@@ -38,21 +39,24 @@ public class TotpService
|
||||
return user!.TotpSecret;
|
||||
}
|
||||
|
||||
public async Task Enable()
|
||||
public async Task GenerateSecret()
|
||||
{
|
||||
var user = (await IdentityService.Get())!;
|
||||
|
||||
user.TotpSecret = GenerateSecret();
|
||||
user.TotpSecret = Base32Encoding.ToString(KeyGeneration.GenerateRandomKey(20));;
|
||||
|
||||
UserRepository.Update(user);
|
||||
|
||||
//TODO: AuditLog
|
||||
}
|
||||
|
||||
public async Task EnforceTotpLogin()
|
||||
public async Task Enable(string code)
|
||||
{
|
||||
var user = (await IdentityService.Get())!;
|
||||
|
||||
if (!await Verify(user.TotpSecret, code))
|
||||
{
|
||||
throw new DisplayException("The 2fa code you entered is invalid");
|
||||
}
|
||||
|
||||
user.TotpEnabled = true;
|
||||
UserRepository.Update(user);
|
||||
}
|
||||
@@ -62,14 +66,10 @@ public class TotpService
|
||||
var user = (await IdentityService.Get())!;
|
||||
|
||||
user.TotpEnabled = false;
|
||||
user.TotpSecret = "";
|
||||
|
||||
UserRepository.Update(user);
|
||||
|
||||
//TODO: AuditLog
|
||||
}
|
||||
|
||||
private string GenerateSecret()
|
||||
{
|
||||
return Base32Encoding.ToString(KeyGeneration.GenerateRandomKey(20));
|
||||
}
|
||||
}
|
||||
@@ -103,6 +103,8 @@
|
||||
<script src="/_content/CurrieTechnologies.Razor.SweetAlert2/sweetAlert2.min.js"></script>
|
||||
<script src="/_content/Blazor.ContextMenu/blazorContextMenu.min.js"></script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/@@shopify/draggable@1.0.0-beta.11/lib/draggable.bundle.js"></script>
|
||||
|
||||
<script src="https://www.google.com/recaptcha/api.js"></script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.7.0/lib/xterm-addon-fit.min.js"></script>
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
{
|
||||
<SmartForm Model="TotpData" OnValidSubmit="DoLogin">
|
||||
<div class="fv-row mb-8 fv-plugins-icon-container">
|
||||
<InputText @bind-Value="TotpData.Code" type="number" class="form-control bg-transparent"></InputText>
|
||||
<InputText @bind-Value="TotpData.Code" type="number" class="form-control bg-transparent" placeholder="@(SmartTranslateService.Translate("2fa code"))"></InputText>
|
||||
</div>
|
||||
<div class="d-grid mb-10">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
@using Moonlight.App.Helpers.Files
|
||||
@using Moonlight.App.Services.Interop
|
||||
|
||||
@inject ModalService ModalService
|
||||
|
||||
<div class="modal fade" id="connectionDetails" tabindex="-1" aria-labelledby="connectionDetails" style="display: none;" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">
|
||||
<TL>Connection details</TL>
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="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="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="@(Port)">
|
||||
</div>
|
||||
</div>
|
||||
<div class="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="@(Username)">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
|
||||
<TL>Close</TL>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public FileAccess Access { get; set; }
|
||||
|
||||
private string Host = "";
|
||||
private string Username = "";
|
||||
private int Port;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
Uri uri = new Uri(await Access.GetLaunchUrl());
|
||||
|
||||
Host = uri.Host;
|
||||
Port = uri.Port;
|
||||
Username = uri.UserInfo.Split(':')[0];
|
||||
}
|
||||
catch (NotImplementedException)
|
||||
{
|
||||
Host = "N/A";
|
||||
Port = -1;
|
||||
Username = "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Show()
|
||||
{
|
||||
await ModalService.Show("connectionDetails");
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,9 @@ else
|
||||
@if (View != null && View.SelectedFiles.Any())
|
||||
{
|
||||
<div class="fw-bold me-5">
|
||||
<span class="me-2">@(View.SelectedFiles.Length) <TL>selected</TL></span>
|
||||
<span class="me-2">
|
||||
@(View.SelectedFiles.Length) <TL>selected</TL>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<WButton Text="@(SmartTranslateService.Translate("Move"))"
|
||||
@@ -57,35 +59,44 @@ else
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" @onclick="Launch" class="btn btn-light-primary me-3">
|
||||
<span class="svg-icon svg-icon-muted svg-icon-2">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path opacity="0.3" d="M5 16C3.3 16 2 14.7 2 13C2 11.3 3.3 10 5 10H5.1C5 9.7 5 9.3 5 9C5 6.2 7.2 4 10 4C11.9 4 13.5 5 14.3 6.5C14.8 6.2 15.4 6 16 6C17.7 6 19 7.3 19 9C19 9.4 18.9 9.7 18.8 10C18.9 10 18.9 10 19 10C20.7 10 22 11.3 22 13C22 14.7 20.7 16 19 16H5ZM8 13.6H16L12.7 10.3C12.3 9.89999 11.7 9.89999 11.3 10.3L8 13.6Z" fill="currentColor"/>
|
||||
<path d="M11 13.6V19C11 19.6 11.4 20 12 20C12.6 20 13 19.6 13 19V13.6H11Z" fill="currentColor"/>
|
||||
</svg>
|
||||
</span>
|
||||
<div class="btn-group me-3">
|
||||
<button type="button" @onclick="Launch" class="btn btn-light-primary">
|
||||
<TL>Launch WinSCP</TL>
|
||||
</button>
|
||||
<button type="button" class="btn btn-light-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<span class="visually-hidden"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a class="dropdown-item btn" target="_blank" href="https://winscp.net/eng/downloads.php">
|
||||
<TL>Download WinSCP</TL>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<button class="dropdown-item btn" @onclick="() => ConnectionDetailsModal.Show()">
|
||||
<TL>Show connection details</TL>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<button type="button" @onclick="CreateFile" class="btn btn-light-primary me-3">
|
||||
<span class="svg-icon svg-icon-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M6 22h12a2 2 0 0 0 2-2V8l-6-6H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2zm7-18 5 5h-5V4zM8 14h3v-3h2v3h3v2h-3v3h-2v-3H8v-2z"></path>
|
||||
</svg>
|
||||
</span>
|
||||
<div class="btn-group me-3">
|
||||
<button type="button" class="btn btn-light-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<TL>New</TL>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<button @onclick="CreateFile" class="dropdown-item btn">
|
||||
<TL>New file</TL>
|
||||
</button>
|
||||
|
||||
<button type="button" @onclick="CreateFolder" class="btn btn-light-primary me-3">
|
||||
<span class="svg-icon svg-icon-2">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path opacity="0.3" d="M10 4H21C21.6 4 22 4.4 22 5V7H10V4Z" fill="currentColor"></path>
|
||||
<path d="M10.4 3.60001L12 6H21C21.6 6 22 6.4 22 7V19C22 19.6 21.6 20 21 20H3C2.4 20 2 19.6 2 19V4C2 3.4 2.4 3 3 3H9.2C9.7 3 10.2 3.20001 10.4 3.60001ZM16 12H13V9C13 8.4 12.6 8 12 8C11.4 8 11 8.4 11 9V12H8C7.4 12 7 12.4 7 13C7 13.6 7.4 14 8 14H11V17C11 17.6 11.4 18 12 18C12.6 18 13 17.6 13 17V14H16C16.6 14 17 13.6 17 13C17 12.4 16.6 12 16 12Z" fill="currentColor"></path>
|
||||
<path opacity="0.3" d="M11 14H8C7.4 14 7 13.6 7 13C7 12.4 7.4 12 8 12H11V14ZM16 12H13V14H16C16.6 14 17 13.6 17 13C17 12.4 16.6 12 16 12Z" fill="currentColor"></path>
|
||||
</svg>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<button @onclick="CreateFolder" class="dropdown-item btn">
|
||||
<TL>New folder</TL>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<FileUpload Access="Access" OnUploadComplete="OnComponentStateChanged"/>
|
||||
}
|
||||
@@ -110,6 +121,8 @@ else
|
||||
Access="MoveAccess"
|
||||
OnSubmit="OnFileMoveSubmit">
|
||||
</FileSelectModal>
|
||||
|
||||
<ConnectionDetailsModal @ref="ConnectionDetailsModal" Access="Access"/>
|
||||
}
|
||||
|
||||
@code
|
||||
@@ -135,6 +148,9 @@ else
|
||||
// Config
|
||||
private ContextAction[] Actions = Array.Empty<ContextAction>();
|
||||
|
||||
// Connection details
|
||||
private ConnectionDetailsModal ConnectionDetailsModal;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
MoveAccess = (FileAccess)Access.Clone();
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<div class="d-flex justify-content-between align-items-start flex-wrap mb-2">
|
||||
<div class="d-flex flex-column">
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<a class="text-gray-900 fs-2 fw-bold me-1">@(User.FirstName) @(User.LastName)</a>
|
||||
<a class="text-gray-900 fs-2 fw-bold me-1 @(User.StreamerMode ? "blur" : "")">@(User.FirstName) @(User.LastName)</a>
|
||||
|
||||
@if (User.Status == UserStatus.Verified)
|
||||
{
|
||||
@@ -16,7 +16,7 @@
|
||||
}
|
||||
</div>
|
||||
<div class="d-flex flex-wrap fw-semibold fs-6 mb-4 pe-2">
|
||||
<span class="d-flex align-items-center text-gray-400 mb-2">
|
||||
<span class="d-flex align-items-center text-gray-400 mb-2 @(User.StreamerMode ? "blur" : "")">
|
||||
@(User.Email)
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -69,14 +69,16 @@
|
||||
</div>
|
||||
<div class="d-flex flex-column">
|
||||
<div class="fw-bold d-flex align-items-center fs-5">
|
||||
<div class="@(User.StreamerMode ? "blur" : "")">
|
||||
@(User.FirstName) @(User.LastName)
|
||||
</div>
|
||||
|
||||
@if (User.Admin)
|
||||
{
|
||||
<span class="badge badge-light-success fw-bold fs-8 px-2 py-1 ms-2">Admin</span>
|
||||
}
|
||||
</div>
|
||||
<a class="fw-semibold text-muted text-hover-primary fs-7">@(User.Email)</a>
|
||||
<a class="fw-semibold text-muted text-hover-primary fs-7 @(User.StreamerMode ? "blur" : "")">@(User.Email)</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
<div class="app-sidebar-footer flex-column-auto pt-2 pb-6 px-6" id="kt_app_sidebar_footer">
|
||||
<a href="/support" class="btn btn-flex flex-center btn-custom btn-primary overflow-hidden text-nowrap px-0 h-40px w-100 btn-label">
|
||||
<TL>Open support</TL>
|
||||
<i class="bx bx-sm bx-support"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
@using Moonlight.App.Models.Forms
|
||||
@using Moonlight.App.Repositories
|
||||
@using Mappy.Net
|
||||
@using Moonlight.App.Exceptions
|
||||
@using Moonlight.App.Helpers
|
||||
|
||||
@inject UserRepository UserRepository
|
||||
|
||||
@@ -13,7 +15,7 @@
|
||||
<LazyLoader Load="Load">
|
||||
<SmartForm OnValidSubmit="Save" Model="Model">
|
||||
<div class="card mb-5 mb-xl-10">
|
||||
<div class="card-body p-9">
|
||||
<div class="card-body p-9 @(CurrentUser.StreamerMode ? "blur" : "")">
|
||||
<div class="row">
|
||||
<div class="col-lg-6 fv-row fv-plugins-icon-container">
|
||||
<div class="mb-3">
|
||||
@@ -74,7 +76,7 @@
|
||||
|
||||
@code
|
||||
{
|
||||
private UserDataModel Model = new UserDataModel();
|
||||
private UserDataModel Model = new();
|
||||
|
||||
[CascadingParameter]
|
||||
public User CurrentUser { get; set; }
|
||||
@@ -89,9 +91,20 @@
|
||||
|
||||
private Task Save()
|
||||
{
|
||||
CurrentUser = Mapper.Map(CurrentUser, Model);
|
||||
// Prevent users from locking out other users by changing their email
|
||||
|
||||
CurrentUser.Email = CurrentUser.Email.ToLower();
|
||||
Model.Email = Model.Email.ToLower();
|
||||
var userWithThatEmail = UserRepository
|
||||
.Get()
|
||||
.FirstOrDefault(x => x.Email == Model.Email);
|
||||
|
||||
if (userWithThatEmail != null && CurrentUser.Id != userWithThatEmail.Id)
|
||||
{
|
||||
Logger.Warn($"A user tried to lock another user out by changing the email. Email: {Model.Email}", "security");
|
||||
throw new DisplayException("A user with that email does already exist");
|
||||
}
|
||||
|
||||
CurrentUser = Mapper.Map(CurrentUser, Model);
|
||||
|
||||
UserRepository.Update(CurrentUser);
|
||||
|
||||
|
||||
@@ -2,130 +2,152 @@
|
||||
|
||||
@using Moonlight.Shared.Components.Navigations
|
||||
@using QRCoder
|
||||
@using Moonlight.App.Services.Sessions
|
||||
@using Moonlight.App.Services
|
||||
@using Moonlight.App.Services.Interop
|
||||
@using System.Text.RegularExpressions
|
||||
@using Moonlight.App.Database.Entities
|
||||
@using Moonlight.App.Models.Misc
|
||||
@using Mappy.Net
|
||||
@using Moonlight.App.Models.Forms
|
||||
@using Moonlight.App.Repositories
|
||||
|
||||
@inject SmartTranslateService SmartTranslateService
|
||||
@inject TotpService TotpService
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IdentityService IdentityService
|
||||
@inject UserService UserService
|
||||
@inject AlertService AlertService
|
||||
@inject ToastService ToastService
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject ModalService ModalService
|
||||
@inject Repository<User> UserRepository
|
||||
@inject SmartTranslateService SmartTranslateService
|
||||
|
||||
<ProfileNavigation Index="2"/>
|
||||
|
||||
<div class="card mb-5 mb-xl-10">
|
||||
<LazyLoader Load="Load">
|
||||
@if (TotpEnabled)
|
||||
{
|
||||
<div class="alert alert-primary d-flex rounded ms-6 me-6 mt-6 mb-8">
|
||||
<table class="w-100">
|
||||
<tr>
|
||||
<td rowspan="2">
|
||||
<span class="svg-icon svg-icon-2tx svg-icon-primary">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path opacity="0.3" d="M20.5543 4.37824L12.1798 2.02473C12.0626 1.99176 11.9376 1.99176 11.8203 2.02473L3.44572 4.37824C3.18118 4.45258 3 4.6807 3 4.93945V13.569C3 14.6914 3.48509 15.8404 4.4417 16.984C5.17231 17.8575 6.18314 18.7345 7.446 19.5909C9.56752 21.0295 11.6566 21.912 11.7445 21.9488C11.8258 21.9829 11.9129 22 12.0001 22C12.0872 22 12.1744 21.983 12.2557 21.9488C12.3435 21.912 14.4326 21.0295 16.5541 19.5909C17.8169 18.7345 18.8277 17.8575 19.5584 16.984C20.515 15.8404 21 14.6914 21 13.569V4.93945C21 4.6807 20.8189 4.45258 20.5543 4.37824Z" fill="currentColor"></path>
|
||||
<path d="M10.5606 11.3042L9.57283 10.3018C9.28174 10.0065 8.80522 10.0065 8.51412 10.3018C8.22897 10.5912 8.22897 11.0559 8.51412 11.3452L10.4182 13.2773C10.8099 13.6747 11.451 13.6747 11.8427 13.2773L15.4859 9.58051C15.771 9.29117 15.771 8.82648 15.4859 8.53714C15.1948 8.24176 14.7183 8.24176 14.4272 8.53714L11.7002 11.3042C11.3869 11.6221 10.874 11.6221 10.5606 11.3042Z" fill="currentColor"></path>
|
||||
</svg>
|
||||
</span>
|
||||
</td>
|
||||
<td class="w-100">
|
||||
<h4 class="text-gray-900 fw-bold ms-4">
|
||||
<TL>Your account is secured with 2fa</TL>
|
||||
</h4>
|
||||
</td>
|
||||
<td rowspan="2">
|
||||
<a @onclick="Disable" class="btn btn-primary px-6 align-self-center text-nowrap" data-bs-toggle="modal" data-bs-target="#twofactorauth">
|
||||
<TL>Disable</TL>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="fs-6 text-gray-700 pe-7 ms-4">
|
||||
<TL>anyone write a fancy text here?</TL>
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6 p-3">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">
|
||||
<TL>Two factor authentication</TL>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-body fs-6">
|
||||
<p>
|
||||
<TL>2fa adds another layer of security to your account. You have to enter a 6 digit code in order to login.</TL>
|
||||
</p>
|
||||
<div class="d-flex justify-content-center">
|
||||
@if (User.TotpEnabled)
|
||||
{
|
||||
<WButton Text="@(SmartTranslateService.Translate("Disable"))"
|
||||
WorkingText=""
|
||||
CssClasses="btn-danger"
|
||||
OnClick="DisableTwoFactor">
|
||||
</WButton>
|
||||
}
|
||||
else
|
||||
{
|
||||
<WButton Text="@(SmartTranslateService.Translate("Enable"))"
|
||||
WorkingText=""
|
||||
CssClasses="btn-primary"
|
||||
OnClick="StartTwoFactorWizard">
|
||||
</WButton>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 p-3">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">
|
||||
<TL>Password</TL>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body fs-6">
|
||||
<div class="d-flex justify-content-center">
|
||||
<div class="input-group">
|
||||
<input @bind="Password" class="form-control" type="password"/>
|
||||
<WButton Text="@(SmartTranslateService.Translate("Enable"))"
|
||||
WorkingText=""
|
||||
CssClasses="btn-primary"
|
||||
OnClick="ChangePassword">
|
||||
</WButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 p-3">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">
|
||||
<TL>Preferences</TL>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body fs-6">
|
||||
<div class="form-check form-switch">
|
||||
<input @bind="UserModel.StreamerMode" class="form-check-input" type="checkbox" role="switch" id="streamerModeSwitch">
|
||||
<label class="form-check-label" for="streamerModeSwitch">
|
||||
<TL>Streamer mode</TL>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div class="text-end">
|
||||
<WButton Text="@(SmartTranslateService.Translate("Save"))"
|
||||
WorkingText=""
|
||||
CssClasses="btn-primary"
|
||||
OnClick="SavePreferences">
|
||||
</WButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* Modals *@
|
||||
<div class="modal fade" id="2fa" tabindex="-1" style="display: none" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h2 class="modal-title">
|
||||
<TL>Activate 2fa</TL>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="modal-body fs-6">
|
||||
@if (!User.TotpEnabled)
|
||||
{
|
||||
if (string.IsNullOrEmpty(User.TotpSecret))
|
||||
{
|
||||
<p>
|
||||
<TL>Make sure you have installed one of the following apps on your smartphone and press continue</TL>
|
||||
</p>
|
||||
|
||||
<a href="https://support.google.com/accounts/answer/1066447?hl=en" target="_blank">Google Authenticator</a>
|
||||
<br/>
|
||||
<a href="https://www.microsoft.com/en-us/account/authenticator" target="_blank">Microsoft Authenticator</a>
|
||||
<br/>
|
||||
<a href="https://authy.com/download/" target="_blank">Authy</a>
|
||||
<br/>
|
||||
<a href="https://support.1password.com/one-time-passwords/" target="_blank">1Password</a>
|
||||
<br/>
|
||||
|
||||
|
||||
<div class="d-flex justify-content-center">
|
||||
<WButton Text="@(SmartTranslateService.Translate("Continue"))"
|
||||
WorkingText="@(SmartTranslateService.Translate("Preparing"))"
|
||||
CssClasses="btn-primary"
|
||||
OnClick="GenerateTwoFactorToken">
|
||||
</WButton>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="alert alert-primary d-flex rounded ms-6 me-6 mt-6 mb-8">
|
||||
<table class="w-100">
|
||||
<tr>
|
||||
<td rowspan="2">
|
||||
<span class="svg-icon svg-icon-2tx svg-icon-primary">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path opacity="0.3" d="M20.5543 4.37824L12.1798 2.02473C12.0626 1.99176 11.9376 1.99176 11.8203 2.02473L3.44572 4.37824C3.18118 4.45258 3 4.6807 3 4.93945V13.569C3 14.6914 3.48509 15.8404 4.4417 16.984C5.17231 17.8575 6.18314 18.7345 7.446 19.5909C9.56752 21.0295 11.6566 21.912 11.7445 21.9488C11.8258 21.9829 11.9129 22 12.0001 22C12.0872 22 12.1744 21.983 12.2557 21.9488C12.3435 21.912 14.4326 21.0295 16.5541 19.5909C17.8169 18.7345 18.8277 17.8575 19.5584 16.984C20.515 15.8404 21 14.6914 21 13.569V4.93945C21 4.6807 20.8189 4.45258 20.5543 4.37824Z" fill="currentColor"></path>
|
||||
<path d="M10.5606 11.3042L9.57283 10.3018C9.28174 10.0065 8.80522 10.0065 8.51412 10.3018C8.22897 10.5912 8.22897 11.0559 8.51412 11.3452L10.4182 13.2773C10.8099 13.6747 11.451 13.6747 11.8427 13.2773L15.4859 9.58051C15.771 9.29117 15.771 8.82648 15.4859 8.53714C15.1948 8.24176 14.7183 8.24176 14.4272 8.53714L11.7002 11.3042C11.3869 11.6221 10.874 11.6221 10.5606 11.3042Z" fill="currentColor"></path>
|
||||
</svg>
|
||||
</span>
|
||||
</td>
|
||||
<td class="w-100">
|
||||
<h4 class="text-gray-900 fw-bold ms-4">
|
||||
<TL>Secure your account</TL>
|
||||
</h4>
|
||||
</td>
|
||||
<td rowspan="2">
|
||||
<a @onclick="Enable" class="btn btn-primary px-6 align-self-center text-nowrap" data-bs-toggle="modal" data-bs-target="#twofactorauth">
|
||||
<TL>Enable</TL>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="fs-6 text-gray-700 pe-7 ms-4">
|
||||
<TL>2fa adds another layer of security to your account. You have to enter a 6 digit code in order to login.</TL>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="modal fade" id="twofactorauth" tabindex="-1" style="display: none;" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered mw-650px">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header flex-stack py-6">
|
||||
<h2 class="ms-3">
|
||||
<TL>Activate 2fa</TL>
|
||||
</h2>
|
||||
<div class="btn btn-sm btn-icon btn-active-color-primary" data-bs-dismiss="modal">
|
||||
<span class="svg-icon svg-icon-1">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect opacity="0.5" x="6" y="17.3137" width="16" height="2" rx="1" transform="rotate(-45 6 17.3137)" fill="currentColor"></rect>
|
||||
<rect x="7.41422" y="6" width="16" height="2" rx="1" transform="rotate(45 7.41422 6)" fill="currentColor"></rect>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body scroll-y ps-10 pe-10 pb-10">
|
||||
<div>
|
||||
<h3 class="text-dark fw-bold mb-3 mt-2">
|
||||
<TL>2fa apps</TL>
|
||||
</h3>
|
||||
<div class="text-gray-500 fw-semibold fs-6 mb-10">
|
||||
<TL>Use an app like </TL>
|
||||
<a href="https://support.google.com/accounts/answer/1066447?hl=en" target="_blank">Google Authenticator</a>,
|
||||
<a href="https://www.microsoft.com/en-us/account/authenticator" target="_blank">Microsoft Authenticator</a>,
|
||||
<a href="https://authy.com/download/" target="_blank">Authy</a>, <TL>or</TL>
|
||||
<a href="https://support.1password.com/one-time-passwords/" target="_blank">1Password</a> <TL>and scan the following QR Code</TL>
|
||||
@if (EnablingTotp)
|
||||
{
|
||||
<div class="pt-5 text-center">
|
||||
<p>
|
||||
<TL>Scan the qr code and enter the code generated by the app you have scanned it in</TL>
|
||||
</p>
|
||||
<div class="mt-3 text-center">
|
||||
@{
|
||||
QRCodeGenerator qrGenerator = new QRCodeGenerator();
|
||||
|
||||
var qrCodeData = qrGenerator.CreateQrCode
|
||||
(
|
||||
$"otpauth://totp/{Uri.EscapeDataString(User.Email)}?secret={TotpSecret}&issuer={Uri.EscapeDataString(Issuer)}",
|
||||
$"otpauth://totp/{Uri.EscapeDataString(User.Email)}?secret={User.TotpSecret}&issuer={Uri.EscapeDataString("Moonlight")}",
|
||||
QRCodeGenerator.ECCLevel.Q
|
||||
);
|
||||
|
||||
@@ -135,184 +157,77 @@
|
||||
}
|
||||
<img src="data:image/png;base64,@(base64)" alt="" class="mw-200px mt-2">
|
||||
</div>
|
||||
<div class="mt-3 d-flex justify-content-center">
|
||||
<div class="input-group">
|
||||
<input type="text"
|
||||
@bind="TwoFactorCode"
|
||||
placeholder="@(SmartTranslateService.Translate("Enter your 2fa code here"))"
|
||||
class="form-control"/>
|
||||
<WButton Text="@(SmartTranslateService.Translate("Enable"))"
|
||||
WorkingText="@(SmartTranslateService.Translate("Processing"))"
|
||||
CssClasses="btn-primary"
|
||||
OnClick="EnableTwoFactor">
|
||||
</WButton>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<div class="notice d-flex bg-light-warning rounded border-warning border border-dashed mb-8 p-6">
|
||||
<span class="svg-icon svg-icon-2tx svg-icon-warning me-4">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect opacity="0.3" x="2" y="2" width="20" height="20" rx="10" fill="currentColor"></rect>
|
||||
<rect x="11" y="14" width="7" height="2" rx="1" transform="rotate(-90 11 14)" fill="currentColor"></rect>
|
||||
<rect x="11" y="17" width="2" height="2" rx="1" transform="rotate(-90 11 17)" fill="currentColor"></rect>
|
||||
</svg>
|
||||
</span>
|
||||
<div class="d-flex flex-stack flex-grow-1">
|
||||
<div class="fw-semibold">
|
||||
<div class="fs-6 text-gray-700">
|
||||
<TL>If you have trouble using the QR Code, select manual input in the app and enter your email and the following code:</TL>
|
||||
<div class="fw-bold text-dark pt-2">@(TotpSecret)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a class="btn btn-primary px-6 align-self-center text-nowrap float-end" data-bs-toggle="modal" data-bs-target="#test">
|
||||
<TL>Next</TL>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="test" tabindex="-1" style="display: none;" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered mw-650px">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header flex-stack py-6">
|
||||
<h2 class="ms-3">
|
||||
<TL>Finish activation</TL>
|
||||
</h2>
|
||||
<div class="btn btn-sm btn-icon btn-active-color-primary" data-bs-dismiss="modal">
|
||||
<span class="svg-icon svg-icon-1">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect opacity="0.5" x="6" y="17.3137" width="16" height="2" rx="1" transform="rotate(-45 6 17.3137)" fill="currentColor"></rect>
|
||||
<rect x="7.41422" y="6" width="16" height="2" rx="1" transform="rotate(45 7.41422 6)" fill="currentColor"></rect>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body scroll-y ps-10 pe-10 pb-10">
|
||||
<div class="text-gray-500 fw-semibold fs-6 mb-10">
|
||||
<div class="alert alert-primary d-flex align-items-center p-5 mb-6">
|
||||
<i class="bx bx-info-circle fs-2hx text-primary me-4">
|
||||
<span class="path1"></span><span class="path2"></span>
|
||||
</i>
|
||||
<div class="d-flex flex-column">
|
||||
<h4 class="mb-1 text-primary">
|
||||
<TL>2fa Code requiered</TL>
|
||||
</h4>
|
||||
<span>In order to finish the activation of 2fa, you need to enter the code your 2fa app shows you.</span>
|
||||
</div>
|
||||
</div>
|
||||
<input type="text" class="form-control form-control-lg form-control-solid mb-0" placeholder="@SmartTranslateService.Translate("2fa Code")" @bind="currentTotp"/>
|
||||
|
||||
<br/>
|
||||
|
||||
<WButton CssClasses="btn btn-primary mb-2 align-self-center text-nowrap float-end" WorkingText="@SmartTranslateService.Translate("Saving")" Text="@SmartTranslateService.Translate("Finish")" OnClick="CheckAndSaveTotp">
|
||||
</WButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="separator mt-2"></div>
|
||||
|
||||
<div class="alert alert-danger d-flex rounded ms-6 me-6 mt-6 mb-8 bg-body">
|
||||
<div class="w-100">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<span class="svg-icon svg-icon-2tx svg-icon-body">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path opacity="0.3" d="M20.5543 4.37824L12.1798 2.02473C12.0626 1.99176 11.9376 1.99176 11.8203 2.02473L3.44572 4.37824C3.18118 4.45258 3 4.6807 3 4.93945V13.569C3 14.6914 3.48509 15.8404 4.4417 16.984C5.17231 17.8575 6.18314 18.7345 7.446 19.5909C9.56752 21.0295 11.6566 21.912 11.7445 21.9488C11.8258 21.9829 11.9129 22 12.0001 22C12.0872 22 12.1744 21.983 12.2557 21.9488C12.3435 21.912 14.4326 21.0295 16.5541 19.5909C17.8169 18.7345 18.8277 17.8575 19.5584 16.984C20.515 15.8404 21 14.6914 21 13.569V4.93945C21 4.6807 20.8189 4.45258 20.5543 4.37824Z" fill="currentColor"></path>
|
||||
<path d="M10.5606 11.3042L9.57283 10.3018C9.28174 10.0065 8.80522 10.0065 8.51412 10.3018C8.22897 10.5912 8.22897 11.0559 8.51412 11.3452L10.4182 13.2773C10.8099 13.6747 11.451 13.6747 11.8427 13.2773L15.4859 9.58051C15.771 9.29117 15.771 8.82648 15.4859 8.53714C15.1948 8.24176 14.7183 8.24176 14.4272 8.53714L11.7002 11.3042C11.3869 11.6221 10.874 11.6221 10.5606 11.3042Z" fill="currentColor"></path>
|
||||
</svg>
|
||||
</span>
|
||||
</td>
|
||||
<td class="w-25">
|
||||
<span class="text-gray-700 fw-semibold fs-6 ms-4 me-2">
|
||||
<TL>New password</TL>
|
||||
</span>
|
||||
</td>
|
||||
<td class="w-75">
|
||||
<input @bind="Password" type="password" class="form-control">
|
||||
</td>
|
||||
<td class="">
|
||||
<WButton OnClick="ChangePassword"
|
||||
CssClasses="btn-danger ms-4"
|
||||
Text="@SmartTranslateService.Translate("Change")"
|
||||
WorkingText="@SmartTranslateService.Translate("Changing")">
|
||||
</WButton>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</LazyLoader>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
private bool TotpEnabled = false;
|
||||
private bool EnablingTotp = false;
|
||||
private string TotpSecret = "";
|
||||
private User User;
|
||||
private string Issuer = "Moonlight";
|
||||
private string currentTotp = "";
|
||||
[CascadingParameter]
|
||||
public User User { get; set; }
|
||||
|
||||
private string TwoFactorCode = "";
|
||||
private string Password = "";
|
||||
private UserPreferencesDataModel UserModel;
|
||||
|
||||
private async void Enable()
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
//TODO: AuditLog
|
||||
await TotpService.Enable();
|
||||
TotpEnabled = await TotpService.GetEnabled();
|
||||
TotpSecret = await TotpService.GetSecret();
|
||||
EnablingTotp = true;
|
||||
StateHasChanged();
|
||||
UserModel = Mapper.Map<UserPreferencesDataModel>(User);
|
||||
}
|
||||
|
||||
public async Task CheckAndSaveTotp()
|
||||
private async Task StartTwoFactorWizard()
|
||||
{
|
||||
if (await TotpService.Verify(TotpSecret, currentTotp))
|
||||
{
|
||||
await TotpService.EnforceTotpLogin();
|
||||
TotpEnabled = true;
|
||||
TotpSecret = await TotpService.GetSecret();
|
||||
await ToastService.Success("Successfully enabled 2fa!");
|
||||
StateHasChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
await AlertService.Error("2fa code incorrect", "The given 2fa code is incorrect. Maybe check if the code in your 2fa app has changed.");
|
||||
}
|
||||
await ModalService.Show("2fa");
|
||||
}
|
||||
|
||||
private async void Disable()
|
||||
private async Task GenerateTwoFactorToken()
|
||||
{
|
||||
//TODO: AuditLog
|
||||
await TotpService.Disable();
|
||||
await TotpService.GenerateSecret();
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private async Task EnableTwoFactor()
|
||||
{
|
||||
await ModalService.Hide("2fa");
|
||||
await TotpService.Enable(TwoFactorCode);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
NavigationManager.NavigateTo(NavigationManager.Uri, true);
|
||||
}
|
||||
|
||||
private async Task Load(LazyLoader lazyLoader)
|
||||
private async Task DisableTwoFactor()
|
||||
{
|
||||
await lazyLoader.SetText("Requesting secrets");
|
||||
|
||||
TotpEnabled = await TotpService.GetEnabled();
|
||||
TotpSecret = await TotpService.GetSecret();
|
||||
|
||||
await lazyLoader.SetText("Requesting identity");
|
||||
User = await IdentityService.Get();
|
||||
|
||||
await TotpService.Disable();
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private async Task ChangePassword()
|
||||
{
|
||||
if (Regex.IsMatch(Password, @"^(?=.*[A-Za-z])(?=.*\d)[A-Za-z@$!%*#.,?&\d]{8,}$"))
|
||||
{
|
||||
await UserService.ChangePassword(User, Password);
|
||||
|
||||
//TODO: AuditLog
|
||||
|
||||
// Reload to make the user login again
|
||||
NavigationManager.NavigateTo(NavigationManager.Uri, true);
|
||||
}
|
||||
else
|
||||
|
||||
private async Task SavePreferences()
|
||||
{
|
||||
await AlertService.Error("Error", "Your password must be at least 8 characters and must contain a number");
|
||||
}
|
||||
User = Mapper.Map(User, UserModel);
|
||||
UserRepository.Update(User);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
NavigationManager.NavigateTo(NavigationManager.Uri, true);
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@
|
||||
<div class="row align-items-center">
|
||||
<div class="col fs-5">
|
||||
<span class="fw-bold"><TL>Shared IP</TL>:</span>
|
||||
<span class="ms-1 text-muted">@($"{CurrentServer.Node.Fqdn}:{CurrentServer.MainAllocation.Port}")</span>
|
||||
<span class="ms-1 text-muted @(User.StreamerMode ? "blur" : "")">@($"{CurrentServer.Node.Fqdn}:{CurrentServer.MainAllocation.Port}")</span>
|
||||
</div>
|
||||
<div class="col fs-5">
|
||||
<span class="fw-bold"><TL>Server ID</TL>:</span>
|
||||
|
||||
@@ -1,211 +1,183 @@
|
||||
@page "/servers"
|
||||
@using Moonlight.App.Repositories.Servers
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Moonlight.App.Database.Entities
|
||||
@using Moonlight.App.Helpers
|
||||
@using Moonlight.App.Models.Misc
|
||||
@using Moonlight.App.Repositories
|
||||
@using Moonlight.App.Services
|
||||
@using Newtonsoft.Json
|
||||
|
||||
@inject ServerRepository ServerRepository
|
||||
@inject Repository<Server> ServerRepository
|
||||
@inject Repository<User> UserRepository
|
||||
@inject SmartTranslateService SmartTranslateService
|
||||
@inject IServiceScopeFactory ServiceScopeFactory
|
||||
@inject IJSRuntime JsRuntime
|
||||
|
||||
<LazyLoader Load="Load">
|
||||
@if (AllServers.Any())
|
||||
<LazyLoader @ref="LazyLoader" Load="Load">
|
||||
<div class="mx-auto">
|
||||
<div class="card card-body">
|
||||
<div class="d-flex justify-content-between">
|
||||
<span class="badge badge-primary badge-lg px-5 me-4">Beta</span>
|
||||
@if (EditMode)
|
||||
{
|
||||
if (UseSortedServerView)
|
||||
{
|
||||
var groupedServers = AllServers
|
||||
.OrderBy(x => x.Name)
|
||||
.GroupBy(x => x.Image.Name);
|
||||
|
||||
foreach (var groupedServer in groupedServers)
|
||||
{
|
||||
<div class="separator separator-content my-15">@(groupedServer.Key)</div>
|
||||
<div class="card card-body bg-secondary py-0 my-0 mx-0 px-0">
|
||||
@foreach (var server in groupedServer)
|
||||
{
|
||||
<div class="row mx-4 my-4">
|
||||
<a class="card card-body" href="/server/@(server.Uuid)">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="symbol symbol-50px me-3">
|
||||
<i class="bx bx-md bx-server"></i>
|
||||
<div>
|
||||
<WButton Text="@(SmartTranslateService.Translate("New group"))"
|
||||
WorkingText=""
|
||||
CssClasses="btn-primary me-3"
|
||||
OnClick="AddGroup">
|
||||
</WButton>
|
||||
<WButton Text="@(SmartTranslateService.Translate("Finish editing layout"))"
|
||||
CssClasses="btn-secondary"
|
||||
OnClick="async () => await SetEditMode(false)">
|
||||
</WButton>
|
||||
</div>
|
||||
<div class="d-flex justify-content-start flex-column">
|
||||
<a href="/server/@(server.Uuid)" class="text-gray-800 text-hover-primary mb-1 fs-5">
|
||||
@(server.Name)
|
||||
</a>
|
||||
<span class="text-gray-400 fw-semibold d-block fs-6">
|
||||
@(Math.Round(server.Memory / 1024D, 2)) GB / @(Math.Round(server.Disk / 1024D, 2)) GB / @(server.Node.Name) <span class="text-gray-700">- @(server.Image.Name)</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-none d-sm-block col my-auto fs-6">
|
||||
@(server.Node.Fqdn):@(server.MainAllocation.Port)
|
||||
</div>
|
||||
<div class="d-none d-sm-block col my-auto fs-6">
|
||||
@if (StatusCache.ContainsKey(server))
|
||||
{
|
||||
var status = StatusCache[server];
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case "offline":
|
||||
<span class="text-danger">
|
||||
<TL>Offline</TL>
|
||||
</span>
|
||||
break;
|
||||
case "stopping":
|
||||
<span class="text-warning">
|
||||
<TL>Stopping</TL>
|
||||
</span>
|
||||
break;
|
||||
case "starting":
|
||||
<span class="text-warning">
|
||||
<TL>Starting</TL>
|
||||
</span>
|
||||
break;
|
||||
case "running":
|
||||
<span class="text-success">
|
||||
<TL>Running</TL>
|
||||
</span>
|
||||
break;
|
||||
case "failed":
|
||||
<span class="text-gray-400">
|
||||
<TL>Failed</TL>
|
||||
</span>
|
||||
break;
|
||||
default:
|
||||
<span class="text-danger">
|
||||
<TL>Offline</TL>
|
||||
</span>
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="text-gray-400">
|
||||
<TL>Loading</TL>
|
||||
</span>
|
||||
<WButton Text="@(SmartTranslateService.Translate("Edit layout"))"
|
||||
CssClasses="btn-secondary"
|
||||
OnClick="async () => await SetEditMode(true)">
|
||||
</WButton>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@foreach (var group in ServerGroups)
|
||||
{
|
||||
<div class="accordion my-3" id="serverListGroup@(group.GetHashCode())">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="serverListGroup-header@(group.GetHashCode())">
|
||||
<button class="accordion-button fs-4 fw-semibold collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#serverListGroup-body@(group.GetHashCode())" aria-expanded="false" aria-controls="serverListGroup-body@(group.GetHashCode())">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
@if (EditMode)
|
||||
{
|
||||
<input @bind="group.Name" class="form-control"/>
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var server in AllServers)
|
||||
if (string.IsNullOrEmpty(group.Name))
|
||||
{
|
||||
<div class="row px-5 mb-5">
|
||||
<a class="card card-body" href="/server/@(server.Uuid)">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="symbol symbol-50px me-3">
|
||||
<i class="bx bx-md bx-server"></i>
|
||||
</div>
|
||||
<div class="d-flex justify-content-start flex-column">
|
||||
<a href="/server/@(server.Uuid)" class="text-gray-800 text-hover-primary mb-1 fs-5">
|
||||
@(server.Name)
|
||||
</a>
|
||||
<span class="text-gray-400 fw-semibold d-block fs-6">
|
||||
@(Math.Round(server.Memory / 1024D, 2)) GB / @(Math.Round(server.Disk / 1024D, 2)) GB / @(server.Node.Name) <span class="text-gray-700">- @(server.Image.Name)</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-none d-sm-block col my-auto fs-6">
|
||||
@(server.Node.Fqdn):@(server.MainAllocation.Port)
|
||||
</div>
|
||||
<div class="d-none d-sm-block col my-auto fs-6">
|
||||
@if (StatusCache.ContainsKey(server))
|
||||
<TL>Unsorted servers</TL>
|
||||
}
|
||||
else
|
||||
{
|
||||
var status = StatusCache[server];
|
||||
<span>@(group.Name)</span>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
@if (EditMode)
|
||||
{
|
||||
<WButton Text="@(SmartTranslateService.Translate("Remove group"))"
|
||||
WorkingText=""
|
||||
CssClasses="btn-danger"
|
||||
OnClick="async () => await RemoveGroup(group)">
|
||||
</WButton>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="serverListGroup-body@(group.GetHashCode())" class="accordion-collapse collapse" aria-labelledby="serverListGroup-header@(group.GetHashCode())" data-bs-parent="#serverListGroup">
|
||||
<div class="accordion-body">
|
||||
<div class="row min-h-200px draggable-zone" ml-server-group="@(group.Name)">
|
||||
@foreach (var id in group.Servers)
|
||||
{
|
||||
var server = AllServers.First(x => x.Id.ToString() == id);
|
||||
|
||||
switch (status)
|
||||
<div class="col-12 col-md-3 p-3 draggable" ml-server-id="@(server.Id)">
|
||||
@if (EditMode)
|
||||
{
|
||||
case "offline":
|
||||
<span class="text-danger">
|
||||
<TL>Offline</TL>
|
||||
</span>
|
||||
break;
|
||||
case "stopping":
|
||||
<span class="text-warning">
|
||||
<TL>Stopping</TL>
|
||||
</span>
|
||||
break;
|
||||
case "starting":
|
||||
<span class="text-warning">
|
||||
<TL>Starting</TL>
|
||||
</span>
|
||||
break;
|
||||
case "running":
|
||||
<span class="text-success">
|
||||
<TL>Running</TL>
|
||||
</span>
|
||||
break;
|
||||
case "failed":
|
||||
<span class="text-gray-400">
|
||||
<TL>Failed</TL>
|
||||
</span>
|
||||
break;
|
||||
default:
|
||||
<span class="text-danger">
|
||||
<TL>Offline</TL>
|
||||
</span>
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="text-gray-400">
|
||||
<TL>Loading</TL>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="alert bg-info d-flex flex-column flex-sm-row w-100 p-5">
|
||||
<div class="d-flex flex-column pe-0 pe-sm-10">
|
||||
<h4 class="fw-semibold">
|
||||
<TL>You have no servers</TL>
|
||||
</h4>
|
||||
<span>
|
||||
<TL>We were not able to find any servers associated with your account</TL>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="row mt-7 px-3">
|
||||
<div class="card">
|
||||
<div class="card bg-secondary">
|
||||
<div class="card-header">
|
||||
<div class="card-title">
|
||||
<span class="badge badge-primary badge-lg">Beta</span>
|
||||
<span class="card-label">@(server.Name)</span>
|
||||
</div>
|
||||
<div class="card-toolbar">
|
||||
<a href="#" class="btn btn-icon btn-sm btn-hover-light-primary draggable-handle">
|
||||
<i class="bx bx-md bx-move"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<label class="col-lg-4 col-form-label fw-semibold fs-6">Sorted server view</label>
|
||||
<div class="col-lg-8 d-flex align-items-center">
|
||||
<div class="form-check form-check-solid form-switch form-check-custom fv-row">
|
||||
<input class="form-check-input w-45px h-30px" type="checkbox" id="sortedServerView" @bind="UseSortedServerView">
|
||||
<label class="form-check-label" for="sortedServerView"></label>
|
||||
<TL>Hidden in edit mode</TL>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a class="invisible-a" href="/server/@(server.Uuid)">
|
||||
<div class="card bg-secondary">
|
||||
<div class="card-header">
|
||||
<div class="card-title">
|
||||
<span class="card-label">@(server.Name)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<span class="card-text fs-6">
|
||||
@(Math.Round(server.Memory / 1024D, 2)) GB / @(Math.Round(server.Disk / 1024D, 2)) GB / @(server.Node.Name) <span class="text-gray-700">- @(server.Image.Name)</span>
|
||||
</span>
|
||||
<div class="card-text my-1 fs-6 fw-bold @(User.StreamerMode ? "blur" : "")">
|
||||
@(server.Node.Fqdn):@(server.MainAllocation.Port)
|
||||
</div>
|
||||
<div class="card-text fs-6">
|
||||
@if (StatusCache.ContainsKey(server))
|
||||
{
|
||||
var status = StatusCache[server];
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case "offline":
|
||||
<span class="text-danger">
|
||||
<TL>Offline</TL>
|
||||
</span>
|
||||
break;
|
||||
case "stopping":
|
||||
<span class="text-warning">
|
||||
<TL>Stopping</TL>
|
||||
</span>
|
||||
break;
|
||||
case "starting":
|
||||
<span class="text-warning">
|
||||
<TL>Starting</TL>
|
||||
</span>
|
||||
break;
|
||||
case "running":
|
||||
<span class="text-success">
|
||||
<TL>Running</TL>
|
||||
</span>
|
||||
break;
|
||||
case "failed":
|
||||
<span class="text-gray-400">
|
||||
<TL>Failed</TL>
|
||||
</span>
|
||||
break;
|
||||
default:
|
||||
<span class="text-danger">
|
||||
<TL>Offline</TL>
|
||||
</span>
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="text-gray-400">
|
||||
<TL>Loading</TL>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</LazyLoader>
|
||||
|
||||
@@ -215,11 +187,13 @@ else
|
||||
public User User { get; set; }
|
||||
|
||||
private Server[] AllServers;
|
||||
private LazyLoader LazyLoader;
|
||||
private readonly Dictionary<Server, string> StatusCache = new();
|
||||
|
||||
private bool UseSortedServerView = false;
|
||||
private List<ServerGroup> ServerGroups = new();
|
||||
private bool EditMode = false;
|
||||
|
||||
private Task Load(LazyLoader arg)
|
||||
private async Task Load(LazyLoader arg)
|
||||
{
|
||||
AllServers = ServerRepository
|
||||
.Get()
|
||||
@@ -231,6 +205,21 @@ else
|
||||
.OrderBy(x => x.Name)
|
||||
.ToArray();
|
||||
|
||||
|
||||
if (string.IsNullOrEmpty(User.ServerListLayoutJson))
|
||||
{
|
||||
ServerGroups.Add(new()
|
||||
{
|
||||
Name = "",
|
||||
Servers = AllServers.Select(x => x.Id.ToString()).ToList()
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerGroups = (JsonConvert.DeserializeObject<ServerGroup[]>(
|
||||
User.ServerListLayoutJson) ?? Array.Empty<ServerGroup>()).ToList();
|
||||
}
|
||||
|
||||
foreach (var server in AllServers)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
@@ -248,6 +237,109 @@ else
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AddGroup()
|
||||
{
|
||||
ServerGroups.Insert(0, new()
|
||||
{
|
||||
Name = "New group"
|
||||
});
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
await JsRuntime.InvokeVoidAsync("moonlight.serverList.init");
|
||||
}
|
||||
|
||||
private async Task RemoveGroup(ServerGroup group)
|
||||
{
|
||||
ServerGroups.Remove(group);
|
||||
await EnsureAllServersInGroups();
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
await JsRuntime.InvokeVoidAsync("moonlight.serverList.init");
|
||||
}
|
||||
|
||||
private async Task SetEditMode(bool toggle)
|
||||
{
|
||||
EditMode = toggle;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
if (EditMode)
|
||||
{
|
||||
await EnsureAllServersInGroups();
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
await JsRuntime.InvokeVoidAsync("moonlight.serverList.init");
|
||||
}
|
||||
else
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(await GetGroupsFromClient());
|
||||
User.ServerListLayoutJson = json;
|
||||
UserRepository.Update(User);
|
||||
|
||||
await LazyLoader.Reload();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<ServerGroup[]> GetGroupsFromClient()
|
||||
{
|
||||
var serverGroups = await JsRuntime.InvokeAsync<ServerGroup[]>("moonlight.serverList.getData");
|
||||
|
||||
// Check user data to prevent users from doing stupid stuff
|
||||
foreach (var serverGroup in serverGroups)
|
||||
{
|
||||
if (serverGroup.Name.Length > 30)
|
||||
{
|
||||
Logger.Verbose("Server list group lenght too long");
|
||||
return Array.Empty<ServerGroup>();
|
||||
}
|
||||
|
||||
if (serverGroup.Servers.Any(x => AllServers.All(y => y.Id.ToString() != x)))
|
||||
{
|
||||
Logger.Verbose("User tried to add a server in his server list which he has no access to");
|
||||
return Array.Empty<ServerGroup>();
|
||||
}
|
||||
}
|
||||
|
||||
return serverGroups;
|
||||
}
|
||||
|
||||
private Task EnsureAllServersInGroups()
|
||||
{
|
||||
var presentInGroup = new List<Server>();
|
||||
|
||||
foreach (var group in ServerGroups)
|
||||
{
|
||||
foreach (var id in group.Servers)
|
||||
presentInGroup.Add(AllServers.First(x => x.Id.ToString() == id));
|
||||
}
|
||||
|
||||
var serversMissing = new List<Server>();
|
||||
|
||||
foreach (var server in AllServers)
|
||||
{
|
||||
if (presentInGroup.All(x => x.Id != server.Id))
|
||||
serversMissing.Add(server);
|
||||
}
|
||||
|
||||
if (serversMissing.Any())
|
||||
{
|
||||
var defaultGroup = ServerGroups.FirstOrDefault(x => x.Name == "");
|
||||
|
||||
if (defaultGroup == null)
|
||||
{
|
||||
defaultGroup = new ServerGroup()
|
||||
{
|
||||
Name = ""
|
||||
};
|
||||
|
||||
ServerGroups.Add(defaultGroup);
|
||||
}
|
||||
|
||||
foreach (var server in serversMissing)
|
||||
defaultGroup.Servers.Add(server.Id.ToString());
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -1,94 +1,94 @@
|
||||
Open support;Support öffnen
|
||||
Open support;Support Öffnen
|
||||
About us;Über uns
|
||||
Imprint;Impressum
|
||||
Privacy;Datenschutz
|
||||
Login;Anmelden
|
||||
Privacy;Privacy
|
||||
Login;Einloggen
|
||||
Register;Registrieren
|
||||
Insert brand name...;Markenname einfügen...
|
||||
Save and continue;Speichern und fortfahren
|
||||
Saving;Speichere
|
||||
Configure basics;Grundlagen konfigurieren
|
||||
Brand name;Markenname
|
||||
test;Test
|
||||
Insert first name...;Vornamen einfügen...
|
||||
Insert last name...;Nachname einfügen...
|
||||
Insert email address...;E-Mail Adresse einfügen...
|
||||
Insert brand name...;Firmenname eingeben...
|
||||
Save and continue;Speichern und Fortfahren
|
||||
Saving;Speichern
|
||||
Configure basics;Grundlagen einstellen
|
||||
Brand name;Firmenname
|
||||
test;test
|
||||
Insert first name...;Vornamen eingeben...
|
||||
Insert last name...;Nachnamen eingeben...
|
||||
Insert email address...;E-Mail-Adresse eingeben...
|
||||
Add;Hinzufügen
|
||||
Adding...;Füge hinzu...
|
||||
Add admin accounts;Admin-Konten hinzufügen
|
||||
Adding...;Wird hinzugefügt...
|
||||
Add admin accounts;Admin Konto hinzufügen
|
||||
First name;Vorname
|
||||
Last name;Nachname
|
||||
Email address;Email-Adresse
|
||||
Enter password;Passwort eingeben
|
||||
Email address;E-Mail-Adresse
|
||||
Enter password;Password eingeben
|
||||
Next;Weiter
|
||||
Back;Zurück
|
||||
Configure features;Funktionen konfigurieren
|
||||
Support chat;Support-Chat
|
||||
Finish;Beenden
|
||||
Finalize installation;Installation abschließen
|
||||
Moonlight basic settings successfully configured;Moonlight Grundeinstellungen erfolgreich konfiguriert
|
||||
Ooops. This page is crashed;Ooops. Diese Seite ist abgestürzt
|
||||
This page is crashed. The error has been reported to the moonlight team. Meanwhile you can try reloading the page;Diese Seite ist abgestürzt. Der Fehler wurde an das Moonlight-Team gemeldet. In der Zwischenzeit kannst du versuchen, die Seite neu zu laden
|
||||
Configure features;Features konfigurieren
|
||||
Support chat;Chat Hilfe
|
||||
Finish;Fertigstellen
|
||||
Finalize installation;Installation Fertigstellen
|
||||
Moonlight basic settings successfully configured;Moonlight's Standard-Einstellungen wurden konfiguriert
|
||||
Ooops. This page is crashed;Ups. Die Seite ist abgestürzt.
|
||||
This page is crashed. The error has been reported to the moonlight team. Meanwhile you can try reloading the page;Diese Seite ist abgestürzt. Bitte versuche, sie neu zu laden.
|
||||
Setup complete;Einrichtung abgeschlossen
|
||||
It looks like this moonlight instance is ready to go;Es sieht so aus, als ob diese Moonlight-Instanz einsatzbereit ist
|
||||
It looks like this moonlight instance is ready to go;Diese Moonlight Instanz ist bereit
|
||||
User successfully created;Benutzer erfolgreich erstellt
|
||||
Ooops. Your moonlight client is crashed;Ups. Dein Moonlight-Client ist abgestürzt
|
||||
This error has been reported to the moonlight team;Dieser Fehler wurde an das Moonlight-Team gemeldet
|
||||
Ooops. Your moonlight client is crashed;Ups. Dein Moonlight ist abgestürzt.
|
||||
This error has been reported to the moonlight team;Dieser Fehler wurde dem Moonlight-Team mitgeteilt
|
||||
Sign In;Anmelden
|
||||
Sign in to start with moonlight;Anmelden, um mit moonlight zu starten
|
||||
Sign in with Discord;Mit Discord anmelden
|
||||
Sign in to start with moonlight;Anmelden, um mit Moonlight zu starten
|
||||
Sign in with Discord;Mit Discord Anmelden
|
||||
Or with email;Oder mit E-Mail
|
||||
Forgot password?;Passwort vergessen?
|
||||
Forgot password?;Password vergessen?
|
||||
Sign-in;Anmelden
|
||||
Not registered yet?;Noch nicht registriert?
|
||||
Sign up;Anmelden
|
||||
Authenticating;Authentifiziere
|
||||
Sign in with Google;Mit Google anmelden
|
||||
Working;Läuft
|
||||
Not registered yet?;Noch nicht Registriert?
|
||||
Sign up;Registrieren
|
||||
Authenticating;Authentifizieren...
|
||||
Sign in with Google;Mit Google Anmelden
|
||||
Working;Bitte warten...
|
||||
Error;Fehler
|
||||
Email and password combination not found;E-Mail- und Passwortkombination nicht gefunden
|
||||
Email;E-Mail
|
||||
Password;Passwort
|
||||
Account settings;Kontoeinstellungen
|
||||
Email and password combination not found;E-Mail und Password-Kombination wurden nicht gefunden
|
||||
Email;E-mail
|
||||
Password;Password
|
||||
Account settings;Benutzer-Einstellungen
|
||||
Logout;Abmelden
|
||||
Dashboard;Dashboard
|
||||
Order;Bestellung
|
||||
Order;Bestellen
|
||||
Website;Website
|
||||
Database;Datenbank
|
||||
Domain;Domain
|
||||
Servers;Server
|
||||
Websites;Webseiten
|
||||
Websites;Websiten
|
||||
Databases;Datenbanken
|
||||
Domains;Domains
|
||||
Changelog;Changelog
|
||||
Changelog;Anderungen
|
||||
Firstname;Vorname
|
||||
Lastname;Nachname
|
||||
Repeat password;Passwort wiederholen
|
||||
Repeat password;Password wiederholen
|
||||
Sign Up;Anmelden
|
||||
Sign up to start with moonlight;Anmelden, um mit Moonlight zu beginnen
|
||||
Sign up with Discord;Mit Discord anmelden
|
||||
Sign up with Google;Mit Google anmelden
|
||||
Sign-up;Anmelden
|
||||
Already registered?;Bereits registriert?
|
||||
Sign in;Anmelden
|
||||
Create something new;Etwas Neues erstellen
|
||||
Sign up to start with moonlight;Registrieren um mit Moonlight zu starten
|
||||
Sign up with Discord;Mit Discord Registrieren
|
||||
Sign up with Google;Mit Google Registrieren
|
||||
Sign-up;Registrieren
|
||||
Already registered?;Schon Registriert?
|
||||
Sign in;Registrieren
|
||||
Create something new;Etwas neues erstellen
|
||||
Create a gameserver;Einen Gameserver erstellen
|
||||
A new gameserver in just a few minutes;Ein neuer Gameserver in nur wenigen Minuten
|
||||
Create a database;Erstelle eine Datenbank
|
||||
A quick way to store your data and manage it from all around the world;Ein schneller Weg, um deine Daten zu speichern und sie von überall auf der Welt zu verwalten
|
||||
Manage your services;Verwalte deine Services
|
||||
Manage your gameservers;Verwalte deine Gameserver
|
||||
A new gameserver in just a few minutes;Ein neuer Gameserver in wenigen Minuten
|
||||
Create a database;Eine Datenbank erstellen
|
||||
A quick way to store your data and manage it from all around the world;Eine schnelle Möglichkeit, um deine Daten von überall auf der Welt zu verwalten
|
||||
Manage your services;Deine Dienste verwalten
|
||||
Manage your gameservers;Gameserver verwalten
|
||||
Adjust your gameservers;Deine Gameserver anpassen
|
||||
Manage your databases;Verwalte deine Datenbanken
|
||||
Insert, delete and update the data in your databases;Einfügen, Löschen und Aktualisieren der Daten in deinen Datenbanken
|
||||
Manage your databases;Datenbanken verwalten
|
||||
Insert, delete and update the data in your databases;Daten in die Datenbank einfügen, entfernen und ändern
|
||||
Create a website;Eine Website erstellen
|
||||
Make your own websites with a webspace;Eigene Websites mit einem Webspace erstellen
|
||||
Make your own websites with a webspace;Mit einem Webspace eine Website erstellen
|
||||
Create a domain;Eine Domain erstellen
|
||||
Make your servvices accessible throught your own domain;Mache deine Services über deine eigene Domain zugänglich
|
||||
Manage your websites;Verwalte deine Webseiten
|
||||
Modify the content of your websites;Ändere den Inhalt deiner Websites
|
||||
Manage your domains;Verwalte deine Domains
|
||||
Add, edit and delete dns records;DNS-Einträge hinzufügen, bearbeiten und löschen
|
||||
Make your servvices accessible throught your own domain;Mache deine Dienste mit einer Domain erreichbar
|
||||
Manage your websites;Deine Websiten verwalten
|
||||
Modify the content of your websites;Den Inhalt deiner Websiten verwalten
|
||||
Manage your domains;Deine Domains verwalten
|
||||
Add, edit and delete dns records;DNS-Records hinzufügen, entfernen oder bearbeiten
|
||||
Admin;Admin
|
||||
System;System
|
||||
Overview;Übersicht
|
||||
@@ -98,72 +98,72 @@ Nodes;Nodes
|
||||
Images;Images
|
||||
aaPanel;aaPanel
|
||||
Users;Benutzer
|
||||
Support;Support
|
||||
Support;Hilfe
|
||||
Statistics;Statistiken
|
||||
No nodes found. Start with adding a new node;Keine Nodes gefunden. Beginne mit dem Hinzufügen einer neuen Node
|
||||
No nodes found. Start with adding a new node;Keine Nodes gefunden. Ein neues Node hinzufügen
|
||||
Nodename;Nodename
|
||||
FQDN;FQDN
|
||||
Create;Erstellen
|
||||
Creating;Erstelle
|
||||
Http port;Http-Port
|
||||
Sftp port;Sftp-Port
|
||||
Creating;Wird erstellt...
|
||||
Http port;Http Port
|
||||
Sftp port;Sftp Port
|
||||
Moonlight daemon port;Moonlight Daemon Port
|
||||
SSL;SSL
|
||||
CPU Usage;CPU-Auslastung
|
||||
CPU Usage;CPU Auslastung
|
||||
In %;In %
|
||||
Memory;Speicher
|
||||
Used / Available memory;Verwendeter / Verfügbarer Speicher
|
||||
Storage;Speicher
|
||||
Available storage;Verfügbarer Speicher
|
||||
Add a new node;Eine neue Node hinzufügen
|
||||
Memory;Arbeitsspeicher
|
||||
Used / Available memory;Benutzter / Verfügbarer Arbeitsspeicher
|
||||
Storage;Speicherplatz
|
||||
Available storage;Verfügbarer Speicherplatz
|
||||
Add a new node;Ein neues Node hinzufügen
|
||||
Delete;Löschen
|
||||
Deleting;Lösche
|
||||
Deleting;Wirt gelöscht...
|
||||
Edit;Bearbeiten
|
||||
Token Id;Token Id
|
||||
Token;Token
|
||||
Save;Speichern
|
||||
Setup;Einrichten
|
||||
Open a ssh connection to your node and enter;Öffne eine SSH-Verbindung zu deiner Node und gebe
|
||||
and paste the config below. Then press STRG+O and STRG+X to save;und füge die unten stehende Konfiguration ein. Drücke dann STRG+O und STRG+X, um zu speichern
|
||||
Before configuring this node, install the daemon;Bevor du diese Node konfigurierst, installiere den Daemon
|
||||
Delete this node?;Diese Node löschen?
|
||||
Do you really want to delete this node;Willst du diese Node wirklich löschen
|
||||
Setup;Aufsetzen
|
||||
Open a ssh connection to your node and enter;Eine SSH verbindung zum Node hinzufügen und öffnen
|
||||
and paste the config below. Then press STRG+O and STRG+X to save;und die Config darunter einfügern. Dann STRG+O und STRG+X um zu Speichern
|
||||
Before configuring this node, install the daemon;Installiere den Daemon bevor du dieses Node konfigurierst
|
||||
Delete this node?;Dieses Node löschen?
|
||||
Do you really want to delete this node;Möchtest du dieses Node wirklich löschen?
|
||||
Yes;Ja
|
||||
No;Nein
|
||||
Status;Status
|
||||
Adding;Füge hinzu
|
||||
Adding;Hinzufügen
|
||||
Port;Port
|
||||
Id;Id
|
||||
Manage;Verwalten
|
||||
Create new server;Neuen Server erstellen
|
||||
No servers found;Keine Server gefunden
|
||||
Server name;Servername
|
||||
Server name;Server Name
|
||||
Cpu cores;CPU Kerne
|
||||
Disk;Datenträger
|
||||
Disk;Speicherplatz
|
||||
Image;Image
|
||||
Override startup;Startup überschreiben
|
||||
Docker image;Docker-Image
|
||||
CPU Cores (100% = 1 Core);CPU-Kerne (100% = 1 Kern)
|
||||
Docker image;Docker Image
|
||||
CPU Cores (100% = 1 Core);CPU Kerne (100% = 1 Kern)
|
||||
Server successfully created;Server erfolgreich erstellt
|
||||
Name;Name
|
||||
Cores;CPU Kerne
|
||||
Owner;Eigentümer
|
||||
Cores;Kerne
|
||||
Owner;Besitzer
|
||||
Value;Wert
|
||||
An unknown error occured;Ein unbekannter Fehler ist aufgetreten
|
||||
No allocation found;Keine Allocation gefunden
|
||||
No allocation found;Keine Zuweisung gefunden
|
||||
Identifier;Identifier
|
||||
UuidIdentifier;UUIDIdentifier
|
||||
Override startup command;Startbefehl überschreiben
|
||||
Loading;Lade
|
||||
UuidIdentifier;UuidIdentifier
|
||||
Override startup command;Startup Befehl überschreiben
|
||||
Loading;Wird geladen...
|
||||
Offline;Offline
|
||||
Connecting;Verbinde
|
||||
Start;Starten
|
||||
Restart;Neustarten
|
||||
Connecting;Verbiden...
|
||||
Start;Start
|
||||
Restart;Neu Starten
|
||||
Stop;Stoppen
|
||||
Shared IP;Shared IP
|
||||
Server ID;Server-ID
|
||||
Cpu;Cpu
|
||||
Console;Konsole
|
||||
Shared IP;Geteilte IP
|
||||
Server ID;Server ID
|
||||
Cpu;CPU
|
||||
Console;Console
|
||||
Files;Dateien
|
||||
Backups;Backups
|
||||
Network;Netzwerk
|
||||
@@ -171,397 +171,466 @@ Plugins;Plugins
|
||||
Settings;Einstellungen
|
||||
Enter command;Befehl eingeben
|
||||
Execute;Ausführen
|
||||
Checking disk space;Prüfe Speicherplatz
|
||||
Updating config files;Aktualisiere Konfigurationsdateien
|
||||
Checking file permissions;Prüfe Dateiberechtigungen
|
||||
Downloading server image;Herunterladen des Serverabbilds
|
||||
Downloaded server image;Heruntergeladenes Serverabbild
|
||||
Starting;Starte
|
||||
Checking disk space;Speicherplatz überprüfen
|
||||
Updating config files;Konfigurations-Dateien werden geupdatet
|
||||
Checking file permissions;Datei-Rechte werden überprüft
|
||||
Downloading server image;Server Image wird heruntergeladen
|
||||
Downloaded server image;Server Image wurde heruntergeladen
|
||||
Starting;Startet
|
||||
Online;Online
|
||||
Kill;Killen
|
||||
Stopping;Stoppe
|
||||
Search files and folders;Dateien und Verzeichnisse durchsuchen
|
||||
Kill;Kill
|
||||
Stopping;Stoppt
|
||||
Search files and folders;Ordner und Dateien durchsuchen
|
||||
Launch WinSCP;WinSCP starten
|
||||
New folder;Neuer Ordner
|
||||
Upload;Hochladen
|
||||
File name;Dateiname
|
||||
File size;Dateigröße
|
||||
Last modified;Letzte Änderung
|
||||
File name;Datei-Name
|
||||
File size;Datei-Größe
|
||||
Last modified;Zuletz geändert
|
||||
Cancel;Abbrechen
|
||||
Canceling;Breche ab
|
||||
Running;Laufend
|
||||
Loading backups;Lade Backups
|
||||
Started backup creation;Backup-Erstellung gestartet
|
||||
Backup is going to be created;Backup wird erstellt
|
||||
Canceling;Wird Abbgebrochen
|
||||
Running;Läuft
|
||||
Loading backups;Backups werden geladen
|
||||
Started backup creation;Backup wird erstellt
|
||||
Backup is going to be created;Backup wird erstellt werden
|
||||
Rename;Umbenennen
|
||||
Move;Verschieben
|
||||
Move;Bewegen
|
||||
Archive;Archivieren
|
||||
Unarchive;Unarchivieren
|
||||
Unarchive;Archivieren rückgängig machen
|
||||
Download;Herunterladen
|
||||
Starting download;Starte Download
|
||||
Backup successfully created;Backup erfolgreich erstellt
|
||||
Starting download;Herunterladen wird gestartet
|
||||
Backup successfully created;Backup wurde erfolgreich erstellt
|
||||
Restore;Wiederherstellen
|
||||
Copy url;Url kopieren
|
||||
Backup deletion started;Backup-Löschung gestartet
|
||||
Backup successfully deleted;Backup erfolgreich gelöscht
|
||||
Primary;Primär
|
||||
This feature is currently not available;Diese Funktion ist derzeit nicht verfügbar
|
||||
Copy url;URL Kopieren
|
||||
Backup deletion started;Backup löschung wird gestartet
|
||||
Backup successfully deleted;Backup wurde erfolgreich gelöscht
|
||||
Primary;Primärer
|
||||
This feature is currently not available;Diese Funktion ist zur Zeit nicht verfügbar
|
||||
Send;Senden
|
||||
Sending;Sende
|
||||
Welcome to the support chat. Ask your question here and we will help you;Willkommen im Support-Chat. Stelle hier deine Frage und wir werden dir helfen
|
||||
minutes ago; vor Minuten
|
||||
just now;jetzt
|
||||
less than a minute ago;vor weniger als einer Minute
|
||||
1 hour ago;vor 1 Stunde
|
||||
1 minute ago;vor 1 Minute
|
||||
Sending;Wird gesendet
|
||||
Welcome to the support chat. Ask your question here and we will help you;Willkommen in der Chat Hilfe. Stelle hier deine Frage und wir helfen dir.
|
||||
minutes ago; Minuten
|
||||
just now;gerade eben
|
||||
less than a minute ago;weniger als eine Minute
|
||||
1 hour ago;vor einer Stunde
|
||||
1 minute ago;vor einer Minute
|
||||
Failed;Fehlgeschlagen
|
||||
hours ago; vor Stunden
|
||||
Open tickets;Offene Tickets
|
||||
hours ago; Stunden
|
||||
Open tickets;Tickets öffnen
|
||||
Actions;Aktionen
|
||||
No support ticket is currently open;Kein Supportticket ist zurzeit offen
|
||||
User information;Benutzerinformationen
|
||||
Close ticket;Anfrage schließen
|
||||
Closing;Schließe
|
||||
The support team has been notified. Please be patient;Das Support-Team wurde benachrichtigt. Bitte habe Geduld
|
||||
The ticket is now closed. Type a message to open it again;Das Ticket ist jetzt geschlossen. Gib eine Nachricht ein, um es wieder zu öffnen
|
||||
1 day ago;Vor 1 Tag
|
||||
is typing;tippt gerade
|
||||
are typing;tippen gerade
|
||||
No support ticket is currently open;Kein Support Ticket ist zurzeit offen.
|
||||
User information;Benutzer-Information
|
||||
Close ticket;Ticket schließen
|
||||
Closing;Wird geschlossen...
|
||||
The support team has been notified. Please be patient;Das Support-Team wurde benachrichtigt. Habe etwas gedult
|
||||
The ticket is now closed. Type a message to open it again;Das Ticket wurde geschlossen. Schreibe etwas, um es wieder zu öffnen
|
||||
1 day ago;vor einem Tag
|
||||
is typing;schreibt...
|
||||
are typing;schreiben
|
||||
No domains available;Keine Domains verfügbar
|
||||
Shared domains;Gemeinsame Domains
|
||||
Shared domain;Geteilte Domains
|
||||
Shared domain successfully deleted;Geteilte Domain erfolgreich gelöscht
|
||||
Shared domain successfully added;Geteilte Domain erfolgreich hinzugefügt
|
||||
Domain name;Domainname
|
||||
DNS records for;DNS-Einträge für
|
||||
Fetching dns records;Rufe DNS-Einträge ab
|
||||
No dns records found;Keine DNS-Einträge gefunden
|
||||
Shared domains;Geteilte Domains
|
||||
Shared domain;Geteilte Domain
|
||||
Shared domain successfully deleted;Geteilte Domain wurde erfolgreich gelöscht
|
||||
Shared domain successfully added;Geteilte Domain wurde erfolgreich hinzugefügt
|
||||
Domain name;Domain Name
|
||||
DNS records for;DNS-Record für
|
||||
Fetching dns records;Es wird nach DNS-Records gesucht
|
||||
No dns records found;Keine DNS-Records gefunden
|
||||
Content;Inhalt
|
||||
Priority;Priorität
|
||||
Ttl;TTL
|
||||
Enable cloudflare proxy;Cloudflare Proxy einschalten
|
||||
Ttl;Ttl
|
||||
Enable cloudflare proxy;Cloudflare-Proxy benutzen
|
||||
CF Proxy;CF Proxy
|
||||
days ago; Tage vergangen
|
||||
days ago; Tage
|
||||
Cancle;Abbrechen
|
||||
An unexpected error occured;Ein unerwarteter Fehler ist aufgetreten
|
||||
An unexpected error occured;Ein unbekannter Fehler ist aufgetreten
|
||||
Testy;Testy
|
||||
Error from cloudflare api;Fehler von der Cloudflare API
|
||||
Error from cloudflare api;Fehler von der Cloudflare-API
|
||||
Profile;Profil
|
||||
No subscription available;Kein Abonnement verfügbar
|
||||
No subscription available;Kein Abo verfügbar
|
||||
Buy;Kaufen
|
||||
Redirecting;Leite um
|
||||
Redirecting;Weiterleiten
|
||||
Apply;Anwenden
|
||||
Applying code;Code anwenden
|
||||
Invalid subscription code;Ungültiger Abo-Code
|
||||
Cancel Subscription;Abonnement kündigen
|
||||
Applying code;Code Anwenden
|
||||
Invalid subscription code;Unbekannter Abo-Code
|
||||
Cancel Subscription;Abo beenden
|
||||
Active until;Aktiv bis
|
||||
We will send you a notification upon subscription expiration;Wir senden dir eine Benachrichtigung, wenn dein Abonnement abläuft
|
||||
This token has been already used;Dieser Token wurde bereits verwendet
|
||||
We will send you a notification upon subscription expiration;Wenn dein Abo endet, senden wir dir eine E-Mail
|
||||
This token has been already used;Dieser Token wurde schon benutzt
|
||||
New login for;Neue Anmeldung für
|
||||
No records found for this day;Keine Daten für diesen Tag gefunden
|
||||
No records found for this day;Für diesen Tag wurden keine Records gefunden
|
||||
Change;Ändern
|
||||
Changing;Ändere
|
||||
Minecraft version;Minecraft-Version
|
||||
Build version;Build-Version
|
||||
Server installation is currently running;Serverinstallation läuft derzeit
|
||||
Changing;Wird geändert
|
||||
Minecraft version;Minecraft Version
|
||||
Build version;Build Version
|
||||
Server installation is currently running;Der Server wird installiert.
|
||||
Selected;Ausgewählt
|
||||
Move deleted;Verschiebe das Gelöschte
|
||||
Delete selected;Lösche das Ausgewählte
|
||||
Log level;Log-Stufe
|
||||
Log message;Logmeldung
|
||||
Move deleted;Gelöschtest Bewegen
|
||||
Delete selected;Ausgewähltes löschen
|
||||
Log level;Log Level
|
||||
Log message;Log Message
|
||||
Time;Zeit
|
||||
Version;Version
|
||||
You are running moonlight version;Du verwendest die Moonlight-Version
|
||||
You are running moonlight version;Du benutzt die Moonlight-Version
|
||||
Operating system;Betriebssystem
|
||||
Moonlight is running on;Moonlight läuft auf
|
||||
Memory usage;Speichernutzung
|
||||
Moonlight is using;Moonlight verwendet
|
||||
of memory;Speicherplatz
|
||||
Cpu usage;CPU Verbrauch
|
||||
Refresh;Aktualisieren
|
||||
Memory usage;Arbeitsspeicher-Auslastung
|
||||
Moonlight is using;Moonlight benutzt
|
||||
of memory;des Arbeitsspeichers
|
||||
Cpu usage;CPU Auslastung
|
||||
Refresh;Neu Laden
|
||||
Send a message to all users;Eine Nachricht an alle Benutzer senden
|
||||
IP;IP
|
||||
URL;URL
|
||||
Device;Gerät
|
||||
Change url;URL ändern
|
||||
Change url;URL Ändern
|
||||
Message;Nachricht
|
||||
Enter message;Nachricht eingeben
|
||||
Enter the message to send;Zu sendende Nachricht eingeben
|
||||
Enter the message to send;Eine Nachricht zum senden eingeben
|
||||
Confirm;Bestätigen
|
||||
Are you sure?;Bist du dir sicher?
|
||||
Are you sure?;Bist du dir sicher
|
||||
Enter url;URL eingeben
|
||||
An unknown error occured while starting backup deletion;Ein unbekannter Fehler ist beim Starten des Löschvorgangs des Backups aufgetreten
|
||||
Success;Erfolg
|
||||
Backup URL successfully copied to your clipboard;Backup-URL erfolgreich in die Zwischenablage kopiert
|
||||
Backup restore started;Wiederherstellung des Backups gestartet
|
||||
Backup successfully restored;Backup erfolgreich wiederhergestellt
|
||||
An unknown error occured while starting backup deletion;Ein unbekannter Fehler ist während der Backuplöschung aufgetreten
|
||||
Success;erfolgreich
|
||||
Backup URL successfully copied to your clipboard;Die Backup URL wurde in deine Zwischenablage kopiert
|
||||
Backup restore started;Backup wiederherstellung gestartet
|
||||
Backup successfully restored;Das Backup wurde erfolgreich wiedergeherstellt
|
||||
Register for;Registrieren für
|
||||
Core;Kern
|
||||
Logs;Logs
|
||||
AuditLog;AuditLog
|
||||
SecurityLog;Sicherheitslog
|
||||
ErrorLog;Fehlerlog
|
||||
Resources;Ressourcen
|
||||
WinSCP cannot be launched here;WinSCP kann hier nicht gestartet werden
|
||||
Create a new folder;Einen neuen Ordner erstellen
|
||||
Enter a name;Namen eingeben
|
||||
File upload complete;Datei-Upload abgeschlossen
|
||||
SecurityLog;SecurityLog
|
||||
ErrorLog;ErrorLog
|
||||
Resources;Resourcen
|
||||
WinSCP cannot be launched here;WinSCP kann nicht gestartet werden
|
||||
Create a new folder;Neuen Ordner erstellen
|
||||
Enter a name;Einen Namen eingeben
|
||||
File upload complete;Dateiupload abgeschlossen
|
||||
New server;Neuer Server
|
||||
Sessions;Sitzungen
|
||||
New user;Neuer Benutzer
|
||||
Created at;Erstellt am
|
||||
Mail template not found;Mailvorlage nicht gefunden
|
||||
Missing admin permissions. This attempt has been logged ;Fehlende Admin-Berechtigungen. Dieser Versuch wurde geloggt
|
||||
Address;Adresse
|
||||
Mail template not found;E-Mail template wurde nicht gefunden
|
||||
Missing admin permissions. This attempt has been logged ;Fehlende Admin-Rechte. Dieser Versuch wurde aufgezeichnet
|
||||
Address;Addresse
|
||||
City;Stadt
|
||||
State;Bundesland
|
||||
Country;Land
|
||||
State;Land
|
||||
Country;Staat
|
||||
Totp;Totp
|
||||
Discord;Discord
|
||||
Subscription;Abonnement
|
||||
None;Keine
|
||||
Subscription;Abonament
|
||||
None;None
|
||||
No user with this id found;Kein Benutzer mit dieser ID gefunden
|
||||
Back to list;Zurück zur Liste
|
||||
New domain;Neue Domain
|
||||
Reset password;Passwort zurücksetzen
|
||||
Password reset;Passwort zurücksetzen
|
||||
Reset the password of your account;Passwort für dein Konto zurücksetzen
|
||||
Back to list;Zurück zur liste
|
||||
New domain;Neue domain
|
||||
Reset password;Password wiederherstellen
|
||||
Password reset;Password wiederherstellung
|
||||
Reset the password of your account;Password deines Accounts zurücksetzen
|
||||
Wrong here?;Falsch hier?
|
||||
A user with this email can not be found;Ein Benutzer mit dieser E-Mail Adresse kann nicht gefunden werden
|
||||
Passwort reset successfull. Check your mail;Passwort zurücksetzen erfolgreich. Überprüfe deine Mails
|
||||
Discord bot;Discord-Bot
|
||||
A user with this email can not be found;Ein Benutzer mit dieser E-Mail konnte nicht gefunden werden
|
||||
Passwort reset successfull. Check your mail;Password wiederherstellung erfolgreich. Überprüfe deine Email
|
||||
Discord bot;Discord Bot
|
||||
New image;Neues Image
|
||||
Description;Beschreibung
|
||||
Uuid;UUID
|
||||
Enter tag name;Tag-Name eingeben
|
||||
Enter tag name;Tag Namen eingeben
|
||||
Remove;Entfernen
|
||||
No tags found;Keine Tags gefunden
|
||||
Enter docker image name;Name des Docker-Images eingeben
|
||||
Enter docker image name;Docker Image Namen eingeben
|
||||
Tags;Tags
|
||||
Docker images;Docker-Images
|
||||
Default image;Standardimage
|
||||
Startup command;Startup-Befehl
|
||||
Install container;Container installieren
|
||||
Install entry;Eintrag installieren
|
||||
Docker images;Docker Images
|
||||
Default image;Standard Image
|
||||
Startup command;Startup Befehl
|
||||
Install container;Install container
|
||||
Install entry;Install entry
|
||||
Configuration files;Konfigurationsdateien
|
||||
Startup detection;Startup-Erkennung
|
||||
Startup detection;Startuperkennung
|
||||
Stop command;Stopp-Befehl
|
||||
Successfully saved image;Image erfolgreich gespeichert
|
||||
Successfully saved image;Das Image wurde erfolgreich gespeichert
|
||||
No docker images found;Keine Docker Images gefunden
|
||||
Key;Schlüssel
|
||||
Default value;Standardwert
|
||||
Allocations;Allocations
|
||||
Allocations;Zuweisung
|
||||
No variables found;Keine Variablen gefunden
|
||||
Successfully added image;Image erfolgreich hinzugefügt
|
||||
Password change for;Passwortänderung für
|
||||
Successfully added image;Das Image wurde erfolgreich hinzugefügt
|
||||
Password change for;Password ändern für
|
||||
of;von
|
||||
New node;Neue Node
|
||||
Fqdn;FQDN
|
||||
Cores used;Verwendete CPU Kerne
|
||||
used;verwendet
|
||||
New node;Neues Node
|
||||
Fqdn;Fqdn
|
||||
Cores used;Kerne genutzt
|
||||
used;benutzt
|
||||
5.15.90.1-microsoft-standard-WSL2 - amd64;5.15.90.1-microsoft-standard-WSL2 - amd64
|
||||
Host system information;Host-System-Informationen
|
||||
Host system information;Host System Information
|
||||
0;0
|
||||
Docker containers running;Laufende Docker-Container
|
||||
Docker containers running;Laufende Docker Container
|
||||
details;Details
|
||||
1;1
|
||||
2;2
|
||||
DDos;DDos
|
||||
No ddos attacks found;Keine DDos-Angriffe gefunden
|
||||
No ddos attacks found;Keine DDoS gefunden
|
||||
Node;Node
|
||||
Date;Datum
|
||||
DDos attack started;DDos-Angriff gestartet
|
||||
DDos attack started;DDos Attacke gestartet
|
||||
packets;Pakete
|
||||
DDos attack stopped;DDos-Angriff gestoppt
|
||||
DDos attack stopped;DDos Attacke gestoppt
|
||||
packets; Pakete
|
||||
Stop all;Alle stoppen
|
||||
Kill all;Alle killen
|
||||
Network in;Netzwerk ein
|
||||
Network out;Netzwerk raus
|
||||
Kill all servers;Alle Server killen
|
||||
Do you really want to kill all running servers?;Willst du wirklich alle laufenden Server killen?
|
||||
Change power state for;Power State ändern für
|
||||
Stop all;Alle Stoppen
|
||||
Kill all;Allen Killen
|
||||
Network in;Network in
|
||||
Network out;Network out
|
||||
Kill all servers;Alle Server Killen
|
||||
Do you really want to kill all running servers?;Möchtest du wirklich alle laufenden Server Killen?
|
||||
Change power state for;Power-State ändern für
|
||||
to;zu
|
||||
Stop all servers;Alle Server stoppen
|
||||
Do you really want to stop all running servers?;Willst du wirklich alle laufenden Server stoppen?
|
||||
Do you really want to stop all running servers?;Möchtest du wirklich alle laufenden Server Killen?
|
||||
Manage ;Verwalten
|
||||
Manage user ;Benutzer verwalten
|
||||
Reloading;Lade neu
|
||||
Reloading;Neu Laden...
|
||||
Update;Aktualisieren
|
||||
Updating;Aktualisiere
|
||||
Updating;Wird Aktualisiert...
|
||||
Successfully updated user;Benutzer erfolgreich aktualisiert
|
||||
Discord id;Discord ID
|
||||
Discord username;Discord-Benutzername
|
||||
Discord discriminator;Discord-Diskriminator
|
||||
The Name field is required.;Das Feld Name ist erforderlich
|
||||
An error occured while logging you in;Beim Einloggen ist ein Fehler aufgetreten
|
||||
You need to enter an email address;Du musst eine E-Mail Adresse eingeben
|
||||
You need to enter a password;Du musst ein Passwort eingeben
|
||||
You need to enter a password with minimum 8 characters in lenght;Du musst ein Passwort mit einer länge von mindestens 8 Zeichen eingeben
|
||||
Proccessing;Bearbeite
|
||||
The FirstName field is required.;Das Feld Vorname ist erforderlich.
|
||||
The LastName field is required.;Das Feld Nachname ist erforderlich.
|
||||
The Address field is required.;Das Adressfeld ist erforderlich.
|
||||
The City field is required.;Das Feld Stadt ist erforderlich.
|
||||
The State field is required.;Das Feld Bundesland ist erforderlich.
|
||||
The Country field is required.;Das Feld Land ist erforderlich.
|
||||
Discord username;Discord Benutzername
|
||||
Discord discriminator;Discord Tag
|
||||
The Name field is required.;Der Name dieses Feldes ist erforderlich
|
||||
An error occured while logging you in;Ein Fehler ist aufgetreten, als du dich angemeldet hast
|
||||
You need to enter an email address;Du musst eine E-Mail-Adresse angeben
|
||||
You need to enter a password;Du musst ein Password eingeben
|
||||
You need to enter a password with minimum 8 characters in lenght;Du musst ein Password eingeben, das mindestens 8 Buchstaben lang ist
|
||||
Proccessing;Weid verarbeitet...
|
||||
The FirstName field is required.;Das Vorname-Feld ist erforderlich
|
||||
The LastName field is required.;Das Nachname-Feld ist erforderlich
|
||||
The Address field is required.;Das Address-Feld ist erforderlich
|
||||
The City field is required.;Das Stadt-Feld ist erforderlich
|
||||
The State field is required.;Das Staat-Feld ist erforderlich
|
||||
The Country field is required.;Das Land-Feld ist erforderlich
|
||||
Street and house number requered;Straße und Hausnummer erforderlich
|
||||
Max lenght reached;Maximale Länge erreicht
|
||||
Server;Server
|
||||
stopped;gestoppt
|
||||
Cleanups;Cleanups
|
||||
executed;ausgeführt
|
||||
Used clanup;Benutztes Cleanup
|
||||
Used clanup;Cleanup benutzt
|
||||
Enable;Aktivieren
|
||||
Disabble;Deaktivieren
|
||||
Disable;Deaktivieren
|
||||
Addons;Addons
|
||||
Addons;Add-ons
|
||||
Javascript version;Javascript Version
|
||||
Javascript file;Javascript-Datei
|
||||
Select javascript file to execute on start;Javascript-Datei zum Ausführen beim Start auswählen
|
||||
Submit;Absenden
|
||||
Processing;Verarbeite
|
||||
Go up;Nach oben
|
||||
Running cleanup;Laufende Cleanup Server
|
||||
servers;Server
|
||||
Select folder to move the file(s) to;Ordner auswählen, in den die Datei(en) verschoben werden sollen
|
||||
Paper version;Paperversion
|
||||
Javascript file;Javascript Datei
|
||||
Select javascript file to execute on start;Javascript Datei zum starten auswählen
|
||||
Submit;Einreichen
|
||||
Processing;Wird verarbeitet...
|
||||
Go up;Nach oben gehen
|
||||
Running cleanup;Cleanup läuft
|
||||
servers;Servers
|
||||
Select folder to move the file(s) to;Ordner zum Bewegen der Dateien auswählen
|
||||
Paper version;Paper Version
|
||||
Join2Start;Join2Start
|
||||
Server reset;Server zurücksetzen
|
||||
Reset;Zurücksetzen
|
||||
Resetting;Setze zurück
|
||||
Are you sure you want to reset this server?;Bist du dir sicher, dass du diesen Server zurücksetzen willst?
|
||||
Resetting;Wird zurückgesetzt
|
||||
Are you sure you want to reset this server?;Möchtest du diesen Server wirklich zurücksetzen?
|
||||
Are you sure? This cannot be undone;Bist du dir sicher? Dies kann nicht rückgängig gemacht werden
|
||||
Resetting server;Setze Server zurück
|
||||
Deleted file;Gelöschte Datei
|
||||
Reinstalling server;Installiere den Server neu
|
||||
Uploading files;Lade Dateien hoch
|
||||
Resetting server;Server wird zurückgesetzt...
|
||||
Deleted file;Datei gelöscht
|
||||
Reinstalling server;Server wird reinstalliert
|
||||
Uploading files;Dateien wurden hochgeladen
|
||||
complete;vollständig
|
||||
Upload complete;Upload vollständig
|
||||
Upload complete;Upload komplett
|
||||
Security;Sicherheit
|
||||
Subscriptions;Abonnements
|
||||
Subscriptions;Abonaments
|
||||
2fa Code;2FA Code
|
||||
Your account is secured with 2fa;Dein Konto ist mit 2FA gesichert
|
||||
anyone write a fancy text here?;Kann hier jemand einen netten Text schreiben? Ja sicher: Ich war hier. - Dannyx
|
||||
Activate 2fa;2FA aktivieren
|
||||
2fa apps;2FA Apps
|
||||
Your account is secured with 2fa;Dein Account wird mit 2-FA gesichert
|
||||
anyone write a fancy text here?;hier einen schönen Text schreiben?
|
||||
Activate 2fa;2-FA Aktivieren
|
||||
2fa apps;2-FA Apps
|
||||
Use an app like ;Benutze eine App wie
|
||||
or;oder
|
||||
and scan the following QR Code;und scanne den folgenden QR-Code
|
||||
If you have trouble using the QR Code, select manual input in the app and enter your email and the following code:;Wenn du Probleme bei der Verwendung des QR-Codes hast, wähle in der App manuelle Eingabe und gib deine E-Mail-Adresse und den folgenden Code ein:
|
||||
Finish activation;Aktivierung beenden
|
||||
2fa Code requiered;2FA-Code erforderlich
|
||||
New password;Neues Passwort
|
||||
Secure your account;Sichere dein Konto
|
||||
2fa adds another layer of security to your account. You have to enter a 6 digit code in order to login.;2FA fügt eine weitere Sicherheitsebene zu deinem Konto hinzu. Du musst dann noch einen 6-stelligen Code eingeben, um dich anzumelden.
|
||||
New subscription;Neues Abonnement
|
||||
and scan the following QR Code;und scanne diesen QR-Code
|
||||
If you have trouble using the QR Code, select manual input in the app and enter your email and the following code:;Wenn du Probleme mit dem Scannen des Qr-Codes has, benutze doch die Manuelle Eingabe der App und gib deine E-Mail und den folgenden Code ein:
|
||||
Finish activation;Aktivierung fertig
|
||||
2fa Code requiered;2-FA Code erforderlich
|
||||
New password;Neues Password
|
||||
Secure your account;Deinen Account sichern
|
||||
2fa adds another layer of security to your account. You have to enter a 6 digit code in order to login.;2-FA fügt eine weitere Sicherheits-Schicht hinzu. Du musst einen 6-Ziffern-Code eingeben, um dich anzumelden.
|
||||
New subscription;Neues Abonament
|
||||
You need to enter a name;Du musst einen Namen eingeben
|
||||
You need to enter a description;Du musst eine Beschreibung eingeben
|
||||
Add new limit;Neues Limit hinzufügen
|
||||
Create subscription;Abonnement erstellen
|
||||
You need to enter a description;Du musst eine Beschreibung eigeben
|
||||
Add new limit;Ein neues Limit hinzufügen
|
||||
Create subscription;Abonament erstellen
|
||||
Options;Optionen
|
||||
Amount;Betrag
|
||||
Do you really want to delete it?;Willst du es wirklich löschen?
|
||||
Loading your subscription;Abonnement laden
|
||||
Searching for deploy node;Suche nach einer Deploy Node
|
||||
Searching for available images;Suche nach verfügbaren Images
|
||||
Server details;Server-Details
|
||||
Configure your server;Konfiguriere deinen Server
|
||||
Default;Standard
|
||||
You reached the maximum amount of servers for every image of your subscription;Du hast die maximale Anzahl von Servern für jedes Image deines Abonnements erreicht
|
||||
Personal information;Persönliche Informationen
|
||||
Do you really want to delete it?;Möchtes du es wirklich löschen?
|
||||
Loading your subscription;Dein Abonament wird geladen
|
||||
Searching for deploy node;#Empty#
|
||||
Searching for available images;Nach verfügbaren Images wird gesucht
|
||||
Server details;Server Details
|
||||
Configure your server;Deinen Server konfigurieren
|
||||
Default;Standart
|
||||
You reached the maximum amount of servers for every image of your subscription;Du hast die maximale Anzahl an Images von deinem Abonament erreicht.
|
||||
Personal information;Prsönliche Informationen
|
||||
Enter code;Code eingeben
|
||||
Server rename;Server umbenennen
|
||||
Server rename;Server Umbenennen
|
||||
Create code;Code erstellen
|
||||
Save subscription;Abonnement speichern
|
||||
Enter your information;Gib deine Informationen ein
|
||||
You need to enter your full name in order to use moonlight;Du musst deinen vollständigen Namen eingeben, um Moonlight zu verwenden
|
||||
No node found;Keine Node gefunden
|
||||
No node found to deploy to found;Kein Node zum Deployen gefunden
|
||||
Save subscription;Abonament speichern
|
||||
Enter your information;Informationen eingeben
|
||||
You need to enter your full name in order to use moonlight;Du musst deinen ganzen Namen eingeben, um Moonlight zu nutzen
|
||||
No node found;Kein Node gefunden
|
||||
No node found to deploy to found;#Empty#
|
||||
Node offline;Node offline
|
||||
The node the server is running on is currently offline;Die Node, auf der der Server läuft, ist derzeit offline
|
||||
Server not found;Server nicht gefunden
|
||||
A server with that id cannot be found or you have no access for this server;Ein Server mit dieser ID kann nicht gefunden werden oder du hast keinen Zugriff auf diesen Server
|
||||
The node the server is running on is currently offline;Das Node, auf dem der Server grat läuft, ist offline
|
||||
Server not found;Server konnte nicht gefunden werden
|
||||
A server with that id cannot be found or you have no access for this server;Ein Server mit dieser ID konnte nicht gefunden werden
|
||||
Compress;Komprimieren
|
||||
Decompress;Dekomprimieren
|
||||
Moving;Verschieben
|
||||
Compressing;Komprimieren
|
||||
selected;ausgewählt
|
||||
Decompress;De-Komprimieren
|
||||
Moving;Bewegen...
|
||||
Compressing;Komprimieren...
|
||||
selected;Ausgewählt
|
||||
New website;Neue Website
|
||||
Plesk servers;Plesk-Server
|
||||
Base domain;Basisdomain
|
||||
Plesk server;Plesk-Server
|
||||
Plesk servers;Plesk Servers
|
||||
Base domain;Base Domain
|
||||
Plesk server;Plesk Server
|
||||
Ftp;FTP
|
||||
No SSL certificate found;Kein SSL-Zertifikat gefunden
|
||||
No SSL certificate found;Keine SSL-Zertifikate gefunden
|
||||
Ftp Host;FTP Host
|
||||
Ftp Port;FTP Port
|
||||
Ftp Username;FTP Benutzername
|
||||
Ftp Password;FTP Passwort
|
||||
Use;Verwenden
|
||||
SSL Certificates;SSL-Zertifikate
|
||||
SSL certificates;SSL-Zertifikate
|
||||
Issue certificate;Zertifikat ausstellen lassen
|
||||
New plesk server;Neuer Plesk-Server
|
||||
Api url;API-URL
|
||||
Host system offline;Hostsystem offline
|
||||
The host system the website is running on is currently offline;Das Hostsystem, auf dem die Website läuft, ist derzeit offline
|
||||
Ftp Username;FTP Username
|
||||
Ftp Password;FTP Password
|
||||
Use;Benutzen
|
||||
SSL Certificates;SSL Zertifikate
|
||||
SSL certificates;SSL Zertifikate
|
||||
Issue certificate;SSL-Zertifikat Ausgeben
|
||||
New plesk server;Neuer Plesk Server
|
||||
Api url;API URL
|
||||
Host system offline;Host System Offline
|
||||
The host system the website is running on is currently offline;Das Host System, auf dem diese Website läuft, ist offline
|
||||
No SSL certificates found;Keine SSL-Zertifikate gefunden
|
||||
No databases found for this website;Keine Datenbanken für diese Website gefunden
|
||||
The name should be at least 8 characters long;Der Name muss mindestens 8 Zeichen lang sein
|
||||
The name should only contain of lower case characters and numbers;Der Name darf nur aus Kleinbuchstaben und Zahlen bestehen
|
||||
Error from plesk;Fehler von Plesk
|
||||
No databases found for this website;Dieser Website konnten keine Datenbanken zugeordnet werden
|
||||
The name should be at least 8 characters long;Der Name sollte mindestens 8 Zeichen lang sein
|
||||
The name should only contain of lower case characters and numbers;Der Name sollte nur Kleinbuchstaben und Zahlen enthalten
|
||||
Error from plesk;Error von Plesk
|
||||
Host;Host
|
||||
Username;Benutzername
|
||||
SRV records cannot be updated thanks to the cloudflare api client. Please delete the record and create a new one;SRV-Einträge können aufgrund des Cloudflare-Api-Clients nicht aktualisiert werden. Bitte lösche den Eintrag und erstelle einen neuen
|
||||
The User field is required.;Das Feld Benutzer ist erforderlich
|
||||
You need to specify a owner;Du musst einen Besitzer angeben
|
||||
You need to specify a image;Du musst ein Image angeben
|
||||
SRV records cannot be updated thanks to the cloudflare api client. Please delete the record and create a new one;SRV Records können aufgrund von Cloudflare nicht geupdatet werden. Bitte lösche den Record und erstelle einen neuen.
|
||||
The User field is required.;Das Benutzer-Feld ist erforderlich
|
||||
You need to specify a owner;Du musst einen Server-Besitzer angeben
|
||||
You need to specify a image;You need to specify a image
|
||||
Api Url;API URL
|
||||
Api Key;API Key
|
||||
Api Key;Api Key
|
||||
Duration;Dauer
|
||||
Enter duration of subscription;Dauer des Abonnements eingeben
|
||||
Enter duration of subscription;Dauer des Abonaments eingeben
|
||||
Copied code to clipboard;Code in die Zwischenablage kopiert
|
||||
Invalid or expired subscription code;Ungültiger oder abgelaufener Abo-Code
|
||||
Current subscription;Aktuelles Abonnement
|
||||
You need to specify a server image;Du musst ein Server-Image angeben
|
||||
Invalid or expired subscription code;Ungültiger oder Abgelaufener Abo-Code
|
||||
Current subscription;Dein Abonament
|
||||
You need to specify a server image;Du musst ein Image angeben
|
||||
CPU;CPU
|
||||
Hour;Stunde
|
||||
Day;Tag
|
||||
Month;Monat
|
||||
Year;Jahr
|
||||
All time;Seit Beginn
|
||||
This function is not implemented;Diese Funktion ist nicht eingebaut
|
||||
Domain details;Domaindetails
|
||||
Configure your domain;Konfiguriere deine Domain
|
||||
You reached the maximum amount of domains in your subscription;Du hast die maximale Anzahl an Domains in deinem Abonnement erreicht
|
||||
You need to specify a shared domain;Du musst eine gemeinsame Domain angeben
|
||||
A domain with this name does already exist for this shared domain;Eine Domain mit diesem Namen existiert bereits für diese gemeinsame Domain
|
||||
The Email field is required.;Das Feld E-Mail ist erforderlich.
|
||||
The Password field is required.;Das Feld Passwort ist erforderlich.
|
||||
The ConfirmPassword field is required.;Das Feld Passwort bestätigen ist erforderlich.
|
||||
Passwords need to match;Passwörter müssen übereinstimmen
|
||||
Cleanup exception;Cleanup Ausnahme
|
||||
No shared domain found;Keine gemeinsame Domain gefunden
|
||||
Searching for deploy plesk server;Suche nach einem Deploy Plesk Server
|
||||
All time;Für immer
|
||||
This function is not implemented;Diese Funktion wurde noch nicht hinzugefügt
|
||||
Domain details;Domain Details
|
||||
Configure your domain;Deine Domain konfigurieren
|
||||
You reached the maximum amount of domains in your subscription;Du hast das Maximum an Domains in deinem Abonament erreicht
|
||||
You need to specify a shared domain;Du musst eine Shared-Domain angeben
|
||||
A domain with this name does already exist for this shared domain;Eine Domain mit diesem Name existiert bereits in dieser Shared-Domain
|
||||
The Email field is required.;Das E-Mail-Feld ist erforderlich
|
||||
The Password field is required.;Das Password-Feld ist erforderlich
|
||||
The ConfirmPassword field is required.;Das Password-Bestätigen-Feld ist erforderlich
|
||||
Passwords need to match;Die Passwörter müssen übereinstimmen
|
||||
Cleanup exception;Cleanup ausnahme
|
||||
No shared domain found;Keine Shared-Domain gefunden
|
||||
Searching for deploy plesk server;Suchen um den Plesk Server aufzusetzen
|
||||
No plesk server found;Kein Plesk Server gefunden
|
||||
No plesk server found to deploy to;Kein Plesk Server für die Bereitstellung gefunden
|
||||
No node found to deploy to;Keine Node für die Bereitstellung gefunden
|
||||
Website details;Webseiten-Details
|
||||
No plesk server found to deploy to;Keinen Plesk Server zum Aufsetzen gefunden
|
||||
No node found to deploy to;Kein Node zum Aufsetzen
|
||||
Website details;Website Details
|
||||
Configure your website;Konfiguriere deine Website
|
||||
The name cannot be longer that 32 characters;Der Name darf nicht länger als 32 Zeichen sein
|
||||
The name should only consist of lower case characters;Der Name darf nur aus Kleinbuchstaben bestehen
|
||||
The name cannot be longer that 32 characters;Der Name kann nicht länger als 32 Zeichen sein
|
||||
The name should only consist of lower case characters;Der Name sollte nur aus Kleinbuchstaben bestehen
|
||||
News;Neuigkeiten
|
||||
Title...;Titel...
|
||||
Enter text...;Text eingeben...
|
||||
Saving...;Speichere...
|
||||
Deleting...;Lösche...
|
||||
Delete post;Beitrag löschen
|
||||
Do you really want to delete the post ";Willst du den Beitrag wirklich löschen "
|
||||
Enter text...;Text einfügen...
|
||||
Saving...;Wird gespeichert...
|
||||
Deleting...;Wird gelöscht...
|
||||
Delete post;Post löschen
|
||||
Do you really want to delete the post ";Post löschen? "
|
||||
You have no domains;Du hast keine Domains
|
||||
We were not able to find any domains associated with your account;Wir konnten keine Domains finden, die mit deinem Konto verbunden sind
|
||||
We were not able to find any domains associated with your account;Wir haben keine Domains, die mit deinem Account verbunden sind, gefunden
|
||||
You have no websites;Du hast keine Websites
|
||||
We were not able to find any websites associated with your account;Wir konnten keine Webseiten finden, die mit deinem Konto verbunden sind
|
||||
We were not able to find any websites associated with your account;Wir haben keine Websiten, die mit deinem Account verbunden sind, gefunden
|
||||
Guest;Gast
|
||||
You need a domain;Du brauchst eine Domain
|
||||
You need a domain;Du brauchts eine Domain
|
||||
New post;Neuer Post
|
||||
New entry;Neuer Eintrag
|
||||
You have no servers;Du hast keine Server
|
||||
We were not able to find any servers associated with your account;Wir haben keine Server, die mit deinem Account verbunden sind, gefunden
|
||||
Error creating server on wings;Fehler bei der Erstellung des Servers auf Wings
|
||||
An unknown error occured while restoring a backup;Ein unbekannter Fehler ist während der Backup-Wiederherstellung aufgetreten
|
||||
Error from daemon;Fehler vom Daemon
|
||||
End;Ende
|
||||
Cloud panel;Cloud Panel
|
||||
Cloud panels;Cloud Panels
|
||||
New cloud panel;Neues cloud Panel
|
||||
You need to enter an api key;Du musst einen API-Key eigeben
|
||||
Webspaces;Webspaces
|
||||
New webspace;Neuer Webspace
|
||||
The uploaded file should not be bigger than 100MB;DIe Datei sollte nicht größer als 100MB sein
|
||||
An unknown error occured while uploading a file;Ein unbekannter Fehler ist während dem Datei-Hochladen aufgetreten
|
||||
No databases found for this webspace;Keine Datenbanken für diesen Webspace gefunden
|
||||
Sftp;SFTP
|
||||
Sftp Host;Sftp Host
|
||||
Sftp Port;Sftp Port
|
||||
Sftp Username;Sftp Benutzername
|
||||
Sftp Password;Sftp Password
|
||||
Lets Encrypt certificate successfully issued;Lets Encrypt Zertifikat erfolgreich erstellt
|
||||
Add shared domain;Shared Domain Hinzufügen
|
||||
Webspace;Webspace
|
||||
You reached the maximum amount of websites in your subscription;Du hast das Maximum an Websiten in deinem Abonament erreicht
|
||||
Searching for deploy web host;Suchen um den Webhost aufzusetzen
|
||||
Webspace details;Webspace Details
|
||||
Web host;Web host
|
||||
Configure your webspaces;Konfiguriere deine Webspaces
|
||||
You reached the maximum amount of webspaces in your subscription;Du hast das Maximum an Webspaces in deinem Abonament erreicht
|
||||
Create a webspace;Einen Webspace erstellen
|
||||
Manage your webspaces;Deine Webspaces verwalten
|
||||
Modify the content of your webspaces;Den Inhalt deiner Webspaces verwalten
|
||||
Successfully updated password;Password erfolgreich geupdatet
|
||||
An unknown error occured while sending your message;Ein unbekannter Fehler ist während dem Senden von deiner Nachricht aufgetreten
|
||||
Open chats;Offene Chats
|
||||
No message sent yet;Keine Nachrichten gesendet
|
||||
Support ticket open;Support-Ticket geöffnet
|
||||
Support ticket closed;Support-Ticket geschlossen
|
||||
Your connection has been paused;Deine Verbindung wurde pausiert
|
||||
We paused your connection because of inactivity. The resume just focus the tab and wait a few seconds;Wir haben deine Verbindung aufgrund von inaktivität pausiert. Wechsle auf den Tab und warte ein paar Sekunden
|
||||
Failed to reconnect to the moonlight servers;Die Wiederverbindung zu den Moonlight-Servern ist gescheitert
|
||||
We were unable to reconnect to moonlight. Please refresh the page;Verbindung zu Moonlight fehlgeschlagen. Bitte aktualisiere die Seite
|
||||
Failed to reconnect to the moonlight servers. The connection has been rejected;Die Wiederverbindung zu den Moonlight-Servern ist fehlgeschlagen. Die Verbindung wurde abgelehnt
|
||||
We were unable to reconnect to moonlight. Most of the time this is caused by an update of moonlight. Please refresh the page;Verbindung zu Moonlight fehlgeschlagen. Meistens wird dies durch eine Aktualisierung von Moonlight verursacht. Bitte aktualisieren Sie die Seite
|
||||
Verifying token, loading user data;Token verifizieren, Benutzer-Daten laden
|
||||
Reload config;Konfiguration neu laden
|
||||
Successfully reloading configuration;Konfiguration wird neu geladen...
|
||||
Successfully reloaded configuration;Die Konfiguration wurde erfolgreich neu geladen
|
||||
Flows;Flows
|
||||
Add node;Node Hinzufügen
|
||||
Web system;Web System
|
||||
Servers with this image;Server mit diesem Image
|
||||
You need to specify a user;Du musst einen Benutzer angeben
|
||||
Import;Importieren
|
||||
Export;Exportieren
|
||||
Exporting;Wird exportiert
|
||||
Successfully imported image;Das Image wurde erfolgreich importiert.
|
||||
Forge version;Forge Version
|
||||
Fabric version;Fabric Version
|
||||
Fabric loader version;Fabric Loader Version
|
||||
Rate;Rate
|
||||
Hey, can i borrow you for a second?;Hey, kann ich dich mal kurz ausleihen?
|
||||
We want to improve our services and get a little bit of feedback how we are currently doing. Please leave us a rating;Da wir unsere Dienste ständig verbessern, möchten wir dich um Feedback bitten. Bitte Bewerte uns:
|
||||
Thanks for your rating;Danke für deine Bewertun
|
||||
It would be really kind of you rating us on a external platform as it will help our project very much;Es wäre wirklich nett, wenn du uns auf einer externen Plattform bewerten würdest, denn das würde unserem Projekt sehr helfen
|
||||
Close;Schließen
|
||||
Rating saved;Bewretung gespeichert
|
||||
Group;Gruppe
|
||||
Beta;Beta
|
||||
Create a new group;Eine neue Gruppe erstellen
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
filter: none;
|
||||
}
|
||||
|
||||
.blur {
|
||||
filter: blur(5px);
|
||||
}
|
||||
|
||||
div.wave {
|
||||
}
|
||||
div.wave .dot {
|
||||
|
||||
@@ -355,8 +355,6 @@
|
||||
{
|
||||
// filter here what key events should be sent to moonlight
|
||||
|
||||
console.log(event);
|
||||
|
||||
if(event.code === "KeyS" && event.ctrlKey)
|
||||
{
|
||||
event.preventDefault();
|
||||
@@ -370,5 +368,54 @@
|
||||
{
|
||||
window.removeEventListener('keydown', moonlight.keyListener.listener);
|
||||
}
|
||||
},
|
||||
serverList: {
|
||||
init: function ()
|
||||
{
|
||||
if(moonlight.serverList.Swappable)
|
||||
{
|
||||
moonlight.serverList.Swappable.destroy();
|
||||
}
|
||||
|
||||
let containers = document.querySelectorAll(".draggable-zone");
|
||||
|
||||
if (containers.length !== 0)
|
||||
{
|
||||
moonlight.serverList.Swappable = new Draggable.Sortable(containers, {
|
||||
draggable: ".draggable",
|
||||
handle: ".draggable .draggable-handle",
|
||||
mirror: {
|
||||
//appendTo: selector,
|
||||
appendTo: "body",
|
||||
constrainDimensions: true
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
getData: function ()
|
||||
{
|
||||
let groups = new Array();
|
||||
|
||||
let groupElements = document.querySelectorAll('[ml-server-group]');
|
||||
|
||||
groupElements.forEach(groupElement => {
|
||||
let group = new Object();
|
||||
group.name = groupElement.attributes.getNamedItem("ml-server-group").value;
|
||||
|
||||
let servers = new Array();
|
||||
let serverElements = groupElement.querySelectorAll("[ml-server-id]");
|
||||
|
||||
serverElements.forEach(serverElement => {
|
||||
let id = serverElement.attributes.getNamedItem("ml-server-id").value;
|
||||
|
||||
servers.push(id);
|
||||
});
|
||||
|
||||
group.servers = servers;
|
||||
groups.push(group);
|
||||
});
|
||||
|
||||
return groups;
|
||||
}
|
||||
}
|
||||
};
|
||||
32
README.md
32
README.md
@@ -67,6 +67,10 @@ Moonlight:
|
||||
Daemon (not wings):
|
||||
`curl https://install.moonlightpanel.xyz/daemon| bash`
|
||||
|
||||
Having any issues?
|
||||
We are happy to help on our discord server:
|
||||
[https://discord.gg/TJaspT7A8p](https://discord.gg/TJaspT7A8p)
|
||||
|
||||
## Roadmap
|
||||
|
||||
The roudmap can be found here:
|
||||
@@ -96,3 +100,31 @@ Distributed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0
|
||||
* **Daniel Balk** - *Endelon Hosting* - [Daniel Balk](https://github.com/Daniel-Balk) - *Notification system & frontend*
|
||||
* **Spielepapagei** - *Endelon Hosting* - [Spielepapagei](https://github.com/Spielepapagei) - *Discord Bot & support tickets*
|
||||
* **Dannyx** - *None* - [Dannyx](https://github.com/Dannyx1604) - *Grammer check and translations*
|
||||
|
||||
## Some screenshots
|
||||
Only user area
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Reference in New Issue
Block a user