20 Commits
v1b10 ... v1b12

Author SHA1 Message Date
Marcel Baumgartner
2fe17473ae Merge pull request #204 from Moonlight-Panel/SwitchToNewConfigSystem
Switched to new config system
2023-07-02 02:19:32 +02:00
Marcel Baumgartner
609cf8cfac Switched to new config system 2023-07-02 02:16:44 +02:00
Marcel Baumgartner
678da30b09 Merge pull request #203 from Moonlight-Panel/LogsAndFixes
Added new log files and log migrators. Fixed some errors with js invokes
2023-07-02 00:21:55 +02:00
Marcel Baumgartner
d19412f4bb Added new log files and log migrators. Fixed some errors with js invokes 2023-07-02 00:21:35 +02:00
Marcel Baumgartner
1665d6e537 Merge pull request #202 from Moonlight-Panel/AddSentrySupport
Added new sentry support
2023-07-01 19:01:00 +02:00
Marcel Baumgartner
fd210f2404 Added new sentry support 2023-07-01 19:00:38 +02:00
Marcel Baumgartner
c33729fb44 Merge pull request #201 from Moonlight-Panel/FixServerList
Fixed server list
2023-06-30 21:55:58 +02:00
Marcel Baumgartner
7983bf3ee4 Fixed server list 2023-06-30 21:55:32 +02:00
Marcel Baumgartner
a09f60aea7 Merge pull request #200 from Moonlight-Panel/HttpTimeoutFixes
Fixed timeout options from assumed seconds to real value miliseconds
2023-06-29 23:28:07 +02:00
Marcel Baumgartner
28b5893c21 Fixed timeout options from assumed seconds to real value miliseconds 2023-06-29 23:27:48 +02:00
Marcel Baumgartner
25b47d8b6c Merge pull request #199 from Moonlight-Panel/AddNewGermanTranslation
Added new german translations
2023-06-29 23:10:21 +02:00
Marcel Baumgartner
85f5b8a7da Added new german translations 2023-06-29 23:09:58 +02:00
Marcel Baumgartner
ab9333f99a Merge pull request #198 from Moonlight-Panel/FixServerDelete
Fixed server delete
2023-06-26 22:51:17 +02:00
Marcel Baumgartner
d60f8fc905 Fixed server delete 2023-06-26 22:50:45 +02:00
Marcel Baumgartner
fe1f4412d8 Merge pull request #197 from Moonlight-Panel/AddedRetry
Added retry class and added it to some api calls that need it
2023-06-26 22:45:56 +02:00
Marcel Baumgartner
f191533410 Added retry class and added it to some api calls that need it 2023-06-26 22:45:33 +02:00
Marcel Baumgartner
a894707536 Merge pull request #196 from Moonlight-Panel/SmallBugFixes
Small bug fixes
2023-06-26 18:05:58 +02:00
Marcel Baumgartner
d2ccc84286 Fixed sidebar text sizing issues 2023-06-26 17:23:25 +02:00
Marcel Baumgartner
f2ec43f2d2 Fixed not enabled installing screen for new servers 2023-06-26 17:19:54 +02:00
Marcel Baumgartner
7feccc8d9f Fixed error loop for fileaccess providers not supporting the launch url 2023-06-26 17:19:32 +02:00
54 changed files with 1416 additions and 1171 deletions

View File

@@ -47,7 +47,7 @@ public class ModrinthApiHelper
var request = new RestRequest(url) var request = new RestRequest(url)
{ {
Timeout = 60 * 15 Timeout = 300000
}; };
request.AddHeader("Content-Type", "application/json"); request.AddHeader("Content-Type", "application/json");

View File

@@ -209,7 +209,7 @@ public class WingsApiHelper
var request = new RestRequest(url) var request = new RestRequest(url)
{ {
Timeout = 60 * 15 Timeout = 300000
}; };
request.AddHeader("Content-Type", "application/json"); request.AddHeader("Content-Type", "application/json");

View File

@@ -0,0 +1,226 @@
namespace Moonlight.App.Configuration;
using System;
using Newtonsoft.Json;
public class ConfigV1
{
[JsonProperty("Moonlight")] public MoonlightData Moonlight { get; set; } = new();
public class MoonlightData
{
[JsonProperty("AppUrl")] public string AppUrl { get; set; } = "http://your-moonlight-url-without-slash";
[JsonProperty("Database")] public DatabaseData Database { get; set; } = new();
[JsonProperty("DiscordBotApi")] public DiscordBotData DiscordBotApi { get; set; } = new();
[JsonProperty("DiscordBot")] public DiscordBotData DiscordBot { get; set; } = new();
[JsonProperty("Domains")] public DomainsData Domains { get; set; } = new();
[JsonProperty("Html")] public HtmlData Html { get; set; } = new();
[JsonProperty("Marketing")] public MarketingData Marketing { get; set; } = new();
[JsonProperty("OAuth2")] public OAuth2Data OAuth2 { get; set; } = new();
[JsonProperty("Security")] public SecurityData Security { get; set; } = new();
[JsonProperty("Mail")] public MailData Mail { get; set; } = new();
[JsonProperty("Cleanup")] public CleanupData Cleanup { get; set; } = new();
[JsonProperty("Subscriptions")] public SubscriptionsData Subscriptions { get; set; } = new();
[JsonProperty("DiscordNotifications")]
public DiscordNotificationsData DiscordNotifications { get; set; } = new();
[JsonProperty("Statistics")] public StatisticsData Statistics { get; set; } = new();
[JsonProperty("Rating")] public RatingData Rating { get; set; } = new();
[JsonProperty("SmartDeploy")] public SmartDeployData SmartDeploy { get; set; } = new();
[JsonProperty("Sentry")] public SentryData Sentry { get; set; } = new();
}
public class CleanupData
{
[JsonProperty("Cpu")] public long Cpu { get; set; } = 90;
[JsonProperty("Memory")] public long Memory { get; set; } = 8192;
[JsonProperty("Wait")] public long Wait { get; set; } = 15;
[JsonProperty("Uptime")] public long Uptime { get; set; } = 6;
[JsonProperty("Enable")] public bool Enable { get; set; } = false;
[JsonProperty("MinUptime")] public long MinUptime { get; set; } = 10;
}
public class DatabaseData
{
[JsonProperty("Database")] public string Database { get; set; } = "moonlight_db";
[JsonProperty("Host")] public string Host { get; set; } = "your.database.host";
[JsonProperty("Password")] public string Password { get; set; } = "secret";
[JsonProperty("Port")] public long Port { get; set; } = 3306;
[JsonProperty("Username")] public string Username { get; set; } = "moonlight_user";
}
public class DiscordBotData
{
[JsonProperty("Enable")] public bool Enable { get; set; } = false;
[JsonProperty("Token")] public string Token { get; set; } = "discord token here";
[JsonProperty("PowerActions")] public bool PowerActions { get; set; } = false;
[JsonProperty("SendCommands")] public bool SendCommands { get; set; } = false;
}
public class DiscordNotificationsData
{
[JsonProperty("Enable")] public bool Enable { get; set; } = false;
[JsonProperty("WebHook")] public string WebHook { get; set; } = "http://your-discord-webhook-url";
}
public class DomainsData
{
[JsonProperty("Enable")] public bool Enable { get; set; } = false;
[JsonProperty("AccountId")] public string AccountId { get; set; } = "cloudflare acc id";
[JsonProperty("Email")] public string Email { get; set; } = "cloudflare@acc.email";
[JsonProperty("Key")] public string Key { get; set; } = "secret";
}
public class HtmlData
{
[JsonProperty("Headers")] public HeadersData Headers { get; set; } = new();
}
public class HeadersData
{
[JsonProperty("Color")] public string Color { get; set; } = "#4b27e8";
[JsonProperty("Description")] public string Description { get; set; } = "the next generation hosting panel";
[JsonProperty("Keywords")] public string Keywords { get; set; } = "moonlight";
[JsonProperty("Title")] public string Title { get; set; } = "Moonlight - endelon.link";
}
public class MailData
{
[JsonProperty("Email")] public string Email { get; set; } = "username@your.mail.host";
[JsonProperty("Server")] public string Server { get; set; } = "your.mail.host";
[JsonProperty("Password")] public string Password { get; set; } = "secret";
[JsonProperty("Port")] public int Port { get; set; } = 465;
[JsonProperty("Ssl")] public bool Ssl { get; set; } = true;
}
public class MarketingData
{
[JsonProperty("BrandName")] public string BrandName { get; set; } = "Endelon Hosting";
[JsonProperty("Imprint")] public string Imprint { get; set; } = "https://your-site.xyz/imprint";
[JsonProperty("Privacy")] public string Privacy { get; set; } = "https://your-site.xyz/privacy";
[JsonProperty("About")] public string About { get; set; } = "https://your-site.xyz/about";
[JsonProperty("Website")] public string Website { get; set; } = "https://your-site.xyz";
}
public class OAuth2Data
{
[JsonProperty("OverrideUrl")] public string OverrideUrl { get; set; } = "https://only-for-development.cases";
[JsonProperty("EnableOverrideUrl")] public bool EnableOverrideUrl { get; set; } = false;
[JsonProperty("Providers")]
public OAuth2ProviderData[] Providers { get; set; } = Array.Empty<OAuth2ProviderData>();
}
public class OAuth2ProviderData
{
[JsonProperty("Id")] public string Id { get; set; }
[JsonProperty("ClientId")] public string ClientId { get; set; }
[JsonProperty("ClientSecret")] public string ClientSecret { get; set; }
}
public class RatingData
{
[JsonProperty("Enabled")] public bool Enabled { get; set; } = false;
[JsonProperty("Url")] public string Url { get; set; } = "https://link-to-google-or-smth";
[JsonProperty("MinRating")] public int MinRating { get; set; } = 4;
[JsonProperty("DaysSince")] public int DaysSince { get; set; } = 5;
}
public class SecurityData
{
[JsonProperty("Token")] public string Token { get; set; } = Guid.NewGuid().ToString();
[JsonProperty("ReCaptcha")] public ReCaptchaData ReCaptcha { get; set; }
}
public class ReCaptchaData
{
[JsonProperty("Enable")] public bool Enable { get; set; } = false;
[JsonProperty("SiteKey")] public string SiteKey { get; set; } = "recaptcha site key here";
[JsonProperty("SecretKey")] public string SecretKey { get; set; } = "recaptcha secret here";
}
public class SentryData
{
[JsonProperty("Enable")] public bool Enable { get; set; } = false;
[JsonProperty("Dsn")] public string Dsn { get; set; } = "http://your-sentry-url-here";
}
public class SmartDeployData
{
[JsonProperty("Server")] public SmartDeployServerData Server { get; set; } = new();
}
public class SmartDeployServerData
{
[JsonProperty("EnableOverride")] public bool EnableOverride { get; set; } = false;
[JsonProperty("OverrideNode")] public long OverrideNode { get; set; } = 1;
}
public class StatisticsData
{
[JsonProperty("Enabled")] public bool Enabled { get; set; } = false;
[JsonProperty("Wait")] public long Wait { get; set; } = 15;
}
public class SubscriptionsData
{
[JsonProperty("SellPass")] public SellPassData SellPass { get; set; } = new();
}
public class SellPassData
{
[JsonProperty("Enable")] public bool Enable { get; set; } = false;
[JsonProperty("Url")] public string Url { get; set; } = "https://not-implemented-yet";
}
}

View File

@@ -52,14 +52,14 @@ public class DataContext : DbContext
if (!optionsBuilder.IsConfigured) if (!optionsBuilder.IsConfigured)
{ {
var config = ConfigService var config = ConfigService
.GetSection("Moonlight") .Get()
.GetSection("Database"); .Moonlight.Database;
var connectionString = $"host={config.GetValue<string>("Host")};" + var connectionString = $"host={config.Host};" +
$"port={config.GetValue<int>("Port")};" + $"port={config.Port};" +
$"database={config.GetValue<string>("Database")};" + $"database={config.Database};" +
$"uid={config.GetValue<string>("Username")};" + $"uid={config.Username};" +
$"pwd={config.GetValue<string>("Password")}"; $"pwd={config.Password}";
optionsBuilder.UseMySql( optionsBuilder.UseMySql(
connectionString, connectionString,

View File

@@ -0,0 +1,30 @@
using Microsoft.JSInterop;
namespace Moonlight.App.Extensions;
public static class JSRuntimeExtensions
{
public static async Task InvokeVoidSafeAsync(this IJSRuntime jsRuntime, string method, params object[] args)
{
try
{
await jsRuntime.InvokeVoidAsync(method, args);
}
catch (Exception)
{
// ignored
}
}
public static void InvokeVoidSafe(this IJSRuntime jsRuntime, string method, params object[] args)
{
try
{
jsRuntime.InvokeVoidAsync(method, args);
}
catch (Exception)
{
// ignored
}
}
}

View File

@@ -66,15 +66,13 @@ public class DatabaseCheckupService
var configService = new ConfigService(new StorageService()); var configService = new ConfigService(new StorageService());
var dateTimeService = new DateTimeService(); var dateTimeService = new DateTimeService();
var config = configService var config = configService.Get().Moonlight.Database;
.GetSection("Moonlight")
.GetSection("Database");
var connectionString = $"host={config.GetValue<string>("Host")};" + var connectionString = $"host={config.Host};" +
$"port={config.GetValue<int>("Port")};" + $"port={config.Port};" +
$"database={config.GetValue<string>("Database")};" + $"database={config.Database};" +
$"uid={config.GetValue<string>("Username")};" + $"uid={config.Username};" +
$"pwd={config.GetValue<string>("Password")}"; $"pwd={config.Password}";
string file = PathBuilder.File("storage", "backups", $"{dateTimeService.GetCurrentUnix()}-mysql.sql"); string file = PathBuilder.File("storage", "backups", $"{dateTimeService.GetCurrentUnix()}-mysql.sql");

View File

@@ -111,7 +111,7 @@ public class WingsFileAccess : FileAccess
request.AddParameter("name", "files"); request.AddParameter("name", "files");
request.AddParameter("filename", name); request.AddParameter("filename", name);
request.AddHeader("Content-Type", "multipart/form-data"); request.AddHeader("Content-Type", "multipart/form-data");
request.AddHeader("Origin", ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl")); request.AddHeader("Origin", ConfigService.Get().Moonlight.AppUrl);
request.AddFile("files", () => request.AddFile("files", () =>
{ {
return new StreamProgressHelper(dataStream) return new StreamProgressHelper(dataStream)

View 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);
}
}

View File

@@ -20,7 +20,7 @@ public class WingsConsoleHelper
{ {
ServerRepository = serverRepository; ServerRepository = serverRepository;
AppUrl = configService.GetSection("Moonlight").GetValue<string>("AppUrl"); AppUrl = configService.Get().Moonlight.AppUrl;
} }
public async Task ConnectWings(WingsConsole console, Server server) public async Task ConnectWings(WingsConsole console, Server server)

View File

@@ -15,7 +15,7 @@ public class WingsJwtHelper
{ {
ConfigService = configService; ConfigService = configService;
AppUrl = ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl"); AppUrl = ConfigService.Get().Moonlight.AppUrl;
} }
public string Generate(string secret, Action<Dictionary<string, string>> claimsAction) public string Generate(string secret, Action<Dictionary<string, string>> claimsAction)

View File

@@ -30,14 +30,14 @@ public class DiscordBotController : Controller
ServerService = serverService; ServerService = serverService;
var config = configService var config = configService
.GetSection("Moonlight") .Get()
.GetSection("DiscordBotApi"); .Moonlight.DiscordBotApi;
Enable = config.GetValue<bool>("Enable"); Enable = config.Enable;
if (Enable) if (Enable)
{ {
Token = config.GetValue<string>("Token"); Token = config.Token;
} }
} }

View File

@@ -27,19 +27,39 @@ public class LogMigrator : ILogger
switch (logLevel) switch (logLevel)
{ {
case LogLevel.Critical: case LogLevel.Critical:
Logger.Fatal($"[{Name}] {formatter(state, exception)}"); Logger.Fatal(formatter(state, exception));
if(exception != null)
Logger.Fatal(exception);
break; break;
case LogLevel.Warning: case LogLevel.Warning:
Logger.Warn($"[{Name}] {formatter(state, exception)}"); Logger.Warn(formatter(state, exception));
if(exception != null)
Logger.Warn(exception);
break; break;
case LogLevel.Debug: case LogLevel.Debug:
Logger.Debug($"[{Name}] {formatter(state, exception)}"); Logger.Debug(formatter(state, exception));
if(exception != null)
Logger.Debug(exception);
break; break;
case LogLevel.Error: case LogLevel.Error:
Logger.Error($"[{Name}] {formatter(state, exception)}"); Logger.Error(formatter(state, exception));
if(exception != null)
Logger.Error(exception);
break; break;
case LogLevel.Information: case LogLevel.Information:
Logger.Info($"[{Name}] {formatter(state, exception)}"); Logger.Info(formatter(state, exception));
if(exception != null)
Logger.Info(exception);
break; break;
} }
} }

View File

@@ -0,0 +1,71 @@
using Moonlight.App.Helpers;
using Sentry;
using Sentry.Extensibility;
namespace Moonlight.App.LogMigrator;
public class SentryDiagnosticsLogger : IDiagnosticLogger
{
private readonly SentryLevel Level;
public SentryDiagnosticsLogger(SentryLevel level)
{
Level = level;
}
public bool IsEnabled(SentryLevel level)
{
if ((int)level >= (int)Level)
{
return true;
}
return false;
}
public void Log(SentryLevel logLevel, string message, Exception? exception = null, params object?[] args)
{
switch (logLevel)
{
case SentryLevel.Debug:
Logger.Debug(string.Format(message, args));
if(exception != null)
Logger.Debug(exception);
break;
case SentryLevel.Info:
Logger.Info(string.Format(message, args));
if(exception != null)
Logger.Info(exception);
break;
case SentryLevel.Warning:
Logger.Warn(string.Format(message, args));
if(exception != null)
Logger.Warn(exception);
break;
case SentryLevel.Error:
Logger.Error(string.Format(message, args));
if(exception != null)
Logger.Error(exception);
break;
case SentryLevel.Fatal:
Logger.Fatal(string.Format(message, args));
if(exception != null)
Logger.Fatal(exception);
break;
}
}
}

View File

@@ -43,15 +43,15 @@ public class CleanupService
CompletedAt = DateTimeService.GetCurrent(); CompletedAt = DateTimeService.GetCurrent();
IsRunning = false; IsRunning = false;
var config = ConfigService.GetSection("Moonlight").GetSection("Cleanup"); var config = ConfigService.Get().Moonlight.Cleanup;
if (!config.GetValue<bool>("Enable") || ConfigService.DebugMode) if (!config.Enable || ConfigService.DebugMode)
{ {
Logger.Info("Disabling cleanup service"); Logger.Info("Disabling cleanup service");
return; return;
} }
Timer = new(TimeSpan.FromMinutes(config.GetValue<int>("Wait"))); Timer = new(TimeSpan.FromMinutes(config.Wait));
Task.Run(Run); Task.Run(Run);
} }
@@ -63,12 +63,12 @@ public class CleanupService
IsRunning = true; IsRunning = true;
using var scope = ServiceScopeFactory.CreateScope(); using var scope = ServiceScopeFactory.CreateScope();
var config = ConfigService.GetSection("Moonlight").GetSection("Cleanup"); var config = ConfigService.Get().Moonlight.Cleanup;
var maxCpu = config.GetValue<int>("Cpu"); var maxCpu = config.Cpu;
var minMemory = config.GetValue<int>("Memory"); var minMemory = config.Memory;
var maxUptime = config.GetValue<int>("Uptime"); var maxUptime = config.Uptime;
var minUptime = config.GetValue<int>("MinUptime"); var minUptime = config.MinUptime;
var nodeRepository = scope.ServiceProvider.GetRequiredService<NodeRepository>(); var nodeRepository = scope.ServiceProvider.GetRequiredService<NodeRepository>();
var nodeService = scope.ServiceProvider.GetRequiredService<NodeService>(); var nodeService = scope.ServiceProvider.GetRequiredService<NodeService>();

View File

@@ -22,14 +22,14 @@ public class DiscordNotificationService
Event = eventSystem; Event = eventSystem;
ResourceService = resourceService; ResourceService = resourceService;
var config = configService.GetSection("Moonlight").GetSection("DiscordNotifications"); var config = configService.Get().Moonlight.DiscordNotifications;
if (config.GetValue<bool>("Enable")) if (config.Enable)
{ {
Logger.Info("Discord notifications enabled"); Logger.Info("Discord notifications enabled");
Client = new(config.GetValue<string>("WebHook")); Client = new(config.WebHook);
AppUrl = configService.GetSection("Moonlight").GetValue<string>("AppUrl"); AppUrl = configService.Get().Moonlight.AppUrl;
Event.On<User>("supportChat.new", this, OnNewSupportChat); Event.On<User>("supportChat.new", this, OnNewSupportChat);
Event.On<SupportChatMessage>("supportChat.message", this, OnSupportChatMessage); Event.On<SupportChatMessage>("supportChat.message", this, OnSupportChatMessage);

View File

@@ -1,15 +1,14 @@
using System.Text; using Moonlight.App.Configuration;
using Microsoft.Extensions.Primitives;
using Moonlight.App.Helpers; using Moonlight.App.Helpers;
using Moonlight.App.Services.Files; using Moonlight.App.Services.Files;
using Newtonsoft.Json;
namespace Moonlight.App.Services; namespace Moonlight.App.Services;
public class ConfigService : IConfiguration public class ConfigService
{ {
private readonly StorageService StorageService; private readonly StorageService StorageService;
private ConfigV1 Configuration;
private IConfiguration Configuration;
public bool DebugMode { get; private set; } = false; public bool DebugMode { get; private set; } = false;
public bool SqlDebugMode { get; private set; } = false; public bool SqlDebugMode { get; private set; } = false;
@@ -41,33 +40,22 @@ public class ConfigService : IConfiguration
public void Reload() public void Reload()
{ {
Configuration = new ConfigurationBuilder().AddJsonStream( var path = PathBuilder.File("storage", "configs", "config.json");
new MemoryStream(Encoding.ASCII.GetBytes(
File.ReadAllText( if (!File.Exists(path))
PathBuilder.File("storage", "configs", "config.json") {
) File.WriteAllText(path, "{}");
)
)).Build();
} }
public IEnumerable<IConfigurationSection> GetChildren() Configuration = JsonConvert.DeserializeObject<ConfigV1>(
{ File.ReadAllText(path)
return Configuration.GetChildren(); ) ?? new ConfigV1();
File.WriteAllText(path, JsonConvert.SerializeObject(Configuration));
} }
public IChangeToken GetReloadToken() public ConfigV1 Get()
{ {
return Configuration.GetReloadToken(); return Configuration;
}
public IConfigurationSection GetSection(string key)
{
return Configuration.GetSection(key);
}
public string this[string key]
{
get => Configuration[key];
set => Configuration[key] = value;
} }
} }

View File

@@ -29,7 +29,7 @@ public class ServerListCommand : BaseModule
{ {
embed = dcs.EmbedBuilderModule.StandardEmbed("Sorry ;( \n Please first create and/or link a Account to Discord! \n Press the Button to register/log in.", Color.Red, command.User); embed = dcs.EmbedBuilderModule.StandardEmbed("Sorry ;( \n Please first create and/or link a Account to Discord! \n Press the Button to register/log in.", Color.Red, command.User);
components = new ComponentBuilder(); components = new ComponentBuilder();
components.WithButton("Click Here", style: ButtonStyle.Link, url: ConfigService.GetSection("Moonlight").GetValue<String>("AppUrl")); components.WithButton("Click Here", style: ButtonStyle.Link, url: ConfigService.Get().Moonlight.AppUrl);
await command.RespondAsync(embed: embed.Build(), components: components.Build(), ephemeral: true); await command.RespondAsync(embed: embed.Build(), components: components.Build(), ephemeral: true);
return; return;
@@ -57,7 +57,7 @@ public class ServerListCommand : BaseModule
components.WithButton("Panel", components.WithButton("Panel",
emote: Emote.Parse("<a:Earth:1092814004113657927>"), emote: Emote.Parse("<a:Earth:1092814004113657927>"),
style: ButtonStyle.Link, style: ButtonStyle.Link,
url: $"{ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl")}"); url: $"{ConfigService.Get().Moonlight.AppUrl}");
if (servers.Count > 25) if (servers.Count > 25)
{ {

View File

@@ -44,10 +44,10 @@ public DiscordBotService(
ServiceScope = ServiceScopeFactory.CreateScope(); ServiceScope = ServiceScopeFactory.CreateScope();
var discordConfig = ConfigService var discordConfig = ConfigService
.GetSection("Moonlight") .Get()
.GetSection("DiscordBot"); .Moonlight.DiscordBot;
if (!discordConfig.GetValue<bool>("Enable")) if (!discordConfig.Enable)
return; return;
Client.Log += Log; Client.Log += Log;
@@ -67,7 +67,7 @@ public DiscordBotService(
await ActivityStatusModule.UpdateActivityStatusList(); await ActivityStatusModule.UpdateActivityStatusList();
await Client.LoginAsync(TokenType.Bot, discordConfig.GetValue<string>("Token")); await Client.LoginAsync(TokenType.Bot, discordConfig.Token);
await Client.StartAsync(); await Client.StartAsync();
await Task.Delay(-1); await Task.Delay(-1);

View File

@@ -87,8 +87,8 @@ public class EmbedBuilderModule : BaseModule
int[] randomNumbers = new int[] { 1, 3, 8, 11, 20 }; int[] randomNumbers = new int[] { 1, 3, 8, 11, 20 };
if (randomNumbers.Contains(random.Next(1, 24))) if (randomNumbers.Contains(random.Next(1, 24)))
return new EmbedAuthorBuilder().WithName(Client.CurrentUser.Username + " - The Rick version").WithUrl(ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl")).WithIconUrl("https://cdn.discordapp.com/attachments/750696464014901268/1092783310129860618/rick.gif"); return new EmbedAuthorBuilder().WithName(Client.CurrentUser.Username + " - The Rick version").WithUrl(ConfigService.Get().Moonlight.AppUrl).WithIconUrl("https://cdn.discordapp.com/attachments/750696464014901268/1092783310129860618/rick.gif");
return new EmbedAuthorBuilder().WithName(Client.CurrentUser.Username).WithUrl(ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl")).WithIconUrl(Client.CurrentUser.GetAvatarUrl()); return new EmbedAuthorBuilder().WithName(Client.CurrentUser.Username).WithUrl(ConfigService.Get().Moonlight.AppUrl).WithIconUrl(Client.CurrentUser.GetAvatarUrl());
} }
} }

View File

@@ -72,7 +72,7 @@ public class ServerListComponentHandlerModule : BaseModule
// stopping // stopping
// offline // offline
// installing // installing
if (!ConfigService.GetSection("Moonlight").GetSection("DiscordBot").GetValue<bool>("PowerActions") && costomId[1] is "Start" or "Restart" or "Stop" or "Kill" or "Update") if (!ConfigService.Get().Moonlight.DiscordBot.PowerActions && costomId[1] is "Start" or "Restart" or "Stop" or "Kill" or "Update")
{ {
embed = dcs.EmbedBuilderModule.StandardEmbed($"This feature is disabled for Security reasons! \n If you believe this is a error please contact the Administrators from this panel.", Color.Red, component.User); embed = dcs.EmbedBuilderModule.StandardEmbed($"This feature is disabled for Security reasons! \n If you believe this is a error please contact the Administrators from this panel.", Color.Red, component.User);
await component.RespondAsync(embed: embed.Build(), ephemeral: true); await component.RespondAsync(embed: embed.Build(), ephemeral: true);
@@ -80,7 +80,7 @@ public class ServerListComponentHandlerModule : BaseModule
return; return;
} }
if (!ConfigService.GetSection("Moonlight").GetSection("DiscordBot").GetValue<bool>("SendCommands") && costomId[1] is "SendCommand") if (!ConfigService.Get().Moonlight.DiscordBot.SendCommands && costomId[1] is "SendCommand")
{ {
embed = dcs.EmbedBuilderModule.StandardEmbed($"This feature is disabled for Security reasons! \n If you believe this is a error please contact the Administrators from this panel.", Color.Red, component.User); embed = dcs.EmbedBuilderModule.StandardEmbed($"This feature is disabled for Security reasons! \n If you believe this is a error please contact the Administrators from this panel.", Color.Red, component.User);
await component.RespondAsync(embed: embed.Build(), ephemeral: true); await component.RespondAsync(embed: embed.Build(), ephemeral: true);
@@ -302,7 +302,7 @@ public class ServerListComponentHandlerModule : BaseModule
{ {
embed = dcs.EmbedBuilderModule.StandardEmbed("Sorry ;( \n Please first create and/or link a Account to Discord! \n Press the Button to register/log in.", Color.Red, component.User); embed = dcs.EmbedBuilderModule.StandardEmbed("Sorry ;( \n Please first create and/or link a Account to Discord! \n Press the Button to register/log in.", Color.Red, component.User);
components = new ComponentBuilder(); components = new ComponentBuilder();
components.WithButton("Click Here", style: ButtonStyle.Link, url: ConfigService.GetSection("Moonlight").GetValue<String>("AppUrl")); components.WithButton("Click Here", style: ButtonStyle.Link, url: ConfigService.Get().Moonlight.AppUrl);
await component.RespondAsync(embed: embed.Build(), components: components.Build(), ephemeral: true); await component.RespondAsync(embed: embed.Build(), components: components.Build(), ephemeral: true);
return; return;
@@ -332,7 +332,7 @@ public class ServerListComponentHandlerModule : BaseModule
components.WithButton("Panel", components.WithButton("Panel",
emote: Emote.Parse("<a:Earth:1092814004113657927>"), emote: Emote.Parse("<a:Earth:1092814004113657927>"),
style: ButtonStyle.Link, style: ButtonStyle.Link,
url: $"{ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl")}"); url: $"{ConfigService.Get().Moonlight.AppUrl}");
components.WithButton("Previous-page", components.WithButton("Previous-page",
emote: Emote.Parse("<:ArrowLeft:1101547474180649030>"), emote: Emote.Parse("<:ArrowLeft:1101547474180649030>"),
@@ -378,7 +378,7 @@ public class ServerListComponentHandlerModule : BaseModule
var components = new ComponentBuilder(); var components = new ComponentBuilder();
if (ConfigService.GetSection("Moonlight").GetSection("DiscordBot").GetValue<bool>("PowerActions")) if (ConfigService.Get().Moonlight.DiscordBot.PowerActions)
{ {
components.WithButton("Start", style: ButtonStyle.Success, customId: $"Sm.Start.{server.Id}", disabled: false); components.WithButton("Start", style: ButtonStyle.Success, customId: $"Sm.Start.{server.Id}", disabled: false);
components.WithButton("Restart", style: ButtonStyle.Primary, customId: $"Sm.Restart.{server.Id}", disabled: false); components.WithButton("Restart", style: ButtonStyle.Primary, customId: $"Sm.Restart.{server.Id}", disabled: false);
@@ -389,14 +389,14 @@ public class ServerListComponentHandlerModule : BaseModule
components.WithButton("Way2Server", components.WithButton("Way2Server",
emote: Emote.Parse("<a:Earth:1092814004113657927>"), emote: Emote.Parse("<a:Earth:1092814004113657927>"),
style: ButtonStyle.Link, style: ButtonStyle.Link,
url: $"{ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl")}/server/{server.Uuid}"); url: $"{ConfigService.Get().Moonlight.AppUrl}/server/{server.Uuid}");
components.WithButton("Update", components.WithButton("Update",
emote: Emote.Parse("<:refresh:1101547898803605605>"), emote: Emote.Parse("<:refresh:1101547898803605605>"),
style: ButtonStyle.Secondary, style: ButtonStyle.Secondary,
customId: $"Sm.Update.{server.Id}"); customId: $"Sm.Update.{server.Id}");
if (ConfigService.GetSection("Moonlight").GetSection("DiscordBot").GetValue<bool>("SendCommands")) if (ConfigService.Get().Moonlight.DiscordBot.SendCommands)
{ {
components.WithButton("SendCommand", components.WithButton("SendCommand",
emote: Emote.Parse("<:Console:1101547358157819944>"), emote: Emote.Parse("<:Console:1101547358157819944>"),

View File

@@ -33,15 +33,15 @@ public class DomainService
SharedDomainRepository = sharedDomainRepository; SharedDomainRepository = sharedDomainRepository;
var config = configService var config = configService
.GetSection("Moonlight") .Get()
.GetSection("Domains"); .Moonlight.Domains;
AccountId = config.GetValue<string>("AccountId"); AccountId = config.AccountId;
Client = new( Client = new(
new ApiKeyAuthentication( new ApiKeyAuthentication(
config.GetValue<string>("Email"), config.Email,
config.GetValue<string>("Key") config.Key
) )
); );
} }

View File

@@ -8,7 +8,7 @@ public class ResourceService
public ResourceService(ConfigService configService) public ResourceService(ConfigService configService)
{ {
AppUrl = configService.GetSection("Moonlight").GetValue<string>("AppUrl"); AppUrl = configService.Get().Moonlight.AppUrl;
} }
public string Image(string name) public string Image(string name)

View File

@@ -15,6 +15,7 @@ public class StorageService
Directory.CreateDirectory(PathBuilder.Dir("storage", "configs")); Directory.CreateDirectory(PathBuilder.Dir("storage", "configs"));
Directory.CreateDirectory(PathBuilder.Dir("storage", "resources")); Directory.CreateDirectory(PathBuilder.Dir("storage", "resources"));
Directory.CreateDirectory(PathBuilder.Dir("storage", "backups")); Directory.CreateDirectory(PathBuilder.Dir("storage", "backups"));
Directory.CreateDirectory(PathBuilder.Dir("storage", "logs"));
if(IsEmpty(PathBuilder.Dir("storage", "resources"))) if(IsEmpty(PathBuilder.Dir("storage", "resources")))
{ {

View File

@@ -25,16 +25,15 @@ public class ReCaptchaService
ConfigService = configService; ConfigService = configService;
var recaptchaConfig = ConfigService var recaptchaConfig = ConfigService
.GetSection("Moonlight") .Get()
.GetSection("Security") .Moonlight.Security.ReCaptcha;
.GetSection("ReCaptcha");
Enable = recaptchaConfig.GetValue<bool>("Enable"); Enable = recaptchaConfig.Enable;
if (Enable) if (Enable)
{ {
SiteKey = recaptchaConfig.GetValue<string>("SiteKey"); SiteKey = recaptchaConfig.SiteKey;
SecretKey = recaptchaConfig.GetValue<string>("SecretKey"); SecretKey = recaptchaConfig.SecretKey;
} }
} }

View File

@@ -17,14 +17,14 @@ public class MailService
public MailService(ConfigService configService) public MailService(ConfigService configService)
{ {
var mailConfig = configService var mailConfig = configService
.GetSection("Moonlight") .Get()
.GetSection("Mail"); .Moonlight.Mail;
Server = mailConfig.GetValue<string>("Server"); Server = mailConfig.Server;
Password = mailConfig.GetValue<string>("Password"); Password = mailConfig.Password;
Email = mailConfig.GetValue<string>("Email"); Email = mailConfig.Email;
Port = mailConfig.GetValue<int>("Port"); Port = mailConfig.Port;
Ssl = mailConfig.GetValue<bool>("Ssl"); Ssl = mailConfig.Ssl;
} }
public async Task SendMail( public async Task SendMail(

View File

@@ -1,4 +1,5 @@
using Moonlight.App.Database.Entities; using Mappy.Net;
using Moonlight.App.Database.Entities;
using Moonlight.App.Exceptions; using Moonlight.App.Exceptions;
using Moonlight.App.Helpers; using Moonlight.App.Helpers;
using Moonlight.App.Models.Misc; using Moonlight.App.Models.Misc;
@@ -24,14 +25,17 @@ public class OAuth2Service
ConfigService = configService; ConfigService = configService;
ServiceScopeFactory = serviceScopeFactory; ServiceScopeFactory = serviceScopeFactory;
var config = ConfigService.GetSection("Moonlight").GetSection("OAuth2"); var config = ConfigService
.Get()
.Moonlight.OAuth2;
Configs = config.GetSection("Providers").Get<OAuth2ProviderConfig[]>() Configs = config.Providers
?? Array.Empty<OAuth2ProviderConfig>(); .Select(Mapper.Map<OAuth2ProviderConfig>)
.ToArray();
OverrideUrl = config.GetValue<string>("OverrideUrl"); OverrideUrl = config.OverrideUrl;
EnableOverrideUrl = config.GetValue<bool>("EnableOverrideUrl"); EnableOverrideUrl = config.EnableOverrideUrl;
AppUrl = configService.GetSection("Moonlight").GetValue<string>("AppUrl"); AppUrl = configService.Get().Moonlight.AppUrl;
// Register additional providers here // Register additional providers here
RegisterOAuth2<DiscordOAuth2Provider>("discord"); RegisterOAuth2<DiscordOAuth2Provider>("discord");

View File

@@ -25,10 +25,9 @@ public class OneTimeJwtService
var opt = new Dictionary<string, string>(); var opt = new Dictionary<string, string>();
options.Invoke(opt); options.Invoke(opt);
string secret = ConfigService var secret = ConfigService
.GetSection("Moonlight") .Get()
.GetSection("Security") .Moonlight.Security.Token;
.GetValue<string>("Token");
var id = StringHelper.GenerateString(16); var id = StringHelper.GenerateString(16);
@@ -55,10 +54,9 @@ public class OneTimeJwtService
public async Task<Dictionary<string, string>?> Validate(string token) public async Task<Dictionary<string, string>?> Validate(string token)
{ {
string secret = ConfigService var secret = ConfigService
.GetSection("Moonlight") .Get()
.GetSection("Security") .Moonlight.Security.Token;
.GetValue<string>("Token");
string json; string json;

View File

@@ -26,12 +26,12 @@ public class RatingService
Event = eventSystem; Event = eventSystem;
UserRepository = userRepository; UserRepository = userRepository;
var config = configService.GetSection("Moonlight").GetSection("Rating"); var config = configService.Get().Moonlight.Rating;
Enabled = config.GetValue<bool>("Enabled"); Enabled = config.Enabled;
Url = config.GetValue<string>("Url"); Url = config.Url;
MinRating = config.GetValue<int>("MinRating"); MinRating = config.MinRating;
DaysSince = config.GetValue<int>("DaysSince"); DaysSince = config.DaysSince;
} }
public async Task<bool> ShouldRate() public async Task<bool> ShouldRate()

View File

@@ -79,10 +79,20 @@ public class ServerService
{ {
Server server = EnsureNodeData(s); 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, server.Node,
$"api/servers/{server.Uuid}" $"api/servers/{server.Uuid}"
); );
});
return result;
} }
public async Task SetPowerState(Server s, PowerSignal signal) public async Task SetPowerState(Server s, PowerSignal signal)
@@ -109,7 +119,8 @@ public class ServerService
var backup = new ServerBackup() 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(), Uuid = Guid.NewGuid(),
CreatedAt = DateTimeService.GetCurrent(), CreatedAt = DateTimeService.GetCurrent(),
Created = false Created = false
@@ -175,8 +186,15 @@ public class ServerService
try 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); null);
});
} }
catch (WingsException e) catch (WingsException e)
{ {
@@ -257,7 +275,8 @@ public class ServerService
freeAllocations = NodeAllocationRepository freeAllocations = NodeAllocationRepository
.Get() .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(); .ToArray();
} }
catch (Exception) catch (Exception)
@@ -285,7 +304,8 @@ public class ServerService
Allocations = freeAllocations.ToList(), Allocations = freeAllocations.ToList(),
Backups = new(), Backups = new(),
OverrideStartup = "", OverrideStartup = "",
DockerImageIndex = image.DockerImages.FindIndex(x => x.Default) DockerImageIndex = image.DockerImages.FindIndex(x => x.Default),
Installing = true
}; };
foreach (var imageVariable in image.Variables) foreach (var imageVariable in image.Variables)
@@ -303,12 +323,18 @@ public class ServerService
var newServerData = ServerRepository.Add(server); var newServerData = ServerRepository.Add(server);
try 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() await WingsApiHelper.Post(node, $"api/servers", new CreateServer()
{ {
Uuid = newServerData.Uuid, Uuid = newServerData.Uuid,
StartOnCompletion = false StartOnCompletion = false
}); });
});
//TODO: AuditLog //TODO: AuditLog
@@ -369,8 +395,6 @@ public class ServerService
public async Task Delete(Server s) public async Task Delete(Server s)
{ {
throw new DisplayException("Deleting servers is currently disabled");
var backups = await GetBackups(s); var backups = await GetBackups(s);
foreach (var backup in backups) foreach (var backup in backups)
@@ -391,7 +415,21 @@ public class ServerService
.Include(x => x.Node) .Include(x => x.Node)
.First(x => x.Id == s.Id); .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); 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()) foreach (var variable in server.Variables.ToArray())
{ {

View File

@@ -30,9 +30,8 @@ public class IdentityService
HttpContextAccessor = httpContextAccessor; HttpContextAccessor = httpContextAccessor;
Secret = configService Secret = configService
.GetSection("Moonlight") .Get()
.GetSection("Security") .Moonlight.Security.Token;
.GetValue<string>("Token");
} }
public async Task<User?> Get() public async Task<User?> Get()

View File

@@ -28,13 +28,12 @@ public class SmartDeployService
public async Task<Node?> GetNode() public async Task<Node?> GetNode()
{ {
var config = ConfigService var config = ConfigService
.GetSection("Moonlight") .Get()
.GetSection("SmartDeploy") .Moonlight.SmartDeploy.Server;
.GetSection("Server");
if (config.GetValue<bool>("EnableOverride")) if (config.EnableOverride)
{ {
var nodeId = config.GetValue<int>("OverrideNode"); var nodeId = config.OverrideNode;
return NodeRepository.Get().FirstOrDefault(x => x.Id == nodeId); return NodeRepository.Get().FirstOrDefault(x => x.Id == nodeId);
} }

View File

@@ -17,13 +17,13 @@ public class StatisticsCaptureService
DateTimeService = dateTimeService; DateTimeService = dateTimeService;
var config = configService var config = configService
.GetSection("Moonlight") .Get()
.GetSection("Statistics"); .Moonlight.Statistics;
if(!config.GetValue<bool>("Enabled")) if(!config.Enabled)
return; return;
var period = TimeSpan.FromMinutes(config.GetValue<int>("Wait")); var period = TimeSpan.FromMinutes(config.Wait);
Timer = new(period); Timer = new(period);
Logger.Info("Starting statistics system"); Logger.Info("Starting statistics system");

View File

@@ -38,9 +38,8 @@ public class UserService
DateTimeService = dateTimeService; DateTimeService = dateTimeService;
JwtSecret = configService JwtSecret = configService
.GetSection("Moonlight") .Get()
.GetSection("Security") .Moonlight.Security.Token;
.GetValue<string>("Token");
} }
public async Task<string> Register(string email, string password, string firstname, string lastname) public async Task<string> Register(string email, string password, string firstname, string lastname)

View File

@@ -46,7 +46,10 @@
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" /> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" />
<PackageReference Include="QRCoder" Version="1.4.3" /> <PackageReference Include="QRCoder" Version="1.4.3" />
<PackageReference Include="RestSharp" Version="109.0.0-preview.1" /> <PackageReference Include="RestSharp" Version="109.0.0-preview.1" />
<PackageReference Include="Sentry.AspNetCore" Version="3.33.1" />
<PackageReference Include="Sentry.Serilog" Version="3.33.1" />
<PackageReference Include="Serilog" Version="3.0.0" /> <PackageReference Include="Serilog" Version="3.0.0" />
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.1-dev-00910" /> <PackageReference Include="Serilog.Sinks.Console" Version="4.1.1-dev-00910" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.1-dev-00947" /> <PackageReference Include="Serilog.Sinks.File" Version="5.0.1-dev-00947" />
<PackageReference Include="SSH.NET" Version="2020.0.2" /> <PackageReference Include="SSH.NET" Version="2020.0.2" />
@@ -100,4 +103,12 @@
<AdditionalFiles Include="Shared\Views\Server\Settings\ServerResetSetting.razor" /> <AdditionalFiles Include="Shared\Views\Server\Settings\ServerResetSetting.razor" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Update="storage\configs\config.json.bak">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
</Project> </Project>

View File

@@ -11,12 +11,12 @@
@{ @{
var headerConfig = ConfigService var headerConfig = ConfigService
.GetSection("Moonlight") .Get()
.GetSection("Html") .Moonlight.Html.Headers;
.GetSection("Headers");
var moonlightConfig = ConfigService var moonlightConfig = ConfigService
.GetSection("Moonlight"); .Get()
.Moonlight;
} }
<!DOCTYPE html> <!DOCTYPE html>
@@ -26,16 +26,16 @@
<meta property="og:locale" content="de_DE"/> <meta property="og:locale" content="de_DE"/>
<meta property="og:type" content="article"/> <meta property="og:type" content="article"/>
<meta content="@(headerConfig.GetValue<string>("Title"))" property="og:title"/> <meta content="@(headerConfig.Title)" property="og:title"/>
<meta content="@(headerConfig.GetValue<string>("Description"))" property="og:description"/> <meta content="@(headerConfig.Description)" property="og:description"/>
<meta content="@(moonlightConfig.GetValue<string>("AppUrl"))" property="og:url"/> <meta content="@(moonlightConfig.AppUrl)" property="og:url"/>
<meta content="@(moonlightConfig.GetValue<string>("AppUrl"))/api/moonlight/resources/images/logolong.png" property="og:image"/> <meta content="@(moonlightConfig.AppUrl)/api/moonlight/resources/images/logolong.png" property="og:image"/>
<meta content="@(headerConfig.GetValue<string>("Color"))" data-react-helmet="true" name="theme-color"/> <meta content="@(headerConfig.Color)" data-react-helmet="true" name="theme-color"/>
<meta content="@(headerConfig.GetValue<string>("Description"))" name="description"/> <meta content="@(headerConfig.Description)" name="description"/>
<meta content="@(headerConfig.GetValue<string>("Keywords"))" name="keywords"/> <meta content="@(headerConfig.Keywords)" name="keywords"/>
<link rel="shortcut icon" href="@(moonlightConfig.GetValue<string>("AppUrl"))/api/moonlight/resources/images/logo.svg"/> <link rel="shortcut icon" href="@(moonlightConfig.AppUrl)/api/moonlight/resources/images/logo.svg"/>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Poppins:300,400,500,600,700"/> <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Poppins:300,400,500,600,700"/>
@@ -75,7 +75,7 @@
<div id="flashbang" class="flashbanglight"></div> <div id="flashbang" class="flashbanglight"></div>
<div class="app-page-loader flex-column"> <div class="app-page-loader flex-column">
<img alt="Logo" src="@(moonlightConfig.GetValue<string>("AppUrl"))/api/moonlight/resources/images/logo.svg" class="h-25px"/> <img alt="Logo" src="@(moonlightConfig.AppUrl)/api/moonlight/resources/images/logo.svg" class="h-25px"/>
@{ @{
string loadingMessage; string loadingMessage;

View File

@@ -29,7 +29,9 @@ using Moonlight.App.Services.Notifications;
using Moonlight.App.Services.Sessions; using Moonlight.App.Services.Sessions;
using Moonlight.App.Services.Statistics; using Moonlight.App.Services.Statistics;
using Moonlight.App.Services.SupportChat; using Moonlight.App.Services.SupportChat;
using Sentry;
using Serilog; using Serilog;
using Serilog.Events;
using Serilog.Sinks.SystemConsole.Themes; using Serilog.Sinks.SystemConsole.Themes;
namespace Moonlight namespace Moonlight
@@ -40,14 +42,53 @@ namespace Moonlight
{ {
// This will also copy all default config files // This will also copy all default config files
var configService = new ConfigService(new StorageService()); var configService = new ConfigService(new StorageService());
var shouldUseSentry = configService
.Get()
.Moonlight.Sentry.Enable;
if (configService.DebugMode) if (configService.DebugMode)
{
if (shouldUseSentry)
{ {
Log.Logger = new LoggerConfiguration() Log.Logger = new LoggerConfiguration()
.MinimumLevel.Verbose() .MinimumLevel.Verbose()
.Enrich.FromLogContext() .Enrich.FromLogContext()
.WriteTo.Console( .WriteTo.Console(
outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}") outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}")
.WriteTo.File(PathBuilder.File("storage", "logs", $"{DateTime.UtcNow:yyyy-MM-dd}.log"))
.WriteTo.Sentry(options =>
{
options.MinimumBreadcrumbLevel = LogEventLevel.Debug;
options.MinimumEventLevel = LogEventLevel.Warning;
})
.CreateLogger();
}
else
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.Enrich.FromLogContext()
.WriteTo.Console(
outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}")
.WriteTo.File(PathBuilder.File("storage", "logs", $"{DateTime.UtcNow:yyyy-MM-dd}.log"))
.CreateLogger();
}
}
else
{
if (shouldUseSentry)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.Enrich.FromLogContext()
.WriteTo.Console(
outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}")
.WriteTo.Sentry(options =>
{
options.MinimumBreadcrumbLevel = LogEventLevel.Information;
options.MinimumEventLevel = LogEventLevel.Warning;
})
.WriteTo.File(PathBuilder.File("storage", "logs", $"{DateTime.UtcNow:yyyy-MM-dd}.log"))
.CreateLogger(); .CreateLogger();
} }
else else
@@ -57,8 +98,10 @@ namespace Moonlight
.Enrich.FromLogContext() .Enrich.FromLogContext()
.WriteTo.Console( .WriteTo.Console(
outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}") outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}")
.WriteTo.File(PathBuilder.File("storage", "logs", $"{DateTime.UtcNow:yyyy-MM-dd}.log"))
.CreateLogger(); .CreateLogger();
} }
}
Logger.Info($"Working dir: {Directory.GetCurrentDirectory()}"); Logger.Info($"Working dir: {Directory.GetCurrentDirectory()}");
@@ -71,8 +114,8 @@ namespace Moonlight
// Switch to logging.net injection // Switch to logging.net injection
// TODO: Enable in production // TODO: Enable in production
//builder.Logging.ClearProviders(); builder.Logging.ClearProviders();
//builder.Logging.AddProvider(new LogMigratorProvider()); builder.Logging.AddProvider(new LogMigratorProvider());
// Add services to the container. // Add services to the container.
builder.Services.AddRazorPages(); builder.Services.AddRazorPages();
@@ -89,6 +132,23 @@ namespace Moonlight
.AddCheck<NodeHealthCheck>("Nodes") .AddCheck<NodeHealthCheck>("Nodes")
.AddCheck<DaemonHealthCheck>("Daemons"); .AddCheck<DaemonHealthCheck>("Daemons");
// Sentry
if (shouldUseSentry)
{
builder.WebHost.UseSentry(options =>
{
options.Dsn = configService
.Get()
.Moonlight.Sentry.Dsn;
options.Debug = configService.DebugMode;
options.DiagnosticLevel = SentryLevel.Warning;
options.TracesSampleRate = 1.0;
options.DiagnosticLogger = new SentryDiagnosticsLogger(SentryLevel.Warning);
});
}
// Databases // Databases
builder.Services.AddDbContext<DataContext>(); builder.Services.AddDbContext<DataContext>();
@@ -203,6 +263,12 @@ namespace Moonlight
app.UseHsts(); app.UseHsts();
} }
// Sentry
if (shouldUseSentry)
{
app.UseSentryTracing();
}
app.UseStaticFiles(); app.UseStaticFiles();
app.UseRouting(); app.UseRouting();
app.UseWebSockets(); app.UseWebSockets();

View File

@@ -1,16 +1,12 @@
@using Moonlight.App.Services @using Moonlight.App.Services
@using Moonlight.App.Services.Files
@inject ConfigService ConfigService @inject ResourceService ResourceService
@{
var moonlightConfig = ConfigService
.GetSection("Moonlight");
}
<div class="card card-flush w-lg-650px py-5"> <div class="card card-flush w-lg-650px py-5">
<div class="card-body py-15 py-lg-20"> <div class="card-body py-15 py-lg-20">
<div class="mb-14"> <div class="mb-14">
<img alt="Logo" src="@(moonlightConfig.GetValue<string>("AppUrl"))/api/moonlight/resources/images/logolong.png" class="h-40px"> <img alt="Logo" src="@(ResourceService.Image("logolong.png"))" class="h-40px">
</div> </div>
<h1 class="fw-bolder text-gray-900 mb-5"><TL>Your account is banned from moonlight</TL></h1> <h1 class="fw-bolder text-gray-900 mb-5"><TL>Your account is banned from moonlight</TL></h1>
<div class="fw-semibold fs-6 text-gray-500 mb-8"> <div class="fw-semibold fs-6 text-gray-500 mb-8">

View File

@@ -1,16 +1,12 @@
@using Moonlight.App.Services @using Moonlight.App.Services
@using Moonlight.App.Services.Files
@inject ConfigService ConfigService @inject ResourceService ResourceService
@{
var moonlightConfig = ConfigService
.GetSection("Moonlight");
}
<div class="card card-flush w-lg-650px py-5"> <div class="card card-flush w-lg-650px py-5">
<div class="card-body py-15 py-lg-20"> <div class="card-body py-15 py-lg-20">
<div class="mb-14"> <div class="mb-14">
<img alt="Logo" src="@(moonlightConfig.GetValue<string>("AppUrl"))/api/moonlight/resources/images/logolong.png" class="h-40px"> <img alt="Logo" src="@(ResourceService.Image("logolong.png"))" class="h-40px">
</div> </div>
<h1 class="fw-bolder text-gray-900 mb-5"><TL>Your moonlight account is disabled</TL></h1> <h1 class="fw-bolder text-gray-900 mb-5"><TL>Your moonlight account is disabled</TL></h1>
<div class="fw-semibold fs-6 text-gray-500 mb-8"> <div class="fw-semibold fs-6 text-gray-500 mb-8">

View File

@@ -63,6 +63,8 @@
private int Port; private int Port;
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()
{
try
{ {
Uri uri = new Uri(await Access.GetLaunchUrl()); Uri uri = new Uri(await Access.GetLaunchUrl());
@@ -70,6 +72,13 @@
Port = uri.Port; Port = uri.Port;
Username = uri.UserInfo.Split(':')[0]; Username = uri.UserInfo.Split(':')[0];
} }
catch (NotImplementedException)
{
Host = "N/A";
Port = -1;
Username = "N/A";
}
}
public async Task Show() public async Task Show()
{ {

View File

@@ -10,8 +10,8 @@
</a> </a>
</li> </li>
<li class="nav-item mt-2"> <li class="nav-item mt-2">
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 1 ? "active" : "")" href="/admin/system/logs"> <a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 1 ? "active" : "")" href="/admin/system/sentry">
<TL>Logs</TL> <TL>Sentry</TL>
</a> </a>
</li> </li>
<li class="nav-item mt-2"> <li class="nav-item mt-2">

View File

@@ -4,31 +4,31 @@
@{ @{
var marketingConfig = ConfigService var marketingConfig = ConfigService
.GetSection("Moonlight") .Get()
.GetSection("Marketing"); .Moonlight.Marketing;
} }
<div id="kt_app_footer" class="app-footer"> <div id="kt_app_footer" class="app-footer">
<div class="app-container container-fluid d-flex flex-column flex-md-row flex-center flex-md-stack py-3"> <div class="app-container container-fluid d-flex flex-column flex-md-row flex-center flex-md-stack py-3">
<div class="text-dark order-2 order-md-1"> <div class="text-dark order-2 order-md-1">
<span class="text-muted fw-semibold me-1">2022 - @DateTime.Now.Year©</span> <span class="text-muted fw-semibold me-1">2022 - @DateTime.Now.Year©</span>
<a href="@(marketingConfig.GetValue<string>("Website"))" target="_blank" class="text-gray-800 text-hover-primary"> <a href="@(marketingConfig.Website)" target="_blank" class="text-gray-800 text-hover-primary">
@(marketingConfig.GetValue<string>("BrandName")) @(marketingConfig.BrandName)
</a> </a>
</div> </div>
<ul class="menu menu-gray-600 menu-hover-primary fw-semibold order-1"> <ul class="menu menu-gray-600 menu-hover-primary fw-semibold order-1">
<li class="menu-item"> <li class="menu-item">
<a href="@(marketingConfig.GetValue<string>("About"))" target="_blank" class="menu-link px-2"> <a href="@(marketingConfig.About)" target="_blank" class="menu-link px-2">
<TL>About us</TL> <TL>About us</TL>
</a> </a>
</li> </li>
<li class="menu-item"> <li class="menu-item">
<a href="@(marketingConfig.GetValue<string>("Imprint"))" target="_blank" class="menu-link px-2"> <a href="@(marketingConfig.Imprint)" target="_blank" class="menu-link px-2">
<TL>Imprint</TL> <TL>Imprint</TL>
</a> </a>
</li> </li>
<li class="menu-item"> <li class="menu-item">
<a href="@(marketingConfig.GetValue<string>("Privacy"))" target="_blank" class="menu-link px-2"> <a href="@(marketingConfig.Privacy)" target="_blank" class="menu-link px-2">
<TL>Privacy</TL> <TL>Privacy</TL>
</a> </a>
</li> </li>

View File

@@ -1,10 +1,6 @@
@using Moonlight.App.Services @using Moonlight.App.Services.Files
@inject ConfigService ConfigService @inject ResourceService ResourceService
@{
var moonlightConfig = ConfigService.GetSection("Moonlight");
}
<div id="kt_app_header" class="app-header"> <div id="kt_app_header" class="app-header">
<div class="app-container container-fluid d-flex align-items-stretch justify-content-between"> <div class="app-container container-fluid d-flex align-items-stretch justify-content-between">
@@ -15,7 +11,7 @@
</div> </div>
<div class="d-flex align-items-center flex-grow-1 flex-lg-grow-0"> <div class="d-flex align-items-center flex-grow-1 flex-lg-grow-0">
<a href="/" class="d-lg-none"> <a href="/" class="d-lg-none">
<img alt="Logo" src="@(moonlightConfig.GetValue<string>("AppUrl"))/api/moonlight/resources/images/logo.svg" class="h-30px"/> <img alt="Logo" src="@(ResourceService.Image("logo.svg"))" class="h-30px"/>
</a> </a>
</div> </div>
<div class="d-flex align-items-stretch justify-content-between flex-lg-grow-1" id="kt_app_header_wrapper"> <div class="d-flex align-items-stretch justify-content-between flex-lg-grow-1" id="kt_app_header_wrapper">

View File

@@ -1,32 +1,28 @@
@using Moonlight.App.Services.Sessions @using Moonlight.App.Services.Sessions
@using Moonlight.App.Database.Entities @using Moonlight.App.Database.Entities
@using Moonlight.App.Services @using Moonlight.App.Services
@using Moonlight.App.Services.Files
@inject IdentityService IdentityService @inject IdentityService IdentityService
@inject ConfigService ConfigService @inject ResourceService ResourceService
@inject IJSRuntime JsRuntime @inject IJSRuntime JsRuntime
@{
var moonlightConfig = ConfigService
.GetSection("Moonlight");
}
<div id="kt_app_sidebar" class="app-sidebar flex-column" data-kt-drawer="true" data-kt-drawer-name="app-sidebar" data-kt-drawer-activate="{default: true, lg: false}" data-kt-drawer-overlay="true" data-kt-drawer-width="225px" data-kt-drawer-direction="start" data-kt-drawer-toggle="#kt_app_sidebar_mobile_toggle"> <div id="kt_app_sidebar" class="app-sidebar flex-column" data-kt-drawer="true" data-kt-drawer-name="app-sidebar" data-kt-drawer-activate="{default: true, lg: false}" data-kt-drawer-overlay="true" data-kt-drawer-width="225px" data-kt-drawer-direction="start" data-kt-drawer-toggle="#kt_app_sidebar_mobile_toggle">
<div class="app-sidebar-logo px-6" id="kt_app_sidebar_logo"> <div class="app-sidebar-logo px-6" id="kt_app_sidebar_logo">
<a href="@(User != null ? "/" : "/login")"> <a href="@(User != null ? "/" : "/login")">
@if (sidebar == "dark-sidebar") @if (sidebar == "dark-sidebar")
{ {
<img alt="Logo" src="@(moonlightConfig.GetValue<string>("AppUrl"))/api/moonlight/resources/images/logolong.png" class="h-45px app-sidebar-logo-default"/> <img alt="Logo" src="@(ResourceService.Image("logolong.png"))" class="h-45px app-sidebar-logo-default"/>
} }
else else
{ {
if (sidebar == "light-sidebar") if (sidebar == "light-sidebar")
{ {
<img alt="Logo" src="@(moonlightConfig.GetValue<string>("AppUrl"))/api/moonlight/resources/images/logo.svg" class="theme-light-show h-20px app-sidebar-logo-default"/> <img alt="Logo" src="@(ResourceService.Image("logo.svg"))" class="theme-light-show h-20px app-sidebar-logo-default"/>
<img alt="Logo" src="@(moonlightConfig.GetValue<string>("AppUrl"))/api/moonlight/resources/images/logo.svg" class="theme-dark-show h-20px app-sidebar-logo-default"/> <img alt="Logo" src="@(ResourceService.Image("logo.svg"))" class="theme-dark-show h-20px app-sidebar-logo-default"/>
} }
} }
<img alt="Logo" src="@(moonlightConfig.GetValue<string>("AppUrl"))/api/moonlight/resources/images/logo.svg" class="h-20px app-sidebar-logo-minimize"/> <img alt="Logo" src="@(ResourceService.Image("logo.svg"))" class="h-20px app-sidebar-logo-minimize"/>
</a> </a>
<div id="kt_app_sidebar_toggle" class="app-sidebar-toggle btn btn-icon btn-shadow btn-sm btn-color-muted btn-active-color-primary body-bg h-30px w-30px position-absolute top-50 start-100 translate-middle rotate" data-kt-toggle="true" data-kt-toggle-state="active" data-kt-toggle-target="body" data-kt-toggle-name="app-sidebar-minimize"> <div id="kt_app_sidebar_toggle" class="app-sidebar-toggle btn btn-icon btn-shadow btn-sm btn-color-muted btn-active-color-primary body-bg h-30px w-30px position-absolute top-50 start-100 translate-middle rotate" data-kt-toggle="true" data-kt-toggle-state="active" data-kt-toggle-target="body" data-kt-toggle-name="app-sidebar-minimize">
@@ -38,7 +34,7 @@
<div class="app-sidebar-footer flex-column-auto pt-2 pb-6 px-6" id="kt_app_sidebar_footer"> <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"> <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> </a>
</div> </div>
</div> </div>

View File

@@ -1,20 +0,0 @@
@using Moonlight.App.Services
@inject ConfigService ConfigService
@{
var setupComplete = ConfigService
.GetSection("Moonlight")
.GetValue<bool>("SetupComplete");
}
@if (!setupComplete)
{
@ChildContent
}
@code
{
[Parameter]
public RenderFragment ChildContent { get; set; }
}

View File

@@ -1,20 +0,0 @@
@using Moonlight.App.Services
@inject ConfigService ConfigService
@{
var setupComplete = ConfigService
.GetSection("Moonlight")
.GetValue<bool>("SetupComplete");
}
@if (setupComplete)
{
@ChildContent
}
@code
{
[Parameter]
public RenderFragment ChildContent { get; set; }
}

View File

@@ -1,4 +1,5 @@
@inherits LayoutComponentBase @using Moonlight.App.Extensions
@inherits LayoutComponentBase
@inject IJSRuntime JS @inject IJSRuntime JS
@inject NavigationManager NavigationManager @inject NavigationManager NavigationManager
@@ -14,23 +15,23 @@ window.emptyBody = function(){
{ {
protected override void OnAfterRender(bool firstRender) protected override void OnAfterRender(bool firstRender)
{ {
JS.InvokeVoidAsync("KTThemeMode.init"); JS.InvokeVoidSafe("KTThemeMode.init");
JS.InvokeVoidAsync("emptyBody"); JS.InvokeVoidSafe("emptyBody");
if (firstRender) if (firstRender)
{ {
JS.InvokeVoidAsync("scrollTo", 0, 0); JS.InvokeVoidSafe("scrollTo", 0, 0);
JS.InvokeVoidAsync("KTDialer.init"); JS.InvokeVoidSafe("KTDialer.init");
JS.InvokeVoidAsync("KTDrawer.init"); JS.InvokeVoidSafe("KTDrawer.init");
JS.InvokeVoidAsync("KTMenu.init"); JS.InvokeVoidSafe("KTMenu.init");
JS.InvokeVoidAsync("KTImageInput.init"); JS.InvokeVoidSafe("KTImageInput.init");
JS.InvokeVoidAsync("KTPasswordMeter.init"); JS.InvokeVoidSafe("KTPasswordMeter.init");
JS.InvokeVoidAsync("KTScroll.init"); JS.InvokeVoidSafe("KTScroll.init");
JS.InvokeVoidAsync("KTScrolltop.init"); JS.InvokeVoidSafe("KTScrolltop.init");
JS.InvokeVoidAsync("KTSticky.init"); JS.InvokeVoidSafe("KTSticky.init");
JS.InvokeVoidAsync("KTSwapper.init"); JS.InvokeVoidSafe("KTSwapper.init");
JS.InvokeVoidAsync("KTToggle.init"); JS.InvokeVoidSafe("KTToggle.init");
JS.InvokeVoidAsync("KTMenu.updateByLinkAttribute", $"/{NavigationManager.ToBaseRelativePath(NavigationManager.Uri)}"); JS.InvokeVoidSafe("KTMenu.updateByLinkAttribute", $"/{NavigationManager.ToBaseRelativePath(NavigationManager.Uri)}");
} }
JS.InvokeVoidAsync("KTLayoutSearch.init"); JS.InvokeVoidAsync("KTLayoutSearch.init");
@@ -45,30 +46,18 @@ window.emptyBody = function(){
private async void OnLocationChanged(object sender, LocationChangedEventArgs args) private async void OnLocationChanged(object sender, LocationChangedEventArgs args)
{ {
await InvokeJsSave("scrollTo", 0, 0); await JS.InvokeVoidSafeAsync("scrollTo", 0, 0);
await InvokeJsSave("KTDrawer.createInstances"); await JS.InvokeVoidSafeAsync("KTDrawer.createInstances");
await InvokeJsSave("KTMenu.createInstances"); await JS.InvokeVoidSafeAsync("KTMenu.createInstances");
await InvokeJsSave("KTImageInput.createInstances"); await JS.InvokeVoidSafeAsync("KTImageInput.createInstances");
await InvokeJsSave("KTPasswordMeter.createInstances"); await JS.InvokeVoidSafeAsync("KTPasswordMeter.createInstances");
await InvokeJsSave("KTScroll.createInstances"); await JS.InvokeVoidSafeAsync("KTScroll.createInstances");
await InvokeJsSave("KTScrolltop.createInstances"); await JS.InvokeVoidSafeAsync("KTScrolltop.createInstances");
await InvokeJsSave("KTSticky.createInstances"); await JS.InvokeVoidSafeAsync("KTSticky.createInstances");
await InvokeJsSave("KTSwapper.createInstances"); await JS.InvokeVoidSafeAsync("KTSwapper.createInstances");
await InvokeJsSave("KTToggle.createInstances"); await JS.InvokeVoidSafeAsync("KTToggle.createInstances");
await InvokeJsSave("KTMenu.updateByLinkAttribute", $"/{NavigationManager.ToBaseRelativePath(args.Location)}"); await JS.InvokeVoidSafeAsync("KTMenu.updateByLinkAttribute", $"/{NavigationManager.ToBaseRelativePath(args.Location)}");
await InvokeJsSave("KTAppSidebar.init"); await JS.InvokeVoidSafeAsync("KTAppSidebar.init");
}
private async Task InvokeJsSave(string method, params object[] args)
{
try
{
await JS.InvokeVoidAsync(method, args);
}
catch (Exception)
{
// ignored
}
} }
public void Dispose() public void Dispose()

View File

@@ -151,8 +151,8 @@
await lazyLoader.SetText("Loading health check data"); await lazyLoader.SetText("Loading health check data");
var appUrl = ConfigService var appUrl = ConfigService
.GetSection("Moonlight") .Get()
.GetValue<string>("AppUrl"); .Moonlight.AppUrl;
try try
{ {

View File

@@ -4,12 +4,10 @@
@using Moonlight.App.Services @using Moonlight.App.Services
@inject NodeRepository NodeRepository @inject NodeRepository NodeRepository
@inject SmartTranslateService SmartTranslateService
@inject ConfigService ConfigService @inject ConfigService ConfigService
@inject NavigationManager NavigationManager
@{ @{
var appUrl = ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl"); var appUrl = ConfigService.Get().Moonlight.AppUrl;
} }
<OnlyAdmin> <OnlyAdmin>

View File

@@ -1,20 +0,0 @@
@page "/admin/system/logs"
@using BlazorTable
@using Moonlight.App.Models.Misc
@using Moonlight.App.Services
@using Moonlight.Shared.Components.Navigations
@inject SmartTranslateService SmartTranslateService
<OnlyAdmin>
<AdminSystemNavigation Index="1"/>
</OnlyAdmin>
@code
{
private Task Load(LazyLoader arg)
{
return Task.CompletedTask;
}
}

View File

@@ -0,0 +1,40 @@
@page "/admin/system/sentry"
@using Moonlight.Shared.Components.Navigations
@using global::Sentry
<OnlyAdmin>
<AdminSystemNavigation Index="1"/>
<div class="row">
<div class="col-xxl-6 my-3">
<div class="card">
<div class="card-header">
<span class="card-title">
<TL>Status</TL>
</span>
</div>
<div class="card-body">
<span class="fs-5">
@if (SentrySdk.IsEnabled)
{
<TL>Sentry is enabled</TL>
}
else
{
<TL>Sentry is disabled</TL>
}
</span>
</div>
</div>
</div>
</div>
</OnlyAdmin>
@code
{
private Task Load(LazyLoader arg)
{
return Task.CompletedTask;
}
}

View File

@@ -24,12 +24,11 @@
@if (Subscription == null) @if (Subscription == null)
{ {
var config = ConfigService var config = ConfigService
.GetSection("Moonlight") .Get()
.GetSection("Subscriptions") .Moonlight.Subscriptions.SellPass;
.GetSection("Sellpass");
var enableSellpass = config.GetValue<bool>("Enable"); var enableSellpass = config.Enable;
var url = config.GetValue<string>("Url"); var url = config.Url;
<h3 class="mb-2"> <h3 class="mb-2">
<div class="input-group mb-3"> <div class="input-group mb-3">

View File

@@ -83,8 +83,10 @@
<div class="row min-h-200px draggable-zone" ml-server-group="@(group.Name)"> <div class="row min-h-200px draggable-zone" ml-server-group="@(group.Name)">
@foreach (var id in group.Servers) @foreach (var id in group.Servers)
{ {
var server = AllServers.First(x => x.Id.ToString() == id); var server = AllServers.FirstOrDefault(x => x.Id.ToString() == id);
if (server != null)
{
<div class="col-12 col-md-3 p-3 draggable" ml-server-id="@(server.Id)"> <div class="col-12 col-md-3 p-3 draggable" ml-server-id="@(server.Id)">
@if (EditMode) @if (EditMode)
{ {
@@ -172,6 +174,7 @@
} }
</div> </div>
} }
}
</div> </div>
</div> </div>
</div> </div>
@@ -353,205 +356,3 @@
} }
} }
} }
@*
@if (AllServers.Any())
{
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>
<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>
}
</div>
</div>
</a>
</div>
}
</div>
}
}
else
{
foreach (var server in AllServers)
{
<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))
{
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>
</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-header">
<div class="card-title">
<span class="badge badge-primary badge-lg">Beta</span>
</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>
</div>
</div>
</div>
</div>
</div>
</div>
*@

View File

@@ -1,97 +0,0 @@
{
"Moonlight": {
"AppUrl": "http://your-moonlight-url.test",
"Database": {
"Database": "database_name",
"Host": "your-moonlight-database-host.de",
"Password": "s3cr3t",
"Port": "10324",
"Username": "user_name"
},
"DiscordBotApi": {
"Enable": false,
"Token": "you api key here"
},
"DiscordBot": {
"Enable": false,
"Token": "Discord.Token.Here",
"PowerActions": false
},
"Domains": {
"_comment": "Cloudflare Api Credentials",
"AccountId": "Account Id here",
"Email": "Cloudflare Email here",
"Key": "Api Key Here"
},
"Html": {
"Headers": {
"Color": "#4b27e8",
"Description": "the next generation hosting panel",
"Keywords": "moonlight",
"Title": "Moonlight - moonlight.tld"
}
},
"Marketing": {
"BrandName": "My cool project",
"Imprint": "https://mycoolproject.de/imprint",
"Privacy": "https://mycoolproject.de/privacy",
"Website": "https://mycoolproject.de/"
},
"OAuth2": {
"_exampleProviders": [
{
"Id": "discord",
"ClientId": "",
"ClientSecret": ""
},
{
"Id": "google",
"ClientId": "",
"ClientSecret": ""
}
],
"Providers": [],
"EnableOverrideUrl": false,
"OverrideUrl": "http://your-moonlight-url.test"
},
"Security": {
"Token": "RANDOM UUID HERE"
},
"Mail": {
"Email": "no-reply@mycoolproject.de",
"Server": "mycoolproject.de",
"Password": "s3cr3t",
"Port": 465,
"Ssl": true
},
"Cleanup": {
"Cpu": 90,
"Memory": 8192,
"Wait": 15,
"Uptime": 6,
"Enable": false,
"MinUptime": 10
},
"Subscriptions": {
"_comment": "Not implemented",
"SellPass": {
"Enable": false,
"Url": ""
}
},
"DiscordNotifications": {
"Enable": false,
"WebHook": ""
},
"Statistics": {
"Enabled": true,
"Wait": 15
},
"Rating": {
"Enabled": true,
"Url": "link-to-google.page",
"MinRating": 4,
"DaysSince": 5
}
}
}

View File

@@ -1,94 +1,94 @@
Open support;Support öffnen Open support;Support Öffnen
About us;Über uns About us;Über uns
Imprint;Impressum Imprint;Impressum
Privacy;Datenschutz Privacy;Privacy
Login;Anmelden Login;Einloggen
Register;Registrieren Register;Registrieren
Insert brand name...;Markenname einfügen... Insert brand name...;Firmenname eingeben...
Save and continue;Speichern und fortfahren Save and continue;Speichern und Fortfahren
Saving;Speichere Saving;Speichern
Configure basics;Grundlagen konfigurieren Configure basics;Grundlagen einstellen
Brand name;Markenname Brand name;Firmenname
test;Test test;test
Insert first name...;Vornamen einfügen... Insert first name...;Vornamen eingeben...
Insert last name...;Nachname einfügen... Insert last name...;Nachnamen eingeben...
Insert email address...;E-Mail Adresse einfügen... Insert email address...;E-Mail-Adresse eingeben...
Add;Hinzufügen Add;Hinzufügen
Adding...;Füge hinzu... Adding...;Wird hinzugefügt...
Add admin accounts;Admin-Konten hinzufügen Add admin accounts;Admin Konto hinzufügen
First name;Vorname First name;Vorname
Last name;Nachname Last name;Nachname
Email address;Email-Adresse Email address;E-Mail-Adresse
Enter password;Passwort eingeben Enter password;Password eingeben
Next;Weiter Next;Weiter
Back;Zurück Back;Zurück
Configure features;Funktionen konfigurieren Configure features;Features konfigurieren
Support chat;Support-Chat Support chat;Chat Hilfe
Finish;Beenden Finish;Fertigstellen
Finalize installation;Installation abschließen Finalize installation;Installation Fertigstellen
Moonlight basic settings successfully configured;Moonlight Grundeinstellungen erfolgreich konfiguriert Moonlight basic settings successfully configured;Moonlight's Standard-Einstellungen wurden konfiguriert
Ooops. This page is crashed;Ooops. Diese Seite ist abgestürzt 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. Der Fehler wurde an das Moonlight-Team gemeldet. In der Zwischenzeit kannst du versuchen, die Seite neu zu laden 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 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 User successfully created;Benutzer erfolgreich erstellt
Ooops. Your moonlight client is crashed;Ups. Dein Moonlight-Client ist abgestürzt Ooops. Your moonlight client is crashed;Ups. Dein Moonlight ist abgestürzt.
This error has been reported to the moonlight team;Dieser Fehler wurde an das Moonlight-Team gemeldet This error has been reported to the moonlight team;Dieser Fehler wurde dem Moonlight-Team mitgeteilt
Sign In;Anmelden Sign In;Anmelden
Sign in to start with moonlight;Anmelden, um mit moonlight zu starten Sign in to start with moonlight;Anmelden, um mit Moonlight zu starten
Sign in with Discord;Mit Discord anmelden Sign in with Discord;Mit Discord Anmelden
Or with email;Oder mit E-Mail Or with email;Oder mit E-Mail
Forgot password?;Passwort vergessen? Forgot password?;Password vergessen?
Sign-in;Anmelden Sign-in;Anmelden
Not registered yet?;Noch nicht registriert? Not registered yet?;Noch nicht Registriert?
Sign up;Anmelden Sign up;Registrieren
Authenticating;Authentifiziere Authenticating;Authentifizieren...
Sign in with Google;Mit Google anmelden Sign in with Google;Mit Google Anmelden
Working;Läuft Working;Bitte warten...
Error;Fehler Error;Fehler
Email and password combination not found;E-Mail- und Passwortkombination nicht gefunden Email and password combination not found;E-Mail und Password-Kombination wurden nicht gefunden
Email;E-Mail Email;E-mail
Password;Passwort Password;Password
Account settings;Kontoeinstellungen Account settings;Benutzer-Einstellungen
Logout;Abmelden Logout;Abmelden
Dashboard;Dashboard Dashboard;Dashboard
Order;Bestellung Order;Bestellen
Website;Website Website;Website
Database;Datenbank Database;Datenbank
Domain;Domain Domain;Domain
Servers;Server Servers;Server
Websites;Webseiten Websites;Websiten
Databases;Datenbanken Databases;Datenbanken
Domains;Domains Domains;Domains
Changelog;Changelog Changelog;Anderungen
Firstname;Vorname Firstname;Vorname
Lastname;Nachname Lastname;Nachname
Repeat password;Passwort wiederholen Repeat password;Password wiederholen
Sign Up;Anmelden Sign Up;Anmelden
Sign up to start with moonlight;Anmelden, um mit Moonlight zu beginnen Sign up to start with moonlight;Registrieren um mit Moonlight zu starten
Sign up with Discord;Mit Discord anmelden Sign up with Discord;Mit Discord Registrieren
Sign up with Google;Mit Google anmelden Sign up with Google;Mit Google Registrieren
Sign-up;Anmelden Sign-up;Registrieren
Already registered?;Bereits registriert? Already registered?;Schon Registriert?
Sign in;Anmelden Sign in;Registrieren
Create something new;Etwas Neues erstellen Create something new;Etwas neues erstellen
Create a gameserver;Einen Gameserver erstellen Create a gameserver;Einen Gameserver erstellen
A new gameserver in just a few minutes;Ein neuer Gameserver in nur wenigen Minuten A new gameserver in just a few minutes;Ein neuer Gameserver in wenigen Minuten
Create a database;Erstelle eine Datenbank Create a database;Eine Datenbank erstellen
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 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;Verwalte deine Services Manage your services;Deine Dienste verwalten
Manage your gameservers;Verwalte deine Gameserver Manage your gameservers;Gameserver verwalten
Adjust your gameservers;Deine Gameserver anpassen Adjust your gameservers;Deine Gameserver anpassen
Manage your databases;Verwalte deine Datenbanken Manage your databases;Datenbanken verwalten
Insert, delete and update the data in your databases;Einfügen, Löschen und Aktualisieren der Daten in deinen Datenbanken Insert, delete and update the data in your databases;Daten in die Datenbank einfügen, entfernen und ändern
Create a website;Eine Website erstellen 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 Create a domain;Eine Domain erstellen
Make your servvices accessible throught your own domain;Mache deine Services über deine eigene Domain zugänglich Make your servvices accessible throught your own domain;Mache deine Dienste mit einer Domain erreichbar
Manage your websites;Verwalte deine Webseiten Manage your websites;Deine Websiten verwalten
Modify the content of your websites;Ändere den Inhalt deiner Websites Modify the content of your websites;Den Inhalt deiner Websiten verwalten
Manage your domains;Verwalte deine Domains Manage your domains;Deine Domains verwalten
Add, edit and delete dns records;DNS-Einträge hinzufügen, bearbeiten und löschen Add, edit and delete dns records;DNS-Records hinzufügen, entfernen oder bearbeiten
Admin;Admin Admin;Admin
System;System System;System
Overview;Übersicht Overview;Übersicht
@@ -98,72 +98,72 @@ Nodes;Nodes
Images;Images Images;Images
aaPanel;aaPanel aaPanel;aaPanel
Users;Benutzer Users;Benutzer
Support;Support Support;Hilfe
Statistics;Statistiken 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 Nodename;Nodename
FQDN;FQDN FQDN;FQDN
Create;Erstellen Create;Erstellen
Creating;Erstelle Creating;Wird erstellt...
Http port;Http-Port Http port;Http Port
Sftp port;Sftp-Port Sftp port;Sftp Port
Moonlight daemon port;Moonlight Daemon Port Moonlight daemon port;Moonlight Daemon Port
SSL;SSL SSL;SSL
CPU Usage;CPU-Auslastung CPU Usage;CPU Auslastung
In %;In % In %;In %
Memory;Speicher Memory;Arbeitsspeicher
Used / Available memory;Verwendeter / Verfügbarer Speicher Used / Available memory;Benutzter / Verfügbarer Arbeitsspeicher
Storage;Speicher Storage;Speicherplatz
Available storage;Verfügbarer Speicher Available storage;Verfügbarer Speicherplatz
Add a new node;Eine neue Node hinzufügen Add a new node;Ein neues Node hinzufügen
Delete;Löschen Delete;Löschen
Deleting;Lösche Deleting;Wirt gelöscht...
Edit;Bearbeiten Edit;Bearbeiten
Token Id;Token Id Token Id;Token Id
Token;Token Token;Token
Save;Speichern Save;Speichern
Setup;Einrichten Setup;Aufsetzen
Open a ssh connection to your node and enter;Öffne eine SSH-Verbindung zu deiner Node und gebe 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 füge die unten stehende Konfiguration ein. Drücke dann STRG+O und STRG+X, um zu speichern 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;Bevor du diese Node konfigurierst, installiere den Daemon Before configuring this node, install the daemon;Installiere den Daemon bevor du dieses Node konfigurierst
Delete this node?;Diese Node löschen? Delete this node?;Dieses Node löschen?
Do you really want to delete this node;Willst du diese Node wirklich löschen Do you really want to delete this node;Möchtest du dieses Node wirklich löschen?
Yes;Ja Yes;Ja
No;Nein No;Nein
Status;Status Status;Status
Adding;Füge hinzu Adding;Hinzufügen
Port;Port Port;Port
Id;Id Id;Id
Manage;Verwalten Manage;Verwalten
Create new server;Neuen Server erstellen Create new server;Neuen Server erstellen
No servers found;Keine Server gefunden No servers found;Keine Server gefunden
Server name;Servername Server name;Server Name
Cpu cores;CPU Kerne Cpu cores;CPU Kerne
Disk;Datenträger Disk;Speicherplatz
Image;Image Image;Image
Override startup;Startup überschreiben Override startup;Startup überschreiben
Docker image;Docker-Image Docker image;Docker Image
CPU Cores (100% = 1 Core);CPU-Kerne (100% = 1 Kern) CPU Cores (100% = 1 Core);CPU Kerne (100% = 1 Kern)
Server successfully created;Server erfolgreich erstellt Server successfully created;Server erfolgreich erstellt
Name;Name Name;Name
Cores;CPU Kerne Cores;Kerne
Owner;Eigentümer Owner;Besitzer
Value;Wert Value;Wert
An unknown error occured;Ein unbekannter Fehler ist aufgetreten An unknown error occured;Ein unbekannter Fehler ist aufgetreten
No allocation found;Keine Allocation gefunden No allocation found;Keine Zuweisung gefunden
Identifier;Identifier Identifier;Identifier
UuidIdentifier;UUIDIdentifier UuidIdentifier;UuidIdentifier
Override startup command;Startbefehl überschreiben Override startup command;Startup Befehl überschreiben
Loading;Lade Loading;Wird geladen...
Offline;Offline Offline;Offline
Connecting;Verbinde Connecting;Verbiden...
Start;Starten Start;Start
Restart;Neustarten Restart;Neu Starten
Stop;Stoppen Stop;Stoppen
Shared IP;Shared IP Shared IP;Geteilte IP
Server ID;Server-ID Server ID;Server ID
Cpu;Cpu Cpu;CPU
Console;Konsole Console;Console
Files;Dateien Files;Dateien
Backups;Backups Backups;Backups
Network;Netzwerk Network;Netzwerk
@@ -171,397 +171,466 @@ Plugins;Plugins
Settings;Einstellungen Settings;Einstellungen
Enter command;Befehl eingeben Enter command;Befehl eingeben
Execute;Ausführen Execute;Ausführen
Checking disk space;Prüfe Speicherplatz Checking disk space;Speicherplatz überprüfen
Updating config files;Aktualisiere Konfigurationsdateien Updating config files;Konfigurations-Dateien werden geupdatet
Checking file permissions;Prüfe Dateiberechtigungen Checking file permissions;Datei-Rechte werden überprüft
Downloading server image;Herunterladen des Serverabbilds Downloading server image;Server Image wird heruntergeladen
Downloaded server image;Heruntergeladenes Serverabbild Downloaded server image;Server Image wurde heruntergeladen
Starting;Starte Starting;Startet
Online;Online Online;Online
Kill;Killen Kill;Kill
Stopping;Stoppe Stopping;Stoppt
Search files and folders;Dateien und Verzeichnisse durchsuchen Search files and folders;Ordner und Dateien durchsuchen
Launch WinSCP;WinSCP starten Launch WinSCP;WinSCP starten
New folder;Neuer Ordner New folder;Neuer Ordner
Upload;Hochladen Upload;Hochladen
File name;Dateiname File name;Datei-Name
File size;Dateigröße File size;Datei-Größe
Last modified;Letzte Änderung Last modified;Zuletz geändert
Cancel;Abbrechen Cancel;Abbrechen
Canceling;Breche ab Canceling;Wird Abbgebrochen
Running;Laufend Running;Läuft
Loading backups;Lade Backups Loading backups;Backups werden geladen
Started backup creation;Backup-Erstellung gestartet Started backup creation;Backup wird erstellt
Backup is going to be created;Backup wird erstellt Backup is going to be created;Backup wird erstellt werden
Rename;Umbenennen Rename;Umbenennen
Move;Verschieben Move;Bewegen
Archive;Archivieren Archive;Archivieren
Unarchive;Unarchivieren Unarchive;Archivieren rückgängig machen
Download;Herunterladen Download;Herunterladen
Starting download;Starte Download Starting download;Herunterladen wird gestartet
Backup successfully created;Backup erfolgreich erstellt Backup successfully created;Backup wurde erfolgreich erstellt
Restore;Wiederherstellen Restore;Wiederherstellen
Copy url;Url kopieren Copy url;URL Kopieren
Backup deletion started;Backup-Löschung gestartet Backup deletion started;Backup löschung wird gestartet
Backup successfully deleted;Backup erfolgreich gelöscht Backup successfully deleted;Backup wurde erfolgreich gelöscht
Primary;Primär Primary;Primärer
This feature is currently not available;Diese Funktion ist derzeit nicht verfügbar This feature is currently not available;Diese Funktion ist zur Zeit nicht verfügbar
Send;Senden Send;Senden
Sending;Sende Sending;Wird gesendet
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 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; vor Minuten minutes ago; Minuten
just now;jetzt just now;gerade eben
less than a minute ago;vor weniger als einer Minute less than a minute ago;weniger als eine Minute
1 hour ago;vor 1 Stunde 1 hour ago;vor einer Stunde
1 minute ago;vor 1 Minute 1 minute ago;vor einer Minute
Failed;Fehlgeschlagen Failed;Fehlgeschlagen
hours ago; vor Stunden hours ago; Stunden
Open tickets;Offene Tickets Open tickets;Tickets öffnen
Actions;Aktionen Actions;Aktionen
No support ticket is currently open;Kein Supportticket ist zurzeit offen No support ticket is currently open;Kein Support Ticket ist zurzeit offen.
User information;Benutzerinformationen User information;Benutzer-Information
Close ticket;Anfrage schließen Close ticket;Ticket schließen
Closing;Schließe Closing;Wird geschlossen...
The support team has been notified. Please be patient;Das Support-Team wurde benachrichtigt. Bitte habe Geduld 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 ist jetzt geschlossen. Gib eine Nachricht ein, um es wieder zu öffnen 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 1 Tag 1 day ago;vor einem Tag
is typing;tippt gerade is typing;schreibt...
are typing;tippen gerade are typing;schreiben
No domains available;Keine Domains verfügbar No domains available;Keine Domains verfügbar
Shared domains;Gemeinsame Domains Shared domains;Geteilte Domains
Shared domain;Geteilte Domains Shared domain;Geteilte Domain
Shared domain successfully deleted;Geteilte Domain erfolgreich gelöscht Shared domain successfully deleted;Geteilte Domain wurde erfolgreich gelöscht
Shared domain successfully added;Geteilte Domain erfolgreich hinzugefügt Shared domain successfully added;Geteilte Domain wurde erfolgreich hinzugefügt
Domain name;Domainname Domain name;Domain Name
DNS records for;DNS-Einträge für DNS records for;DNS-Record für
Fetching dns records;Rufe DNS-Einträge ab Fetching dns records;Es wird nach DNS-Records gesucht
No dns records found;Keine DNS-Einträge gefunden No dns records found;Keine DNS-Records gefunden
Content;Inhalt Content;Inhalt
Priority;Priorität Priority;Priorität
Ttl;TTL Ttl;Ttl
Enable cloudflare proxy;Cloudflare Proxy einschalten Enable cloudflare proxy;Cloudflare-Proxy benutzen
CF Proxy;CF Proxy CF Proxy;CF Proxy
days ago; Tage vergangen days ago; Tage
Cancle;Abbrechen Cancle;Abbrechen
An unexpected error occured;Ein unerwarteter Fehler ist aufgetreten An unexpected error occured;Ein unbekannter Fehler ist aufgetreten
Testy;Testy Testy;Testy
Error from cloudflare api;Fehler von der Cloudflare API Error from cloudflare api;Fehler von der Cloudflare-API
Profile;Profil Profile;Profil
No subscription available;Kein Abonnement verfügbar No subscription available;Kein Abo verfügbar
Buy;Kaufen Buy;Kaufen
Redirecting;Leite um Redirecting;Weiterleiten
Apply;Anwenden Apply;Anwenden
Applying code;Code anwenden Applying code;Code Anwenden
Invalid subscription code;Ungültiger Abo-Code Invalid subscription code;Unbekannter Abo-Code
Cancel Subscription;Abonnement kündigen Cancel Subscription;Abo beenden
Active until;Aktiv bis Active until;Aktiv bis
We will send you a notification upon subscription expiration;Wir senden dir eine Benachrichtigung, wenn dein Abonnement abläuft 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 bereits verwendet This token has been already used;Dieser Token wurde schon benutzt
New login for;Neue Anmeldung für 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 Change;Ändern
Changing;Ändere Changing;Wird geändert
Minecraft version;Minecraft-Version Minecraft version;Minecraft Version
Build version;Build-Version Build version;Build Version
Server installation is currently running;Serverinstallation läuft derzeit Server installation is currently running;Der Server wird installiert.
Selected;Ausgewählt Selected;Ausgewählt
Move deleted;Verschiebe das Gelöschte Move deleted;Gelöschtest Bewegen
Delete selected;Lösche das Ausgewählte Delete selected;Ausgewähltes löschen
Log level;Log-Stufe Log level;Log Level
Log message;Logmeldung Log message;Log Message
Time;Zeit Time;Zeit
Version;Version 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 Operating system;Betriebssystem
Moonlight is running on;Moonlight läuft auf Moonlight is running on;Moonlight läuft auf
Memory usage;Speichernutzung Memory usage;Arbeitsspeicher-Auslastung
Moonlight is using;Moonlight verwendet Moonlight is using;Moonlight benutzt
of memory;Speicherplatz of memory;des Arbeitsspeichers
Cpu usage;CPU Verbrauch Cpu usage;CPU Auslastung
Refresh;Aktualisieren Refresh;Neu Laden
Send a message to all users;Eine Nachricht an alle Benutzer senden Send a message to all users;Eine Nachricht an alle Benutzer senden
IP;IP IP;IP
URL;URL URL;URL
Device;Gerät Device;Gerät
Change url;URL ändern Change url;URL Ändern
Message;Nachricht Message;Nachricht
Enter message;Nachricht eingeben 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 Confirm;Bestätigen
Are you sure?;Bist du dir sicher? Are you sure?;Bist du dir sicher
Enter url;URL eingeben Enter url;URL eingeben
An unknown error occured while starting backup deletion;Ein unbekannter Fehler ist beim Starten des Löschvorgangs des Backups aufgetreten An unknown error occured while starting backup deletion;Ein unbekannter Fehler ist während der Backuplöschung aufgetreten
Success;Erfolg Success;erfolgreich
Backup URL successfully copied to your clipboard;Backup-URL erfolgreich in die Zwischenablage kopiert Backup URL successfully copied to your clipboard;Die Backup URL wurde in deine Zwischenablage kopiert
Backup restore started;Wiederherstellung des Backups gestartet Backup restore started;Backup wiederherstellung gestartet
Backup successfully restored;Backup erfolgreich wiederhergestellt Backup successfully restored;Das Backup wurde erfolgreich wiedergeherstellt
Register for;Registrieren für Register for;Registrieren für
Core;Kern Core;Kern
Logs;Logs Logs;Logs
AuditLog;AuditLog AuditLog;AuditLog
SecurityLog;Sicherheitslog SecurityLog;SecurityLog
ErrorLog;Fehlerlog ErrorLog;ErrorLog
Resources;Ressourcen Resources;Resourcen
WinSCP cannot be launched here;WinSCP kann hier nicht gestartet werden WinSCP cannot be launched here;WinSCP kann nicht gestartet werden
Create a new folder;Einen neuen Ordner erstellen Create a new folder;Neuen Ordner erstellen
Enter a name;Namen eingeben Enter a name;Einen Namen eingeben
File upload complete;Datei-Upload abgeschlossen File upload complete;Dateiupload abgeschlossen
New server;Neuer Server New server;Neuer Server
Sessions;Sitzungen Sessions;Sitzungen
New user;Neuer Benutzer New user;Neuer Benutzer
Created at;Erstellt am Created at;Erstellt am
Mail template not found;Mailvorlage nicht gefunden Mail template not found;E-Mail template wurde nicht gefunden
Missing admin permissions. This attempt has been logged ;Fehlende Admin-Berechtigungen. Dieser Versuch wurde geloggt Missing admin permissions. This attempt has been logged ;Fehlende Admin-Rechte. Dieser Versuch wurde aufgezeichnet
Address;Adresse Address;Addresse
City;Stadt City;Stadt
State;Bundesland State;Land
Country;Land Country;Staat
Totp;Totp Totp;Totp
Discord;Discord Discord;Discord
Subscription;Abonnement Subscription;Abonament
None;Keine None;None
No user with this id found;Kein Benutzer mit dieser ID gefunden No user with this id found;Kein Benutzer mit dieser ID gefunden
Back to list;Zurück zur Liste Back to list;Zurück zur liste
New domain;Neue Domain New domain;Neue domain
Reset password;Passwort zurücksetzen Reset password;Password wiederherstellen
Password reset;Passwort zurücksetzen Password reset;Password wiederherstellung
Reset the password of your account;Passwort für dein Konto zurücksetzen Reset the password of your account;Password deines Accounts zurücksetzen
Wrong here?;Falsch hier? Wrong here?;Falsch hier?
A user with this email can not be found;Ein Benutzer mit dieser E-Mail Adresse kann nicht gefunden werden 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;Passwort zurücksetzen erfolgreich. Überprüfe deine Mails Passwort reset successfull. Check your mail;Password wiederherstellung erfolgreich. Überprüfe deine Email
Discord bot;Discord-Bot Discord bot;Discord Bot
New image;Neues Image New image;Neues Image
Description;Beschreibung Description;Beschreibung
Uuid;UUID Uuid;UUID
Enter tag name;Tag-Name eingeben Enter tag name;Tag Namen eingeben
Remove;Entfernen Remove;Entfernen
No tags found;Keine Tags gefunden 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 Tags;Tags
Docker images;Docker-Images Docker images;Docker Images
Default image;Standardimage Default image;Standard Image
Startup command;Startup-Befehl Startup command;Startup Befehl
Install container;Container installieren Install container;Install container
Install entry;Eintrag installieren Install entry;Install entry
Configuration files;Konfigurationsdateien Configuration files;Konfigurationsdateien
Startup detection;Startup-Erkennung Startup detection;Startuperkennung
Stop command;Stopp-Befehl 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 No docker images found;Keine Docker Images gefunden
Key;Schlüssel Key;Schlüssel
Default value;Standardwert Default value;Standardwert
Allocations;Allocations Allocations;Zuweisung
No variables found;Keine Variablen gefunden No variables found;Keine Variablen gefunden
Successfully added image;Image erfolgreich hinzugefügt Successfully added image;Das Image wurde erfolgreich hinzugefügt
Password change for;Passwortänderung für Password change for;Password ändern für
of;von of;von
New node;Neue Node New node;Neues Node
Fqdn;FQDN Fqdn;Fqdn
Cores used;Verwendete CPU Kerne Cores used;Kerne genutzt
used;verwendet used;benutzt
5.15.90.1-microsoft-standard-WSL2 - amd64;5.15.90.1-microsoft-standard-WSL2 - amd64 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 0;0
Docker containers running;Laufende Docker-Container Docker containers running;Laufende Docker Container
details;Details details;Details
1;1 1;1
2;2 2;2
DDos;DDos DDos;DDos
No ddos attacks found;Keine DDos-Angriffe gefunden No ddos attacks found;Keine DDoS gefunden
Node;Node Node;Node
Date;Datum Date;Datum
DDos attack started;DDos-Angriff gestartet DDos attack started;DDos Attacke gestartet
packets;Pakete packets;Pakete
DDos attack stopped;DDos-Angriff gestoppt DDos attack stopped;DDos Attacke gestoppt
packets; Pakete packets; Pakete
Stop all;Alle stoppen Stop all;Alle Stoppen
Kill all;Alle killen Kill all;Allen Killen
Network in;Netzwerk ein Network in;Network in
Network out;Netzwerk raus Network out;Network out
Kill all servers;Alle Server killen Kill all servers;Alle Server Killen
Do you really want to kill all running servers?;Willst du wirklich alle laufenden 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 Change power state for;Power-State ändern für
to;zu to;zu
Stop all servers;Alle Server stoppen 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 ;Verwalten
Manage user ;Benutzer verwalten Manage user ;Benutzer verwalten
Reloading;Lade neu Reloading;Neu Laden...
Update;Aktualisieren Update;Aktualisieren
Updating;Aktualisiere Updating;Wird Aktualisiert...
Successfully updated user;Benutzer erfolgreich aktualisiert Successfully updated user;Benutzer erfolgreich aktualisiert
Discord id;Discord ID Discord id;Discord ID
Discord username;Discord-Benutzername Discord username;Discord Benutzername
Discord discriminator;Discord-Diskriminator Discord discriminator;Discord Tag
The Name field is required.;Das Feld Name ist erforderlich The Name field is required.;Der Name dieses Feldes ist erforderlich
An error occured while logging you in;Beim Einloggen ist ein Fehler aufgetreten 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 eingeben You need to enter an email address;Du musst eine E-Mail-Adresse angeben
You need to enter a password;Du musst ein Passwort eingeben 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 Passwort mit einer länge von mindestens 8 Zeichen 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;Bearbeite Proccessing;Weid verarbeitet...
The FirstName field is required.;Das Feld Vorname ist erforderlich. The FirstName field is required.;Das Vorname-Feld ist erforderlich
The LastName field is required.;Das Feld Nachname ist erforderlich. The LastName field is required.;Das Nachname-Feld ist erforderlich
The Address field is required.;Das Adressfeld ist erforderlich. The Address field is required.;Das Address-Feld ist erforderlich
The City field is required.;Das Feld Stadt ist erforderlich. The City field is required.;Das Stadt-Feld ist erforderlich
The State field is required.;Das Feld Bundesland ist erforderlich. The State field is required.;Das Staat-Feld ist erforderlich
The Country field is required.;Das Feld Land ist erforderlich. The Country field is required.;Das Land-Feld ist erforderlich
Street and house number requered;Straße und Hausnummer erforderlich Street and house number requered;Straße und Hausnummer erforderlich
Max lenght reached;Maximale Länge erreicht Max lenght reached;Maximale Länge erreicht
Server;Server Server;Server
stopped;gestoppt stopped;gestoppt
Cleanups;Cleanups Cleanups;Cleanups
executed;ausgeführt executed;ausgeführt
Used clanup;Benutztes Cleanup Used clanup;Cleanup benutzt
Enable;Aktivieren Enable;Aktivieren
Disabble;Deaktivieren
Disable;Deaktivieren Disable;Deaktivieren
Addons;Addons Addons;Add-ons
Javascript version;Javascript Version Javascript version;Javascript Version
Javascript file;Javascript-Datei Javascript file;Javascript Datei
Select javascript file to execute on start;Javascript-Datei zum Ausführen beim Start auswählen Select javascript file to execute on start;Javascript Datei zum starten auswählen
Submit;Absenden Submit;Einreichen
Processing;Verarbeite Processing;Wird verarbeitet...
Go up;Nach oben Go up;Nach oben gehen
Running cleanup;Laufende Cleanup Server Running cleanup;Cleanup läuft
servers;Server servers;Servers
Select folder to move the file(s) to;Ordner auswählen, in den die Datei(en) verschoben werden sollen Select folder to move the file(s) to;Ordner zum Bewegen der Dateien auswählen
Paper version;Paperversion Paper version;Paper Version
Join2Start;Join2Start Join2Start;Join2Start
Server reset;Server zurücksetzen Server reset;Server zurücksetzen
Reset;Zurücksetzen Reset;Zurücksetzen
Resetting;Setze zurück Resetting;Wird zurückgesetzt
Are you sure you want to reset this server?;Bist du dir sicher, dass du diesen Server zurücksetzen willst? 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 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 Resetting server;Server wird zurückgesetzt...
Deleted file;Gelöschte Datei Deleted file;Datei gelöscht
Reinstalling server;Installiere den Server neu Reinstalling server;Server wird reinstalliert
Uploading files;Lade Dateien hoch Uploading files;Dateien wurden hochgeladen
complete;vollständig complete;vollständig
Upload complete;Upload vollständig Upload complete;Upload komplett
Security;Sicherheit Security;Sicherheit
Subscriptions;Abonnements Subscriptions;Abonaments
2fa Code;2FA Code 2fa Code;2FA Code
Your account is secured with 2fa;Dein Konto ist mit 2FA gesichert Your account is secured with 2fa;Dein Account wird mit 2-FA gesichert
anyone write a fancy text here?;Kann hier jemand einen netten Text schreiben? Ja sicher: Ich war hier. - Dannyx anyone write a fancy text here?;hier einen schönen Text schreiben?
Activate 2fa;2FA aktivieren Activate 2fa;2-FA Aktivieren
2fa apps;2FA Apps 2fa apps;2-FA Apps
Use an app like ;Benutze eine App wie Use an app like ;Benutze eine App wie
or;oder or;oder
and scan the following QR Code;und scanne den folgenden QR-Code 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 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: 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 beenden Finish activation;Aktivierung fertig
2fa Code requiered;2FA-Code erforderlich 2fa Code requiered;2-FA Code erforderlich
New password;Neues Passwort New password;Neues Password
Secure your account;Sichere dein Konto 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.;2FA fügt eine weitere Sicherheitsebene zu deinem Konto hinzu. Du musst dann noch einen 6-stelligen Code eingeben, um dich anzumelden. 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 Abonnement New subscription;Neues Abonament
You need to enter a name;Du musst einen Namen eingeben You need to enter a name;Du musst einen Namen eingeben
You need to enter a description;Du musst eine Beschreibung eingeben You need to enter a description;Du musst eine Beschreibung eigeben
Add new limit;Neues Limit hinzufügen Add new limit;Ein neues Limit hinzufügen
Create subscription;Abonnement erstellen Create subscription;Abonament erstellen
Options;Optionen Options;Optionen
Amount;Betrag Amount;Betrag
Do you really want to delete it?;Willst du es wirklich löschen? Do you really want to delete it?;Möchtes du es wirklich löschen?
Loading your subscription;Abonnement laden Loading your subscription;Dein Abonament wird geladen
Searching for deploy node;Suche nach einer Deploy Node Searching for deploy node;#Empty#
Searching for available images;Suche nach verfügbaren Images Searching for available images;Nach verfügbaren Images wird gesucht
Server details;Server-Details Server details;Server Details
Configure your server;Konfiguriere deinen Server Configure your server;Deinen Server konfigurieren
Default;Standard Default;Standart
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 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;Persönliche Informationen Personal information;Prsönliche Informationen
Enter code;Code eingeben Enter code;Code eingeben
Server rename;Server umbenennen Server rename;Server Umbenennen
Create code;Code erstellen Create code;Code erstellen
Save subscription;Abonnement speichern Save subscription;Abonament speichern
Enter your information;Gib deine Informationen ein Enter your information;Informationen eingeben
You need to enter your full name in order to use moonlight;Du musst deinen vollständigen Namen eingeben, um Moonlight zu verwenden 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;Keine Node gefunden No node found;Kein Node gefunden
No node found to deploy to found;Kein Node zum Deployen gefunden No node found to deploy to found;#Empty#
Node offline;Node offline 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 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 nicht gefunden 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 kann nicht gefunden werden oder du hast keinen Zugriff auf diesen Server 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 Compress;Komprimieren
Decompress;Dekomprimieren Decompress;De-Komprimieren
Moving;Verschieben Moving;Bewegen...
Compressing;Komprimieren Compressing;Komprimieren...
selected;ausgewählt selected;Ausgewählt
New website;Neue Website New website;Neue Website
Plesk servers;Plesk-Server Plesk servers;Plesk Servers
Base domain;Basisdomain Base domain;Base Domain
Plesk server;Plesk-Server Plesk server;Plesk Server
Ftp;FTP Ftp;FTP
No SSL certificate found;Kein SSL-Zertifikat gefunden No SSL certificate found;Keine SSL-Zertifikate gefunden
Ftp Host;FTP Host Ftp Host;FTP Host
Ftp Port;FTP Port Ftp Port;FTP Port
Ftp Username;FTP Benutzername Ftp Username;FTP Username
Ftp Password;FTP Passwort Ftp Password;FTP Password
Use;Verwenden Use;Benutzen
SSL Certificates;SSL-Zertifikate SSL Certificates;SSL Zertifikate
SSL certificates;SSL-Zertifikate SSL certificates;SSL Zertifikate
Issue certificate;Zertifikat ausstellen lassen Issue certificate;SSL-Zertifikat Ausgeben
New plesk server;Neuer Plesk-Server New plesk server;Neuer Plesk Server
Api url;API-URL Api url;API URL
Host system offline;Hostsystem offline Host system offline;Host System Offline
The host system the website is running on is currently offline;Das Hostsystem, auf dem die Website läuft, ist derzeit 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 SSL certificates found;Keine SSL-Zertifikate gefunden
No databases found for this website;Keine Datenbanken für diese Website gefunden No databases found for this website;Dieser Website konnten keine Datenbanken zugeordnet werden
The name should be at least 8 characters long;Der Name muss mindestens 8 Zeichen lang sein 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 darf nur aus Kleinbuchstaben und Zahlen bestehen The name should only contain of lower case characters and numbers;Der Name sollte nur Kleinbuchstaben und Zahlen enthalten
Error from plesk;Fehler von Plesk Error from plesk;Error von Plesk
Host;Host Host;Host
Username;Benutzername 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 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 Feld Benutzer ist erforderlich The User field is required.;Das Benutzer-Feld ist erforderlich
You need to specify a owner;Du musst einen Besitzer angeben You need to specify a owner;Du musst einen Server-Besitzer angeben
You need to specify a image;Du musst ein Image angeben You need to specify a image;You need to specify a image
Api Url;API URL Api Url;API URL
Api Key;API Key Api Key;Api Key
Duration;Dauer 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 Copied code to clipboard;Code in die Zwischenablage kopiert
Invalid or expired subscription code;Ungültiger oder abgelaufener Abo-Code Invalid or expired subscription code;Ungültiger oder Abgelaufener Abo-Code
Current subscription;Aktuelles Abonnement Current subscription;Dein Abonament
You need to specify a server image;Du musst ein Server-Image angeben You need to specify a server image;Du musst ein Image angeben
CPU;CPU CPU;CPU
Hour;Stunde Hour;Stunde
Day;Tag Day;Tag
Month;Monat Month;Monat
Year;Jahr Year;Jahr
All time;Seit Beginn All time;Für immer
This function is not implemented;Diese Funktion ist nicht eingebaut This function is not implemented;Diese Funktion wurde noch nicht hinzugefügt
Domain details;Domaindetails Domain details;Domain Details
Configure your domain;Konfiguriere deine Domain Configure your domain;Deine Domain konfigurieren
You reached the maximum amount of domains in your subscription;Du hast die maximale Anzahl an Domains in deinem Abonnement erreicht 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 gemeinsame Domain angeben 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 Namen existiert bereits für diese gemeinsame Domain 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 Feld E-Mail ist erforderlich. The Email field is required.;Das E-Mail-Feld ist erforderlich
The Password field is required.;Das Feld Passwort ist erforderlich. The Password field is required.;Das Password-Feld ist erforderlich
The ConfirmPassword field is required.;Das Feld Passwort bestätigen ist erforderlich. The ConfirmPassword field is required.;Das Password-Bestätigen-Feld ist erforderlich
Passwords need to match;Passwörter müssen übereinstimmen Passwords need to match;Die Passwörter müssen übereinstimmen
Cleanup exception;Cleanup Ausnahme Cleanup exception;Cleanup ausnahme
No shared domain found;Keine gemeinsame Domain gefunden No shared domain found;Keine Shared-Domain gefunden
Searching for deploy plesk server;Suche nach einem Deploy Plesk Server Searching for deploy plesk server;Suchen um den Plesk Server aufzusetzen
No plesk server found;Kein Plesk Server gefunden No plesk server found;Kein Plesk Server gefunden
No plesk server found to deploy to;Kein Plesk Server für die Bereitstellung gefunden No plesk server found to deploy to;Keinen Plesk Server zum Aufsetzen gefunden
No node found to deploy to;Keine Node für die Bereitstellung gefunden No node found to deploy to;Kein Node zum Aufsetzen
Website details;Webseiten-Details Website details;Website Details
Configure your website;Konfiguriere deine Website 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 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 darf nur aus Kleinbuchstaben bestehen The name should only consist of lower case characters;Der Name sollte nur aus Kleinbuchstaben bestehen
News;Neuigkeiten News;Neuigkeiten
Title...;Titel... Title...;Titel...
Enter text...;Text eingeben... Enter text...;Text einfügen...
Saving...;Speichere... Saving...;Wird gespeichert...
Deleting...;Lösche... Deleting...;Wird gelöscht...
Delete post;Beitrag löschen Delete post;Post löschen
Do you really want to delete the post ";Willst du den Beitrag wirklich löschen " Do you really want to delete the post ";Post löschen? "
You have no domains;Du hast keine Domains 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 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 Guest;Gast
You need a domain;Du brauchst eine Domain You need a domain;Du brauchts eine Domain
New post;Neuer Post New post;Neuer Post
New entry;Neuer Eintrag 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