From 601ba7d3723713431a59f37c9feb1856166446cb Mon Sep 17 00:00:00 2001 From: Marcel Baumgartner Date: Thu, 13 Apr 2023 16:29:40 +0200 Subject: [PATCH] Implemented new storage system, path builder, default resources, fixed lang stuff --- .gitignore | 4 +- Moonlight/App/Helpers/PathBuilder.cs | 28 + Moonlight/App/Helpers/SmartTranslateHelper.cs | 4 +- .../Api/Moonlight/ResourcesController.cs | 8 +- Moonlight/App/Services/ConfigService.cs | 34 +- Moonlight/App/Services/StorageService.cs | 58 ++ Moonlight/Dockerfile | 2 + Moonlight/Moonlight.csproj | 2 +- Moonlight/Program.cs | 1 + Moonlight/README.md | 13 + .../Shared/Views/Admin/Sys/Resources.razor | 5 +- Moonlight/defaultstorage/configs/config.json | 78 +++ .../resources/lang/de_de.lang | 0 .../resources/lang/en_us.lang | 0 .../resources/mail/login.html | 0 .../resources/mail/passwordChange.html | 0 .../resources/mail/passwordReset.html | 0 .../resources/mail/register.html | 0 .../resources/public/images/logo.svg | 0 .../resources/public/images/logolong.png | Bin Moonlight/deleteme_resources/lang/de_de.lang | 567 ++++++++++++++++++ Moonlight/deleteme_resources/lang/en_us.lang | 86 +++ Moonlight/deleteme_resources/mail/login.html | 53 ++ .../mail/passwordChange.html | 53 ++ .../mail/passwordReset.html | 54 ++ .../deleteme_resources/mail/register.html | 50 ++ .../deleteme_resources/public/images/logo.svg | 14 + .../public/images/logolong.png | Bin 0 -> 12410 bytes 28 files changed, 1096 insertions(+), 18 deletions(-) create mode 100644 Moonlight/App/Helpers/PathBuilder.cs create mode 100644 Moonlight/App/Services/StorageService.cs create mode 100644 Moonlight/README.md create mode 100644 Moonlight/defaultstorage/configs/config.json rename Moonlight/{ => defaultstorage}/resources/lang/de_de.lang (100%) rename Moonlight/{ => defaultstorage}/resources/lang/en_us.lang (100%) rename Moonlight/{ => defaultstorage}/resources/mail/login.html (100%) rename Moonlight/{ => defaultstorage}/resources/mail/passwordChange.html (100%) rename Moonlight/{ => defaultstorage}/resources/mail/passwordReset.html (100%) rename Moonlight/{ => defaultstorage}/resources/mail/register.html (100%) rename Moonlight/{ => defaultstorage}/resources/public/images/logo.svg (100%) rename Moonlight/{ => defaultstorage}/resources/public/images/logolong.png (100%) create mode 100644 Moonlight/deleteme_resources/lang/de_de.lang create mode 100644 Moonlight/deleteme_resources/lang/en_us.lang create mode 100644 Moonlight/deleteme_resources/mail/login.html create mode 100644 Moonlight/deleteme_resources/mail/passwordChange.html create mode 100644 Moonlight/deleteme_resources/mail/passwordReset.html create mode 100644 Moonlight/deleteme_resources/mail/register.html create mode 100644 Moonlight/deleteme_resources/public/images/logo.svg create mode 100644 Moonlight/deleteme_resources/public/images/logolong.png diff --git a/.gitignore b/.gitignore index 4ec99a85..7487adb3 100644 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,6 @@ _UpgradeReport_Files/ Thumbs.db Desktop.ini .DS_Store -.idea/.idea.Moonlight/.idea/discord.xml \ No newline at end of file +.idea/.idea.Moonlight/.idea/discord.xml + +storage/ \ No newline at end of file diff --git a/Moonlight/App/Helpers/PathBuilder.cs b/Moonlight/App/Helpers/PathBuilder.cs new file mode 100644 index 00000000..0b3e98bd --- /dev/null +++ b/Moonlight/App/Helpers/PathBuilder.cs @@ -0,0 +1,28 @@ +namespace Moonlight.App.Helpers; + +public class PathBuilder +{ + public static string Dir(params string[] parts) + { + var res = ""; + + foreach (var part in parts) + { + res += part + Path.DirectorySeparatorChar; + } + + return res; + } + + public static string File(params string[] parts) + { + var res = ""; + + foreach (var part in parts) + { + res += part + (part == parts.Last() ? "" : Path.DirectorySeparatorChar); + } + + return res; + } +} \ No newline at end of file diff --git a/Moonlight/App/Helpers/SmartTranslateHelper.cs b/Moonlight/App/Helpers/SmartTranslateHelper.cs index 309b6300..6ce8d656 100644 --- a/Moonlight/App/Helpers/SmartTranslateHelper.cs +++ b/Moonlight/App/Helpers/SmartTranslateHelper.cs @@ -8,7 +8,7 @@ public class SmartTranslateHelper { Languages = new(); - foreach (var file in Directory.GetFiles("resources/lang")) + foreach (var file in Directory.GetFiles(PathBuilder.Dir("storage", "resources", "lang"))) { if (Path.GetExtension(file) == ".lang") { @@ -40,7 +40,7 @@ public class SmartTranslateHelper { Languages[langKey].Add(content, content); - File.WriteAllLines($"resources/lang/{langKey}.lang", GenerateData(Languages[langKey])); + File.WriteAllLines(PathBuilder.File("storage", "resources", "lang", $"{langKey}.lang"), GenerateData(Languages[langKey])); } return Languages[langKey][content]; diff --git a/Moonlight/App/Http/Controllers/Api/Moonlight/ResourcesController.cs b/Moonlight/App/Http/Controllers/Api/Moonlight/ResourcesController.cs index 52750c40..254a0b5a 100644 --- a/Moonlight/App/Http/Controllers/Api/Moonlight/ResourcesController.cs +++ b/Moonlight/App/Http/Controllers/Api/Moonlight/ResourcesController.cs @@ -1,5 +1,6 @@ using Logging.Net; using Microsoft.AspNetCore.Mvc; +using Moonlight.App.Helpers; using Moonlight.App.Models.Misc; using Moonlight.App.Services.LogServices; @@ -28,14 +29,13 @@ public class ResourcesController : Controller return NotFound(); } - if (System.IO.File.Exists($"resources/public/images/{name}")) + if (System.IO.File.Exists(PathBuilder.File("storage", "resources", "public", "images", name))) { - var fs = new FileStream($"resources/public/images/{name}", FileMode.Open); + var fs = new FileStream(PathBuilder.File("storage", "resources", "public", "images", name), FileMode.Open); return File(fs, MimeTypes.GetMimeType(name), name); } - - Logger.Debug("404 on resources"); + return NotFound(); } } \ No newline at end of file diff --git a/Moonlight/App/Services/ConfigService.cs b/Moonlight/App/Services/ConfigService.cs index ed124f62..27bc5bc7 100644 --- a/Moonlight/App/Services/ConfigService.cs +++ b/Moonlight/App/Services/ConfigService.cs @@ -7,26 +7,44 @@ namespace Moonlight.App.Services; public class ConfigService : IConfiguration { + private readonly StorageService StorageService; + private IConfiguration Configuration; public bool DebugMode { get; private set; } = false; - - public ConfigService() + + public ConfigService(StorageService storageService) { - Configuration = new ConfigurationBuilder().AddJsonStream( - new MemoryStream(Encoding.ASCII.GetBytes(File.ReadAllText("..\\..\\appsettings.json"))) - ).Build(); + StorageService = storageService; + StorageService.EnsureCreated(); + + Reload(); // Env vars var debugVar = Environment.GetEnvironmentVariable("ML_DEBUG"); if (debugVar != null) DebugMode = bool.Parse(debugVar); - - if(DebugMode) + + if (DebugMode) Logger.Debug("Debug mode enabled"); } - + + public void Reload() + { + Logger.Info($"Reading config from '{PathBuilder.File("storage", "configs", "config.json")}'"); + + Configuration = new ConfigurationBuilder().AddJsonStream( + new MemoryStream(Encoding.ASCII.GetBytes( + File.ReadAllText( + PathBuilder.File("storage", "configs", "config.json") + ) + ) + )).Build(); + + Logger.Info("Reloaded configuration file"); + } + public IEnumerable GetChildren() { return Configuration.GetChildren(); diff --git a/Moonlight/App/Services/StorageService.cs b/Moonlight/App/Services/StorageService.cs new file mode 100644 index 00000000..455e5329 --- /dev/null +++ b/Moonlight/App/Services/StorageService.cs @@ -0,0 +1,58 @@ +using Logging.Net; +using Moonlight.App.Helpers; + +namespace Moonlight.App.Services; + +public class StorageService +{ + public StorageService() + { + EnsureCreated(); + } + + public void EnsureCreated() + { + Directory.CreateDirectory(PathBuilder.Dir("storage", "uploads")); + Directory.CreateDirectory(PathBuilder.Dir("storage", "configs")); + Directory.CreateDirectory(PathBuilder.Dir("storage", "resources")); + + if(IsEmpty(PathBuilder.Dir("storage", "resources"))) + { + Logger.Info("Default resources not found. Copying default resources"); + + CopyFilesRecursively( + PathBuilder.Dir("defaultstorage", "resources"), + PathBuilder.Dir("storage", "resources") + ); + } + + if (IsEmpty(PathBuilder.Dir("storage", "configs"))) + { + Logger.Info("Default configs not found. Copying default configs"); + + CopyFilesRecursively( + PathBuilder.Dir("defaultstorage", "configs"), + PathBuilder.Dir("storage", "configs") + ); + } + } + + private bool IsEmpty(string path) + { + return !Directory.EnumerateFiles(path).Any(); + } + private static void CopyFilesRecursively(string sourcePath, string targetPath) + { + //Now Create all of the directories + foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories)) + { + Directory.CreateDirectory(dirPath.Replace(sourcePath, targetPath)); + } + + //Copy all the files & Replaces any files with the same name + foreach (string newPath in Directory.GetFiles(sourcePath, "*.*",SearchOption.AllDirectories)) + { + File.Copy(newPath, newPath.Replace(sourcePath, targetPath), true); + } + } +} \ No newline at end of file diff --git a/Moonlight/Dockerfile b/Moonlight/Dockerfile index 8f612a2c..4fecfff7 100644 --- a/Moonlight/Dockerfile +++ b/Moonlight/Dockerfile @@ -19,4 +19,6 @@ RUN dotnet publish "Moonlight.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . +RUN mkdir /app/storage +RUN rm -r /app/storage/* ENTRYPOINT ["dotnet", "Moonlight.dll"] \ No newline at end of file diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index 1c684c61..e2a9aa4a 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -70,7 +70,7 @@ - + diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index 8651fe58..76e01893 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -74,6 +74,7 @@ namespace Moonlight // Services builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/Moonlight/README.md b/Moonlight/README.md new file mode 100644 index 00000000..264af4c5 --- /dev/null +++ b/Moonlight/README.md @@ -0,0 +1,13 @@ +### Some explanations + +defaultstorage: + +This directory is for the default assets of a moonlight instance, e.g. lang files, images etc + +storage: + +This directory is empty in fresh moonlight instances and will be populated with example config upon first run. +Before using moonlight this config file has to be modified. +Also resources are going to be copied from the default storage to this storage. +To access files in this storage we recommend to use the PathBuilder functions to ensure cross platform compatibility. +The storage directory should be mounted to a specific path when using docker container so when the container is replaced/rebuild your storage will not be modified \ No newline at end of file diff --git a/Moonlight/Shared/Views/Admin/Sys/Resources.razor b/Moonlight/Shared/Views/Admin/Sys/Resources.razor index 28a53119..982b4064 100644 --- a/Moonlight/Shared/Views/Admin/Sys/Resources.razor +++ b/Moonlight/Shared/Views/Admin/Sys/Resources.razor @@ -3,12 +3,13 @@ @using Moonlight.Shared.Components.FileManagerPartials @using Moonlight.App.Helpers.Files @using Moonlight.Shared.Components.Navigations +@using Moonlight.App.Helpers
- - + +
\ No newline at end of file diff --git a/Moonlight/defaultstorage/configs/config.json b/Moonlight/defaultstorage/configs/config.json new file mode 100644 index 00000000..6e9ff6dd --- /dev/null +++ b/Moonlight/defaultstorage/configs/config.json @@ -0,0 +1,78 @@ +{ + "Moonlight": { + "AppUrl": "http://your-moonlight-url.test", + "Database": { + "Database": "database_name", + "Host": "your-moonlight-database-host.de", + "Password": "s3cr3t", + "Port": "10324", + "Username": "user_name" + }, + "DiscordBot": { + "Enable": "True", + "Token": "Discord.Token.Here" + }, + "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": { + "Discord": { + "ClientId": "10324", + "ClientSecret": "s3cr3t", + "Enable": "True" + }, + "Google": { + "ClientId": "xyz.apps.googleusercontent.com", + "ClientSecret": "s3cr3t", + "Enable": "True" + }, + "EnableOverrideUrl": "True", + "OverrideUrl": "http://your-moonlight-url.test" + }, + "Security": { + "Token": "RANDOM UUID HERE" + }, + "SetupComplete": "True", + "SupportChat": { + "Enabled": "True" + }, + "Mail": { + "Email": "no-reply@mycoolproject.de", + "Server": "mycoolproject.de", + "Password": "s3cr3t", + "Port": 25 + }, + "Cleanup": { + "Cpu": 90, + "Memory": 8192, + "Wait": 15, + "Uptime": 6, + "Enable": false, + "MinUptime": 10 + }, + "Subscriptions": { + "_comment": "Not implemented", + "SellPass": { + "Enable": false, + "Url": "" + } + } + } +} \ No newline at end of file diff --git a/Moonlight/resources/lang/de_de.lang b/Moonlight/defaultstorage/resources/lang/de_de.lang similarity index 100% rename from Moonlight/resources/lang/de_de.lang rename to Moonlight/defaultstorage/resources/lang/de_de.lang diff --git a/Moonlight/resources/lang/en_us.lang b/Moonlight/defaultstorage/resources/lang/en_us.lang similarity index 100% rename from Moonlight/resources/lang/en_us.lang rename to Moonlight/defaultstorage/resources/lang/en_us.lang diff --git a/Moonlight/resources/mail/login.html b/Moonlight/defaultstorage/resources/mail/login.html similarity index 100% rename from Moonlight/resources/mail/login.html rename to Moonlight/defaultstorage/resources/mail/login.html diff --git a/Moonlight/resources/mail/passwordChange.html b/Moonlight/defaultstorage/resources/mail/passwordChange.html similarity index 100% rename from Moonlight/resources/mail/passwordChange.html rename to Moonlight/defaultstorage/resources/mail/passwordChange.html diff --git a/Moonlight/resources/mail/passwordReset.html b/Moonlight/defaultstorage/resources/mail/passwordReset.html similarity index 100% rename from Moonlight/resources/mail/passwordReset.html rename to Moonlight/defaultstorage/resources/mail/passwordReset.html diff --git a/Moonlight/resources/mail/register.html b/Moonlight/defaultstorage/resources/mail/register.html similarity index 100% rename from Moonlight/resources/mail/register.html rename to Moonlight/defaultstorage/resources/mail/register.html diff --git a/Moonlight/resources/public/images/logo.svg b/Moonlight/defaultstorage/resources/public/images/logo.svg similarity index 100% rename from Moonlight/resources/public/images/logo.svg rename to Moonlight/defaultstorage/resources/public/images/logo.svg diff --git a/Moonlight/resources/public/images/logolong.png b/Moonlight/defaultstorage/resources/public/images/logolong.png similarity index 100% rename from Moonlight/resources/public/images/logolong.png rename to Moonlight/defaultstorage/resources/public/images/logolong.png diff --git a/Moonlight/deleteme_resources/lang/de_de.lang b/Moonlight/deleteme_resources/lang/de_de.lang new file mode 100644 index 00000000..eb4bb032 --- /dev/null +++ b/Moonlight/deleteme_resources/lang/de_de.lang @@ -0,0 +1,567 @@ +Open support;Open support +About us;About us +Imprint;Imprint +Privacy;Privacy +Login;Login +Register;Register +Insert brand name...;Insert brand name... +Save and continue;Save and continue +Saving;Saving +Configure basics;Configure basics +Brand name;Brand name +test;test +Insert first name...;Insert first name... +Insert last name...;Insert last name... +Insert email address...;Insert email address... +Add;Add +Adding...;Adding... +Add admin accounts;Add admin accounts +First name;First name +Last name;Last name +Email address;Email address +Enter password;Enter password +Next;Next +Back;Back +Configure features;Configure features +Support chat;Support chat +Finish;Finish +Finalize installation;Finalize installation +Moonlight basic settings successfully configured;Moonlight basic settings successfully configured +Ooops. This page is crashed;Ooops. This page is crashed +This page is crashed. The error has been reported to the moonlight team. Meanwhile you can try reloading the page;This page is crashed. The error has been reported to the moonlight team. Meanwhile you can try reloading the page +Setup complete;Setup complete +It looks like this moonlight instance is ready to go;It looks like this moonlight instance is ready to go +User successfully created;User successfully created +Ooops. Your moonlight client is crashed;Ooops. Your moonlight client is crashed +This error has been reported to the moonlight team;This error has been reported to the moonlight team +Sign In;Sign In +Sign in to start with moonlight;Sign in to start with moonlight +Sign in with Discord;Sign in with Discord +Or with email;Or with email +Forgot password?;Forgot password? +Sign-in;Sign-in +Not registered yet?;Not registered yet? +Sign up;Sign up +Authenticating;Authenticating +Sign in with Google;Sign in with Google +Working;Working +Error;Error +Email and password combination not found;Email and password combination not found +Email;Email +Password;Password +Account settings;Account settings +Logout;Logout +Dashboard;Dashboard +Order;Order +Website;Website +Database;Database +Domain;Domain +Servers;Servers +Websites;Websites +Databases;Databases +Domains;Domains +Changelog;Changelog +Firstname;Firstname +Lastname;Lastname +Repeat password;Repeat password +Sign Up;Sign Up +Sign up to start with moonlight;Sign up to start with moonlight +Sign up with Discord;Sign up with Discord +Sign up with Google;Sign up with Google +Sign-up;Sign-up +Already registered?;Already registered? +Sign in;Sign in +Create something new;Create something new +Create a gameserver;Create a gameserver +A new gameserver in just a few minutes;A new gameserver in just a few minutes +Create a database;Create a database +A quick way to store your data and manage it from all around the world;A quick way to store your data and manage it from all around the world +Manage your services;Manage your services +Manage your gameservers;Manage your gameservers +Adjust your gameservers;Adjust your gameservers +Manage your databases;Manage your databases +Insert, delete and update the data in your databases;Insert, delete and update the data in your databases +Create a website;Create a website +Make your own websites with a webspace;Make your own websites with a webspace +Create a domain;Create a domain +Make your servvices accessible throught your own domain;Make your servvices accessible throught your own domain +Manage your websites;Manage your websites +Modify the content of your websites;Modify the content of your websites +Manage your domains;Manage your domains +Add, edit and delete dns records;Add, edit and delete dns records +Admin;Admin +System;System +Overview;Overview +Manager;Manager +Cleanup;Cleanup +Nodes;Nodes +Images;Images +aaPanel;aaPanel +Users;Users +Support;Support +Statistics;Statistics +No nodes found. Start with adding a new node;No nodes found. Start with adding a new node +Nodename;Nodename +FQDN;FQDN +Create;Create +Creating;Creating +Http port;Http port +Sftp port;Sftp port +Moonlight daemon port;Moonlight daemon port +SSL;SSL +CPU Usage;CPU Usage +In %;In % +Memory;Memory +Used / Available memory;Used / Available memory +Storage;Storage +Available storage;Available storage +Add a new node;Add a new node +Delete;Delete +Deleting;Deleting +Edit;Edit +Token Id;Token Id +Token;Token +Save;Save +Setup;Setup +Open a ssh connection to your node and enter;Open a ssh connection to your node and enter +and paste the config below. Then press STRG+O and STRG+X to save;and paste the config below. Then press STRG+O and STRG+X to save +Before configuring this node, install the daemon;Before configuring this node, install the daemon +Delete this node?;Delete this node? +Do you really want to delete this node;Do you really want to delete this node +Yes;Yes +No;No +Status;Status +Adding;Adding +Port;Port +Id;Id +Manage;Manage +Create new server;Create new server +No servers found;No servers found +Server name;Server name +Cpu cores;Cpu cores +Disk;Disk +Image;Image +Override startup;Override startup +Docker image;Docker image +CPU Cores (100% = 1 Core);CPU Cores (100% = 1 Core) +Server successfully created;Server successfully created +Name;Name +Cores;Cores +Owner;Owner +Value;Value +An unknown error occured;An unknown error occured +No allocation found;No allocation found +Identifier;Identifier +UuidIdentifier;UuidIdentifier +Override startup command;Override startup command +Loading;Loading +Offline;Offline +Connecting;Connecting +Start;Start +Restart;Restart +Stop;Stop +Shared IP;Shared IP +Server ID;Server ID +Cpu;Cpu +Console;Console +Files;Files +Backups;Backups +Network;Network +Plugins;Plugins +Settings;Settings +Enter command;Enter command +Execute;Execute +Checking disk space;Checking disk space +Updating config files;Updating config files +Checking file permissions;Checking file permissions +Downloading server image;Downloading server image +Downloaded server image;Downloaded server image +Starting;Starting +Online;Online +Kill;Kill +Stopping;Stopping +Search files and folders;Search files and folders +Launch WinSCP;Launch WinSCP +New folder;New folder +Upload;Upload +File name;File name +File size;File size +Last modified;Last modified +Cancel;Cancel +Canceling;Canceling +Running;Running +Loading backups;Loading backups +Started backup creation;Started backup creation +Backup is going to be created;Backup is going to be created +Rename;Rename +Move;Move +Archive;Archive +Unarchive;Unarchive +Download;Download +Starting download;Starting download +Backup successfully created;Backup successfully created +Restore;Restore +Copy url;Copy url +Backup deletion started;Backup deletion started +Backup successfully deleted;Backup successfully deleted +Primary;Primary +This feature is currently not available;This feature is currently not available +Send;Send +Sending;Sending +Welcome to the support chat. Ask your question here and we will help you;Welcome to the support chat. Ask your question here and we will help you + minutes ago; minutes ago +just now;just now +less than a minute ago;less than a minute ago +1 hour ago;1 hour ago +1 minute ago;1 minute ago +Failed;Failed + hours ago; hours ago +Open tickets;Open tickets +Actions;Actions +No support ticket is currently open;No support ticket is currently open +User information;User information +Close ticket;Close ticket +Closing;Closing +The support team has been notified. Please be patient;The support team has been notified. Please be patient +The ticket is now closed. Type a message to open it again;The ticket is now closed. Type a message to open it again +1 day ago;1 day ago +is typing;is typing +are typing;are typing +No domains available;No domains available +Shared domains;Shared domains +Shared domain;Shared domain +Shared domain successfully deleted;Shared domain successfully deleted +Shared domain successfully added;Shared domain successfully added +Domain name;Domain name +DNS records for;DNS records for +Fetching dns records;Fetching dns records +No dns records found;No dns records found +Content;Content +Priority;Priority +Ttl;Ttl +Enable cloudflare proxy;Enable cloudflare proxy +CF Proxy;CF Proxy + days ago; days ago +Cancle;Cancle +An unexpected error occured;An unexpected error occured +Testy;Testy +Error from cloudflare api;Error from cloudflare api +Profile;Profile +No subscription available;No subscription available +Buy;Buy +Redirecting;Redirecting +Apply;Apply +Applying code;Applying code +Invalid subscription code;Invalid subscription code +Cancel Subscription;Cancel Subscription +Active until;Active until +We will send you a notification upon subscription expiration;We will send you a notification upon subscription expiration +This token has been already used;This token has been already used +New login for;New login for +No records found for this day;No records found for this day +Change;Change +Changing;Changing +Minecraft version;Minecraft version +Build version;Build version +Server installation is currently running;Server installation is currently running +Selected;Selected +Move deleted;Move deleted +Delete selected;Delete selected +Log level;Log level +Log message;Log message +Time;Time +Version;Version +You are running moonlight version;You are running moonlight version +Operating system;Operating system +Moonlight is running on;Moonlight is running on +Memory usage;Memory usage +Moonlight is using;Moonlight is using +of memory;of memory +Cpu usage;Cpu usage +Refresh;Refresh +Send a message to all users;Send a message to all users +IP;IP +URL;URL +Device;Device +Change url;Change url +Message;Message +Enter message;Enter message +Enter the message to send;Enter the message to send +Confirm;Confirm +Are you sure?;Are you sure? +Enter url;Enter url +An unknown error occured while starting backup deletion;An unknown error occured while starting backup deletion +Success;Success +Backup URL successfully copied to your clipboard;Backup URL successfully copied to your clipboard +Backup restore started;Backup restore started +Backup successfully restored;Backup successfully restored +Register for;Register for +Core;Core +Logs;Logs +AuditLog;AuditLog +SecurityLog;SecurityLog +ErrorLog;ErrorLog +Resources;Resources +WinSCP cannot be launched here;WinSCP cannot be launched here +Create a new folder;Create a new folder +Enter a name;Enter a name +File upload complete;File upload complete +New server;New server +Sessions;Sessions +New user;New user +Created at;Created at +Mail template not found;Mail template not found +Missing admin permissions. This attempt has been logged ;) +Address;Address +City;City +State;State +Country;Country +Totp;Totp +Discord;Discord +Subscription;Subscription +None;None +No user with this id found;No user with this id found +Back to list;Back to list +New domain;New domain +Reset password;Reset password +Password reset;Password reset +Reset the password of your account;Reset the password of your account +Wrong here?;Wrong here? +A user with this email can not be found;A user with this email can not be found +Passwort reset successfull. Check your mail;Passwort reset successfull. Check your mail +Discord bot;Discord bot +New image;New image +Description;Description +Uuid;Uuid +Enter tag name;Enter tag name +Remove;Remove +No tags found;No tags found +Enter docker image name;Enter docker image name +Tags;Tags +Docker images;Docker images +Default image;Default image +Startup command;Startup command +Install container;Install container +Install entry;Install entry +Configuration files;Configuration files +Startup detection;Startup detection +Stop command;Stop command +Successfully saved image;Successfully saved image +No docker images found;No docker images found +Key;Key +Default value;Default value +Allocations;Allocations +No variables found;No variables found +Successfully added image;Successfully added image +Password change for;Password change for +of;of +New node;New node +Fqdn;Fqdn +Cores used;Cores used +used;used +5.15.90.1-microsoft-standard-WSL2 - amd64;5.15.90.1-microsoft-standard-WSL2 - amd64 +Host system information;Host system information +0;0 +Docker containers running;Docker containers running +details;details +1;1 +2;2 +DDos;DDos +No ddos attacks found;No ddos attacks found +Node;Node +Date;Date +DDos attack started;DDos attack started +packets;packets +DDos attack stopped;DDos attack stopped + packets; packets +Stop all;Stop all +Kill all;Kill all +Network in;Network in +Network out;Network out +Kill all servers;Kill all servers +Do you really want to kill all running servers?;Do you really want to kill all running servers? +Change power state for;Change power state for +to;to +Stop all servers;Stop all servers +Do you really want to stop all running servers?;Do you really want to stop all running servers? +Manage ;Manage +Manage user ;Manage user +Reloading;Reloading +Update;Update +Updating;Updating +Successfully updated user;Successfully updated user +Discord id;Discord id +Discord username;Discord username +Discord discriminator;Discord discriminator +The Name field is required.;The Name field is required. +An error occured while logging you in;An error occured while logging you in +You need to enter an email address;You need to enter an email address +You need to enter a password;You need to enter a password +You need to enter a password with minimum 8 characters in lenght;You need to enter a password with minimum 8 characters in lenght +Proccessing;Proccessing +The FirstName field is required.;The FirstName field is required. +The LastName field is required.;The LastName field is required. +The Address field is required.;The Address field is required. +The City field is required.;The City field is required. +The State field is required.;The State field is required. +The Country field is required.;The Country field is required. +Street and house number requered;Street and house number requered +Max lenght reached;Max lenght reached +Server;Server +stopped;stopped +Cleanups;Cleanups +executed;executed +Used clanup;Used clanup +Enable;Enable +Disabble;Disabble +Disable;Disable +Addons;Addons +Javascript version;Javascript version +Javascript file;Javascript file +Select javascript file to execute on start;Select javascript file to execute on start +Submit;Submit +Processing;Processing +Go up;Go up +Running cleanup;Running cleanup +servers;servers +Select folder to move the file(s) to;Select folder to move the file(s) to +Paper version;Paper version +Join2Start;Join2Start +Server reset;Server reset +Reset;Reset +Resetting;Resetting +Are you sure you want to reset this server?;Are you sure you want to reset this server? +Are you sure? This cannot be undone;Are you sure? This cannot be undone +Resetting server;Resetting server +Deleted file;Deleted file +Reinstalling server;Reinstalling server +Uploading files;Uploading files +complete;complete +Upload complete;Upload complete +Security;Security +Subscriptions;Subscriptions +2fa Code;2fa Code +Your account is secured with 2fa;Your account is secured with 2fa +anyone write a fancy text here?;anyone write a fancy text here? +Activate 2fa;Activate 2fa +2fa apps;2fa apps +Use an app like ;Use an app like +or;or +and scan the following QR Code;and scan the following QR Code +If you have trouble using the QR Code, select manual input in the app and enter your email and the following code:;If you have trouble using the QR Code, select manual input in the app and enter your email and the following code: +Finish activation;Finish activation +2fa Code requiered;2fa Code requiered +New password;New password +Secure your account;Secure your account +2fa adds another layer of security to your account. You have to enter a 6 digit code in order to login.;2fa adds another layer of security to your account. You have to enter a 6 digit code in order to login. +New subscription;New subscription +You need to enter a name;You need to enter a name +You need to enter a description;You need to enter a description +Add new limit;Add new limit +Create subscription;Create subscription +Options;Options +Amount;Amount +Do you really want to delete it?;Do you really want to delete it? +Loading your subscription;Loading your subscription +Searching for deploy node;Searching for deploy node +Searching for available images;Searching for available images +Server details;Server details +Configure your server;Configure your server +Default;Default +You reached the maximum amount of servers for every image of your subscription;You reached the maximum amount of servers for every image of your subscription +Personal information;Personal information +Enter code;Enter code +Server rename;Server rename +Create code;Create code +Save subscription;Save subscription +Enter your information;Enter your information +You need to enter your full name in order to use moonlight;You need to enter your full name in order to use moonlight +No node found;No node found +No node found to deploy to found;No node found to deploy to found +Node offline;Node offline +The node the server is running on is currently offline;The node the server is running on is currently offline +Server not found;Server not found +A server with that id cannot be found or you have no access for this server;A server with that id cannot be found or you have no access for this server +Compress;Compress +Decompress;Decompress +Moving;Moving +Compressing;Compressing +selected;selected +New website;New website +Plesk servers;Plesk servers +Base domain;Base domain +Plesk server;Plesk server +Ftp;Ftp +No SSL certificate found;No SSL certificate found +Ftp Host;Ftp Host +Ftp Port;Ftp Port +Ftp Username;Ftp Username +Ftp Password;Ftp Password +Use;Use +SSL Certificates;SSL Certificates +SSL certificates;SSL certificates +Issue certificate;Issue certificate +New plesk server;New plesk server +Api url;Api url +Host system offline;Host system offline +The host system the website is running on is currently offline;The host system the website is running on is currently offline +No SSL certificates found;No SSL certificates found +No databases found for this website;No databases found for this website +The name should be at least 8 characters long;The name should be at least 8 characters long +The name should only contain of lower case characters and numbers;The name should only contain of lower case characters and numbers +Error from plesk;Error from plesk +Host;Host +Username;Username +SRV records cannot be updated thanks to the cloudflare api client. Please delete the record and create a new one;SRV records cannot be updated thanks to the cloudflare api client. Please delete the record and create a new one +The User field is required.;The User field is required. +You need to specify a owner;You need to specify a owner +You need to specify a image;You need to specify a image +Api Url;Api Url +Api Key;Api Key +Duration;Duration +Enter duration of subscription;Enter duration of subscription +Copied code to clipboard;Copied code to clipboard +Invalid or expired subscription code;Invalid or expired subscription code +Current subscription;Current subscription +You need to specify a server image;You need to specify a server image +CPU;CPU +Hour;Hour +Day;Day +Month;Month +Year;Year +All time;All time +This function is not implemented;This function is not implemented +Domain details;Domain details +Configure your domain;Configure your domain +You reached the maximum amount of domains in your subscription;You reached the maximum amount of domains in your subscription +You need to specify a shared domain;You need to specify a shared domain +A domain with this name does already exist for this shared domain;A domain with this name does already exist for this shared domain +The Email field is required.;The Email field is required. +The Password field is required.;The Password field is required. +The ConfirmPassword field is required.;The ConfirmPassword field is required. +Passwords need to match;Passwords need to match +Cleanup exception;Cleanup exception +No shared domain found;No shared domain found +Searching for deploy plesk server;Searching for deploy plesk server +No plesk server found;No plesk server found +No plesk server found to deploy to;No plesk server found to deploy to +No node found to deploy to;No node found to deploy to +Website details;Website details +Configure your website;Configure your website +The name cannot be longer that 32 characters;The name cannot be longer that 32 characters +The name should only consist of lower case characters;The name should only consist of lower case characters +News;News +Title...;Title... +Enter text...;Enter text... +Saving...;Saving... +Deleting...;Deleting... +Delete post;Delete post +Do you really want to delete the post ";Do you really want to delete the post " +You have no domains;You have no domains +We were not able to find any domains associated with your account;We were not able to find any domains associated with your account +You have no websites;You have no websites +We were not able to find any websites associated with your account;We were not able to find any websites associated with your account +Guest;Guest +You need a domain;You need a domain +New post;New post +New entry;New entry diff --git a/Moonlight/deleteme_resources/lang/en_us.lang b/Moonlight/deleteme_resources/lang/en_us.lang new file mode 100644 index 00000000..2e2e1fcb --- /dev/null +++ b/Moonlight/deleteme_resources/lang/en_us.lang @@ -0,0 +1,86 @@ +Open support;Open support +About us;About us +Imprint;Imprint +Privacy;Privacy +Create;Create +Server;Server +Domain;Domain +Website;Website +Login;Login +Register;Register +Email;Email +Password;Password +Sign In;Sign In +Sign in to start with moonlight;Sign in to start with moonlight +Sign in with Discord;Sign in with Discord +Sign in with Google;Sign in with Google +Or with email;Or with email +Forgot password?;Forgot password? +Sign-in;Sign-in +Not registered yet?;Not registered yet? +Sign up;Sign up +Profile;Profile +Logout;Logout +Dashboard;Dashboard +Servers;Servers +Websites;Websites +Domains;Domains +Changelog;Changelog +Admin;Admin +System;System +Overview;Overview +Manager;Manager +Cleanup;Cleanup +Nodes;Nodes +Images;Images +Users;Users +Shared domains;Shared domains +Support;Support +Subscriptions;Subscriptions +Statistics;Statistics +Create something new;Create something new +Create a gameserver;Create a gameserver +A new gameserver in just a few minutes;A new gameserver in just a few minutes +Create a website;Create a website +Make your own websites with a webspace;Make your own websites with a webspace +Create a domain;Create a domain +Make your servvices accessible throught your own domain;Make your servvices accessible throught your own domain +Manage your services;Manage your services +Manage your gameservers;Manage your gameservers +Adjust your gameservers;Adjust your gameservers +Manage your websites;Manage your websites +Modify the content of your websites;Modify the content of your websites +Manage your domains;Manage your domains +Add, edit and delete dns records;Add, edit and delete dns records +New server;New server +Id;Id +Name;Name +Cores;Cores +Memory;Memory +Disk;Disk +Owner;Owner +Manage;Manage +Node offline;Node offline +The node the server is running on is currently offline;The node the server is running on is currently offline +Sessions;Sessions +New user;New user +First name;First name +Last name;Last name +Created at;Created at +Refresh;Refresh +Send a message to all users;Send a message to all users +IP;IP +URL;URL +Device;Device +Time;Time +Actions;Actions +Change url;Change url +Message;Message +Enter url;Enter url +Send;Send +Sending;Sending +Welcome to the support chat. Ask your question here and we will help you;Welcome to the support chat. Ask your question here and we will help you +less than a minute ago;less than a minute ago +The support team has been notified. Please be patient;The support team has been notified. Please be patient +is typing;is typing +Proccessing;Proccessing diff --git a/Moonlight/deleteme_resources/mail/login.html b/Moonlight/deleteme_resources/mail/login.html new file mode 100644 index 00000000..06748d96 --- /dev/null +++ b/Moonlight/deleteme_resources/mail/login.html @@ -0,0 +1,53 @@ + + + + + New moonlight login + + +
+ + + + + + + + + + + + +
+
+
+ + Logo + +
+
+

Hey {{FirstName}}, there is a new login in your moonlight account

+

Here is all the data we collected

+

IP: {{Ip}}

+

Device: {{Device}}

+

Location: {{Location}}

+
+ Open Moonlight + +
+
+

You need help?

+

We are happy to help!

+

More information at + endelon.link/support. +

+
+

Copyright 2022 Endelon Hosting

+
+
+ + \ No newline at end of file diff --git a/Moonlight/deleteme_resources/mail/passwordChange.html b/Moonlight/deleteme_resources/mail/passwordChange.html new file mode 100644 index 00000000..ae3e5a12 --- /dev/null +++ b/Moonlight/deleteme_resources/mail/passwordChange.html @@ -0,0 +1,53 @@ + + + + + Moonlight password change + + +
+ + + + + + + + + + + + +
+
+
+ + Logo + +
+
+

Hey {{FirstName}}, your password has been changed

+

If this was not you please contact us. Also here is the data we collected.

+

IP: {{Ip}}

+

Device: {{Device}}

+

Location: {{Location}}

+
+ Open Moonlight + +
+
+

You need help?

+

We are happy to help!

+

More information at + endelon.link/support. +

+
+

Copyright 2023 Endelon Hosting

+
+
+ + \ No newline at end of file diff --git a/Moonlight/deleteme_resources/mail/passwordReset.html b/Moonlight/deleteme_resources/mail/passwordReset.html new file mode 100644 index 00000000..9dd066c6 --- /dev/null +++ b/Moonlight/deleteme_resources/mail/passwordReset.html @@ -0,0 +1,54 @@ + + + + + Moonlight password reset + + +
+ + + + + + + + + + + + +
+
+
+ + Logo + +
+
+

Hey {{FirstName}}, your password has been resetted

+

Your new password is: {{Password}}

+

If this was not you please contact us. Also here is the data we collected.

+

IP: {{Ip}}

+

Device: {{Device}}

+

Location: {{Location}}

+
+ Open Moonlight + +
+
+

You need help?

+

We are happy to help!

+

More information at + endelon.link/support. +

+
+

Copyright 2022 Endelon Hosting

+
+
+ + \ No newline at end of file diff --git a/Moonlight/deleteme_resources/mail/register.html b/Moonlight/deleteme_resources/mail/register.html new file mode 100644 index 00000000..3ffbc507 --- /dev/null +++ b/Moonlight/deleteme_resources/mail/register.html @@ -0,0 +1,50 @@ + + + + + Welcome + + +
+ + + + + + + + + + + + +
+
+
+ + Logo + +
+
+

Hey {{FirstName}}, welcome to moonlight

+

We are happy to welcome you in ;)

+
+ Open Moonlight + +
+
+

You need help?

+

We are happy to help!

+

More information at + endelon.link/support. +

+
+

Copyright 2022 Endelon Hosting

+
+
+ + \ No newline at end of file diff --git a/Moonlight/deleteme_resources/public/images/logo.svg b/Moonlight/deleteme_resources/public/images/logo.svg new file mode 100644 index 00000000..193ebfae --- /dev/null +++ b/Moonlight/deleteme_resources/public/images/logo.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/Moonlight/deleteme_resources/public/images/logolong.png b/Moonlight/deleteme_resources/public/images/logolong.png new file mode 100644 index 0000000000000000000000000000000000000000..1494f265b2016cf05d3c58cfe0f77c72be0ef78b GIT binary patch literal 12410 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 U*aKCyJ{bUVQeP$O#f^ji2N?sR&;S4c literal 0 HcmV?d00001