Compare commits
126 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
388deacf60 | ||
|
|
aa547038de | ||
|
|
17e3345b8a | ||
|
|
f95312c1e3 | ||
|
|
2144ca3823 | ||
|
|
de45ff40d8 | ||
|
|
606085c012 | ||
|
|
00525d8099 | ||
|
|
26617d67f5 | ||
|
|
600bec3417 | ||
|
|
4e85d1755a | ||
|
|
0832936933 | ||
|
|
ecda2ec6d1 | ||
|
|
6f3765a3bf | ||
|
|
29002d3445 | ||
|
|
6d0456a008 | ||
|
|
3e698123bb | ||
|
|
f3fb86819a | ||
|
|
e2248a8444 | ||
|
|
2cf2b77090 | ||
|
|
fedc9278d4 | ||
|
|
f29206a69b | ||
|
|
0658e55a78 | ||
|
|
21bea974a9 | ||
|
|
33ef09433e | ||
|
|
173bff67df | ||
|
|
512a989609 | ||
|
|
2d7dac5089 | ||
|
|
11708fbc3b | ||
|
|
daeb4dd5b9 | ||
|
|
daba4cba04 | ||
|
|
1cd0f0f96f | ||
|
|
6a30db07a7 | ||
|
|
6c8754d008 | ||
|
|
356ba94592 | ||
|
|
d3b55d155b | ||
|
|
0015001d7c | ||
|
|
0a86aa8aa4 | ||
|
|
74d4ee729d | ||
|
|
178ff36e86 | ||
|
|
f852df5807 | ||
|
|
90f4b04857 | ||
|
|
244e87ed18 | ||
|
|
80ea5a543f | ||
|
|
5baba05f5f | ||
|
|
591da6de5c | ||
|
|
c1ddff4ae3 | ||
|
|
67d78d7104 | ||
|
|
52f4b00f84 | ||
|
|
8f028e2ac6 | ||
|
|
5bd6f15203 | ||
|
|
4c39ad6170 | ||
|
|
12392d4f47 | ||
|
|
b75147e4c0 | ||
|
|
8f9508f30b | ||
|
|
428e2668d3 | ||
|
|
c1cfb35c86 | ||
|
|
d6777c463e | ||
|
|
f9126bffe0 | ||
|
|
0488e83a38 | ||
|
|
d87ddc90e3 | ||
|
|
151bc82998 | ||
|
|
e4c21c74a5 | ||
|
|
13741a2be9 | ||
|
|
c866e89b72 | ||
|
|
8be93bc53c | ||
|
|
384b6a3e7d | ||
|
|
ba2de54c60 | ||
|
|
bd5567e24f | ||
|
|
b8e39824b5 | ||
|
|
d8c9bdbd8d | ||
|
|
80eb210af0 | ||
|
|
a295354549 | ||
|
|
749ea5dc8e | ||
|
|
f52b9e2951 | ||
|
|
d2dbb68967 | ||
|
|
d1c9009e9f | ||
|
|
d024a834f9 | ||
|
|
c0df8ac507 | ||
|
|
ab529991fd | ||
|
|
92705837ba | ||
|
|
609d5451f9 | ||
|
|
2bb2caeeed | ||
|
|
61db49bfb7 | ||
|
|
a75678d305 | ||
|
|
d418c91efa | ||
|
|
7f2da5a55d | ||
|
|
5e592ccdcb | ||
|
|
016f50fb1c | ||
|
|
fe21668a2b | ||
|
|
1aab86a317 | ||
|
|
243d23d4e2 | ||
|
|
2fe17473ae | ||
|
|
609cf8cfac | ||
|
|
678da30b09 | ||
|
|
d19412f4bb | ||
|
|
1665d6e537 | ||
|
|
fd210f2404 | ||
|
|
c33729fb44 | ||
|
|
7983bf3ee4 | ||
|
|
a09f60aea7 | ||
|
|
28b5893c21 | ||
|
|
25b47d8b6c | ||
|
|
85f5b8a7da | ||
|
|
ab9333f99a | ||
|
|
d60f8fc905 | ||
|
|
fe1f4412d8 | ||
|
|
f191533410 | ||
|
|
a894707536 | ||
|
|
d2ccc84286 | ||
|
|
f2ec43f2d2 | ||
|
|
7feccc8d9f | ||
|
|
a8bd1193ce | ||
|
|
366d1a9205 | ||
|
|
df9ed95c6b | ||
|
|
23a211362e | ||
|
|
cf91d44902 | ||
|
|
35633e21a9 | ||
|
|
ce8b8f6798 | ||
|
|
c28c80ba25 | ||
|
|
da17b1df93 | ||
|
|
f9f5865ef9 | ||
|
|
389ded9b77 | ||
|
|
faebaa59dd | ||
|
|
6b7dc2ad05 | ||
|
|
e356c9d0c8 |
9
.gitattributes
vendored
9
.gitattributes
vendored
@@ -1,3 +1,10 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
Moonlight/wwwroot/* linguist-vendored
|
||||
Moonlight/wwwroot/** linguist-vendored
|
||||
Moonlight/wwwroot/assets/js/scripts.bundle.js linguist-vendored
|
||||
Moonlight/wwwroot/assets/js/widgets.bundle.js linguist-vendored
|
||||
Moonlight/wwwroot/assets/js/theme.js linguist-vendored
|
||||
Moonlight/wwwroot/assets/css/boxicons.min.css linguist-vendored
|
||||
Moonlight/wwwroot/assets/css/style.bundle.css linguist-vendored
|
||||
Moonlight/wwwroot/assets/plugins/** linguist-vendored
|
||||
Moonlight/wwwroot/assets/fonts/** linguist-vendored
|
||||
|
||||
@@ -47,7 +47,7 @@ public class ModrinthApiHelper
|
||||
|
||||
var request = new RestRequest(url)
|
||||
{
|
||||
Timeout = 60 * 15
|
||||
Timeout = 300000
|
||||
};
|
||||
|
||||
request.AddHeader("Content-Type", "application/json");
|
||||
|
||||
11
Moonlight/App/ApiClients/Telemetry/Requests/TelemetryData.cs
Normal file
11
Moonlight/App/ApiClients/Telemetry/Requests/TelemetryData.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace Moonlight.App.ApiClients.Telemetry.Requests;
|
||||
|
||||
public class TelemetryData
|
||||
{
|
||||
public string AppUrl { get; set; } = "";
|
||||
public int Servers { get; set; }
|
||||
public int Nodes { get; set; }
|
||||
public int Users { get; set; }
|
||||
public int Databases { get; set; }
|
||||
public int Webspaces { get; set; }
|
||||
}
|
||||
52
Moonlight/App/ApiClients/Telemetry/TelemetryApiHelper.cs
Normal file
52
Moonlight/App/ApiClients/Telemetry/TelemetryApiHelper.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using Newtonsoft.Json;
|
||||
using RestSharp;
|
||||
|
||||
namespace Moonlight.App.ApiClients.Telemetry;
|
||||
|
||||
public class TelemetryApiHelper
|
||||
{
|
||||
private readonly RestClient Client;
|
||||
|
||||
public TelemetryApiHelper()
|
||||
{
|
||||
Client = new();
|
||||
}
|
||||
|
||||
public async Task Post(string resource, object? body)
|
||||
{
|
||||
var request = CreateRequest(resource);
|
||||
|
||||
request.Method = Method.Post;
|
||||
|
||||
request.AddParameter("application/json", JsonConvert.SerializeObject(body), ParameterType.RequestBody);
|
||||
|
||||
var response = await Client.ExecuteAsync(request);
|
||||
|
||||
if (!response.IsSuccessful)
|
||||
{
|
||||
if (response.StatusCode != 0)
|
||||
{
|
||||
throw new TelemetryException(
|
||||
$"An error occured: ({response.StatusCode}) {response.Content}",
|
||||
(int)response.StatusCode
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"An internal error occured: {response.ErrorMessage}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private RestRequest CreateRequest(string resource)
|
||||
{
|
||||
var url = "https://telemetry.moonlightpanel.xyz/" + resource;
|
||||
|
||||
var request = new RestRequest(url)
|
||||
{
|
||||
Timeout = 3000000
|
||||
};
|
||||
|
||||
return request;
|
||||
}
|
||||
}
|
||||
32
Moonlight/App/ApiClients/Telemetry/TelemetryException.cs
Normal file
32
Moonlight/App/ApiClients/Telemetry/TelemetryException.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Moonlight.App.ApiClients.Telemetry;
|
||||
|
||||
[Serializable]
|
||||
public class TelemetryException : Exception
|
||||
{
|
||||
public int StatusCode { get; set; }
|
||||
|
||||
public TelemetryException()
|
||||
{
|
||||
}
|
||||
|
||||
public TelemetryException(string message, int statusCode) : base(message)
|
||||
{
|
||||
StatusCode = statusCode;
|
||||
}
|
||||
|
||||
public TelemetryException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public TelemetryException(string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
}
|
||||
|
||||
protected TelemetryException(
|
||||
SerializationInfo info,
|
||||
StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -209,7 +209,7 @@ public class WingsApiHelper
|
||||
|
||||
var request = new RestRequest(url)
|
||||
{
|
||||
Timeout = 60 * 15
|
||||
Timeout = 300000
|
||||
};
|
||||
|
||||
request.AddHeader("Content-Type", "application/json");
|
||||
|
||||
356
Moonlight/App/Configuration/ConfigV1.cs
Normal file
356
Moonlight/App/Configuration/ConfigV1.cs
Normal file
@@ -0,0 +1,356 @@
|
||||
using System.ComponentModel;
|
||||
using Moonlight.App.Helpers;
|
||||
|
||||
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")]
|
||||
[Description("The url moonlight is accesible with from the internet")]
|
||||
public string AppUrl { get; set; } = "http://your-moonlight-url-without-slash";
|
||||
|
||||
[JsonProperty("EnableLatencyCheck")]
|
||||
[Description(
|
||||
"This will enable a latency check for connections to moonlight. Users with an too high latency will be warned that moonlight might be buggy for them")]
|
||||
public bool EnableLatencyCheck { get; set; } = true;
|
||||
|
||||
[JsonProperty("LatencyCheckThreshold")]
|
||||
[Description("Specify the latency threshold which has to be reached in order to trigger the warning message")]
|
||||
public int LatencyCheckThreshold { get; set; } = 500;
|
||||
|
||||
[JsonProperty("Auth")] public AuthData Auth { get; set; } = new();
|
||||
|
||||
[JsonProperty("Database")] public DatabaseData Database { get; set; } = new();
|
||||
|
||||
[JsonProperty("DiscordBotApi")] public DiscordBotApiData 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("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();
|
||||
|
||||
[JsonProperty("Stripe")] public StripeData Stripe { get; set; } = new();
|
||||
|
||||
[JsonProperty("Tickets")] public TicketsData Tickets { get; set; } = new();
|
||||
}
|
||||
|
||||
public class TicketsData
|
||||
{
|
||||
[JsonProperty("WelcomeMessage")]
|
||||
[Description("The message that will be sent when a user created a ticket")]
|
||||
public string WelcomeMessage { get; set; } = "Welcome to the support";
|
||||
}
|
||||
|
||||
public class StripeData
|
||||
{
|
||||
[JsonProperty("ApiKey")]
|
||||
[Description("Put here your stripe api key if you add subscriptions. Currently the only billing option is stripe which is enabled by default and cannot be turned off. This feature is still experimental")]
|
||||
public string ApiKey { get; set; } = "";
|
||||
}
|
||||
|
||||
public class AuthData
|
||||
{
|
||||
[JsonProperty("DenyLogin")]
|
||||
[Description("Prevent every new login")]
|
||||
public bool DenyLogin { get; set; } = false;
|
||||
|
||||
[JsonProperty("DenyRegister")]
|
||||
[Description("Prevent every new user to register")]
|
||||
public bool DenyRegister { get; set; } = false;
|
||||
}
|
||||
|
||||
public class CleanupData
|
||||
{
|
||||
[JsonProperty("Cpu")]
|
||||
[Description("The maximum amount of cpu usage in percent a node is allowed to use before the cleanup starts")]
|
||||
public long Cpu { get; set; } = 90;
|
||||
|
||||
[JsonProperty("Memory")]
|
||||
[Description("The minumum amount of memory in megabytes avaliable before the cleanup starts")]
|
||||
public long Memory { get; set; } = 8192;
|
||||
|
||||
[JsonProperty("Wait")]
|
||||
[Description("The delay between every cleanup check in minutes")]
|
||||
public long Wait { get; set; } = 15;
|
||||
|
||||
[JsonProperty("Uptime")]
|
||||
[Description("The maximum uptime of any server in hours before it the server restarted by the cleanup system")]
|
||||
public long Uptime { get; set; } = 6;
|
||||
|
||||
[JsonProperty("Enable")]
|
||||
[Description("The cleanup system provides a fair way for stopping unused servers and staying stable even with overallocation. A detailed explanation: docs.endelon-hosting.de/erklaerungen/cleanup")]
|
||||
public bool Enable { get; set; } = false;
|
||||
|
||||
[JsonProperty("MinUptime")]
|
||||
[Description("The minumum uptime of a server in minutes to prevent stopping servers which just started")]
|
||||
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")]
|
||||
[Blur]
|
||||
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 DiscordBotApiData
|
||||
{
|
||||
[JsonProperty("Enable")]
|
||||
[Description("Enable the discord bot api. Currently only DatBot is using this api")]
|
||||
public bool Enable { get; set; } = false;
|
||||
|
||||
[JsonProperty("Token")]
|
||||
[Description("Specify the token the api client needs to provide")]
|
||||
[Blur]
|
||||
public string Token { get; set; } = Guid.NewGuid().ToString();
|
||||
}
|
||||
public class DiscordBotData
|
||||
{
|
||||
[JsonProperty("Enable")]
|
||||
[Description("The discord bot can be used to allow customers to manage their servers via discord")]
|
||||
public bool Enable { get; set; } = false;
|
||||
|
||||
[JsonProperty("Token")]
|
||||
[Description("Your discord bot token goes here")]
|
||||
[Blur]
|
||||
public string Token { get; set; } = "discord token here";
|
||||
|
||||
[JsonProperty("PowerActions")]
|
||||
[Description("Enable actions like starting and stopping servers")]
|
||||
public bool PowerActions { get; set; } = false;
|
||||
|
||||
[JsonProperty("SendCommands")]
|
||||
[Description("Allow users to send commands to their servers")]
|
||||
public bool SendCommands { get; set; } = false;
|
||||
}
|
||||
|
||||
public class DiscordNotificationsData
|
||||
{
|
||||
[JsonProperty("Enable")]
|
||||
[Description("The discord notification system sends you a message everytime a event like a new support chat message is triggered with usefull data describing the event")]
|
||||
public bool Enable { get; set; } = false;
|
||||
|
||||
[JsonProperty("WebHook")]
|
||||
[Description("The discord webhook the notifications are being sent to")]
|
||||
[Blur]
|
||||
public string WebHook { get; set; } = "http://your-discord-webhook-url";
|
||||
}
|
||||
|
||||
public class DomainsData
|
||||
{
|
||||
[JsonProperty("Enable")]
|
||||
[Description("This enables the domain system")]
|
||||
public bool Enable { get; set; } = false;
|
||||
|
||||
[JsonProperty("AccountId")]
|
||||
[Description("This option specifies the cloudflare account id")]
|
||||
public string AccountId { get; set; } = "cloudflare acc id";
|
||||
|
||||
[JsonProperty("Email")]
|
||||
[Description("This specifies the cloudflare email to use for communicating with the cloudflare api")]
|
||||
public string Email { get; set; } = "cloudflare@acc.email";
|
||||
|
||||
[JsonProperty("Key")]
|
||||
[Description("Your cloudflare api key goes here")]
|
||||
[Blur]
|
||||
public string Key { get; set; } = "secret";
|
||||
}
|
||||
|
||||
public class HtmlData
|
||||
{
|
||||
[JsonProperty("Headers")] public HeadersData Headers { get; set; } = new();
|
||||
}
|
||||
|
||||
public class HeadersData
|
||||
{
|
||||
[JsonProperty("Color")]
|
||||
[Description("This specifies the color of the embed generated by platforms like discord when someone posts a link to your moonlight instance")]
|
||||
public string Color { get; set; } = "#4b27e8";
|
||||
|
||||
[JsonProperty("Description")]
|
||||
[Description("This specifies the description text of the embed generated by platforms like discord when someone posts a link to your moonlight instance and can also help google to index your moonlight instance correctly")]
|
||||
public string Description { get; set; } = "the next generation hosting panel";
|
||||
|
||||
[JsonProperty("Keywords")]
|
||||
[Description("To help search engines like google to index your moonlight instance correctly you can specify keywords seperated by a comma here")]
|
||||
public string Keywords { get; set; } = "moonlight";
|
||||
|
||||
[JsonProperty("Title")]
|
||||
[Description("This specifies the title of the embed generated by platforms like discord when someone posts a link to your moonlight instance")]
|
||||
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")]
|
||||
[Blur]
|
||||
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")]
|
||||
[Description("This overrides the redirect url which would be typicaly the app url")]
|
||||
public string OverrideUrl { get; set; } = "https://only-for-development.cases";
|
||||
|
||||
[JsonProperty("EnableOverrideUrl")]
|
||||
[Description("This enables the url override")]
|
||||
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")]
|
||||
[Blur]
|
||||
public string ClientSecret { get; set; }
|
||||
}
|
||||
|
||||
public class RatingData
|
||||
{
|
||||
[JsonProperty("Enabled")]
|
||||
[Description("The rating systems shows a user who is registered longer than the set amout of days a popup to rate this platform if he hasnt rated it before")]
|
||||
public bool Enabled { get; set; } = false;
|
||||
|
||||
[JsonProperty("Url")]
|
||||
[Description("This is the url a user who rated above a set limit is shown to rate you again. Its recommended to put your google or trustpilot rate link here")]
|
||||
public string Url { get; set; } = "https://link-to-google-or-smth";
|
||||
|
||||
[JsonProperty("MinRating")]
|
||||
[Description("The minimum star count on the rating ranging from 1 to 5")]
|
||||
public int MinRating { get; set; } = 4;
|
||||
|
||||
[JsonProperty("DaysSince")]
|
||||
[Description("The days a user has to be registered to even be able to get this popup")]
|
||||
public int DaysSince { get; set; } = 5;
|
||||
}
|
||||
|
||||
public class SecurityData
|
||||
{
|
||||
[JsonProperty("Token")]
|
||||
[Description("This is the moonlight app token. It is used to encrypt and decrypt data and validte tokens and sessions")]
|
||||
[Blur]
|
||||
public string Token { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
[JsonProperty("BlockIpDuration")]
|
||||
[Description("The duration in minutes a ip will be blocked by the anti ddos system")]
|
||||
public int BlockIpDuration { get; set; } = 15;
|
||||
|
||||
[JsonProperty("ReCaptcha")] public ReCaptchaData ReCaptcha { get; set; } = new();
|
||||
}
|
||||
|
||||
public class ReCaptchaData
|
||||
{
|
||||
[JsonProperty("Enable")]
|
||||
[Description("Enables repatcha at places like the register page. For information how to get your recaptcha credentails go to google.com/recaptcha/about/")]
|
||||
public bool Enable { get; set; } = false;
|
||||
|
||||
[JsonProperty("SiteKey")]
|
||||
[Blur]
|
||||
public string SiteKey { get; set; } = "recaptcha site key here";
|
||||
|
||||
[JsonProperty("SecretKey")]
|
||||
[Blur]
|
||||
public string SecretKey { get; set; } = "recaptcha secret here";
|
||||
}
|
||||
|
||||
public class SentryData
|
||||
{
|
||||
[JsonProperty("Enable")]
|
||||
[Description("Sentry is a way to monitor application crashes and performance issues in real time. Enable this option only if you set a sentry dsn")]
|
||||
public bool Enable { get; set; } = false;
|
||||
|
||||
[JsonProperty("Dsn")]
|
||||
[Description("The dsn is the key moonlight needs to communicate with your sentry instance")]
|
||||
[Blur]
|
||||
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 SellPassData
|
||||
{
|
||||
[JsonProperty("Enable")] public bool Enable { get; set; } = false;
|
||||
|
||||
[JsonProperty("Url")] public string Url { get; set; } = "https://not-implemented-yet";
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Moonlight.App.Database.Entities;
|
||||
using Moonlight.App.Database.Entities.LogsEntries;
|
||||
using Moonlight.App.Database.Entities.Notification;
|
||||
using Moonlight.App.Database.Interceptors;
|
||||
using Moonlight.App.Models.Misc;
|
||||
using Moonlight.App.Services;
|
||||
|
||||
namespace Moonlight.App.Database;
|
||||
@@ -27,10 +25,6 @@ public class DataContext : DbContext
|
||||
public DbSet<ServerVariable> ServerVariables { get; set; }
|
||||
public DbSet<User> Users { get; set; }
|
||||
public DbSet<LoadingMessage> LoadingMessages { get; set; }
|
||||
public DbSet<AuditLogEntry> AuditLog { get; set; }
|
||||
public DbSet<ErrorLogEntry> ErrorLog { get; set; }
|
||||
public DbSet<SecurityLogEntry> SecurityLog { get; set; }
|
||||
|
||||
public DbSet<SharedDomain> SharedDomains { get; set; }
|
||||
public DbSet<Domain> Domains { get; set; }
|
||||
public DbSet<Revoke> Revokes { get; set; }
|
||||
@@ -46,20 +40,27 @@ public class DataContext : DbContext
|
||||
public DbSet<WebSpace> WebSpaces { get; set; }
|
||||
public DbSet<SupportChatMessage> SupportChatMessages { get; set; }
|
||||
public DbSet<IpBan> IpBans { get; set; }
|
||||
public DbSet<PermissionGroup> PermissionGroups { get; set; }
|
||||
public DbSet<SecurityLog> SecurityLogs { get; set; }
|
||||
public DbSet<BlocklistIp> BlocklistIps { get; set; }
|
||||
public DbSet<WhitelistIp> WhitelistIps { get; set; }
|
||||
|
||||
public DbSet<Ticket> Tickets { get; set; }
|
||||
public DbSet<TicketMessage> TicketMessages { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
if (!optionsBuilder.IsConfigured)
|
||||
{
|
||||
var config = ConfigService
|
||||
.GetSection("Moonlight")
|
||||
.GetSection("Database");
|
||||
.Get()
|
||||
.Moonlight.Database;
|
||||
|
||||
var connectionString = $"host={config.GetValue<string>("Host")};" +
|
||||
$"port={config.GetValue<int>("Port")};" +
|
||||
$"database={config.GetValue<string>("Database")};" +
|
||||
$"uid={config.GetValue<string>("Username")};" +
|
||||
$"pwd={config.GetValue<string>("Password")}";
|
||||
var connectionString = $"host={config.Host};" +
|
||||
$"port={config.Port};" +
|
||||
$"database={config.Database};" +
|
||||
$"uid={config.Username};" +
|
||||
$"pwd={config.Password}";
|
||||
|
||||
optionsBuilder.UseMySql(
|
||||
connectionString,
|
||||
|
||||
10
Moonlight/App/Database/Entities/BlocklistIp.cs
Normal file
10
Moonlight/App/Database/Entities/BlocklistIp.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Moonlight.App.Database.Entities;
|
||||
|
||||
public class BlocklistIp
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Ip { get; set; } = "";
|
||||
public DateTime ExpiresAt { get; set; }
|
||||
public long Packets { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using Moonlight.App.Models.Misc;
|
||||
|
||||
namespace Moonlight.App.Database.Entities.LogsEntries;
|
||||
|
||||
public class AuditLogEntry
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public AuditLogType Type { get; set; }
|
||||
public string JsonData { get; set; } = "";
|
||||
public bool System { get; set; }
|
||||
public string Ip { get; set; } = "";
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
namespace Moonlight.App.Database.Entities.LogsEntries;
|
||||
|
||||
public class ErrorLogEntry
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Stacktrace { get; set; } = "";
|
||||
public bool System { get; set; }
|
||||
public string JsonData { get; set; } = "";
|
||||
public string Ip { get; set; } = "";
|
||||
public string Class { get; set; } = "";
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using Moonlight.App.Models.Misc;
|
||||
|
||||
namespace Moonlight.App.Database.Entities.LogsEntries;
|
||||
|
||||
public class SecurityLogEntry
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public bool System { get; set; }
|
||||
public string Ip { get; set; } = "";
|
||||
public SecurityLogType Type { get; set; }
|
||||
public string JsonData { get; set; } = "";
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
8
Moonlight/App/Database/Entities/PermissionGroup.cs
Normal file
8
Moonlight/App/Database/Entities/PermissionGroup.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Moonlight.App.Database.Entities;
|
||||
|
||||
public class PermissionGroup
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = "";
|
||||
public byte[] Permissions { get; set; } = Array.Empty<byte>();
|
||||
}
|
||||
8
Moonlight/App/Database/Entities/SecurityLog.cs
Normal file
8
Moonlight/App/Database/Entities/SecurityLog.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Moonlight.App.Database.Entities;
|
||||
|
||||
public class SecurityLog
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Text { get; set; } = "";
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -1,9 +1,16 @@
|
||||
namespace Moonlight.App.Database.Entities;
|
||||
using Moonlight.App.Models.Misc;
|
||||
|
||||
namespace Moonlight.App.Database.Entities;
|
||||
|
||||
public class Subscription
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = "";
|
||||
public string Description { get; set; } = "";
|
||||
public Currency Currency { get; set; } = Currency.USD;
|
||||
public double Price { get; set; }
|
||||
public string StripeProductId { get; set; } = "";
|
||||
public string StripePriceId { get; set; } = "";
|
||||
public string LimitsJson { get; set; } = "";
|
||||
public int Duration { get; set; } = 30;
|
||||
}
|
||||
19
Moonlight/App/Database/Entities/Ticket.cs
Normal file
19
Moonlight/App/Database/Entities/Ticket.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Moonlight.App.Models.Misc;
|
||||
|
||||
namespace Moonlight.App.Database.Entities;
|
||||
|
||||
public class Ticket
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string IssueTopic { get; set; } = "";
|
||||
public string IssueDescription { get; set; } = "";
|
||||
public string IssueTries { get; set; } = "";
|
||||
public User CreatedBy { get; set; }
|
||||
public User? AssignedTo { get; set; }
|
||||
public TicketPriority Priority { get; set; }
|
||||
public TicketStatus Status { get; set; }
|
||||
public TicketSubject Subject { get; set; }
|
||||
public int SubjectId { get; set; }
|
||||
public List<TicketMessage> Messages { get; set; } = new();
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
14
Moonlight/App/Database/Entities/TicketMessage.cs
Normal file
14
Moonlight/App/Database/Entities/TicketMessage.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace Moonlight.App.Database.Entities;
|
||||
|
||||
public class TicketMessage
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Content { get; set; } = "";
|
||||
public string? AttachmentUrl { get; set; }
|
||||
public User? Sender { get; set; }
|
||||
public bool IsSystemMessage { get; set; }
|
||||
public bool IsEdited { get; set; }
|
||||
public bool IsSupportMessage { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Moonlight.App.Models.Misc;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Moonlight.App.Models.Misc;
|
||||
|
||||
namespace Moonlight.App.Database.Entities;
|
||||
|
||||
@@ -24,6 +25,8 @@ public class User
|
||||
|
||||
public string Country { get; set; } = "";
|
||||
|
||||
public string ServerListLayoutJson { get; set; } = "";
|
||||
|
||||
// States
|
||||
|
||||
public UserStatus Status { get; set; } = UserStatus.Unverified;
|
||||
@@ -31,11 +34,14 @@ public class User
|
||||
public bool SupportPending { get; set; } = false;
|
||||
public bool HasRated { get; set; } = false;
|
||||
public int Rating { get; set; } = 0;
|
||||
public bool StreamerMode { get; set; } = false;
|
||||
|
||||
// Security
|
||||
public bool TotpEnabled { get; set; } = false;
|
||||
public string TotpSecret { get; set; } = "";
|
||||
public DateTime TokenValidTime { get; set; } = DateTime.UtcNow;
|
||||
public byte[] Permissions { get; set; } = Array.Empty<byte>();
|
||||
public PermissionGroup? PermissionGroup { get; set; }
|
||||
|
||||
// Discord
|
||||
public ulong DiscordId { get; set; }
|
||||
@@ -48,6 +54,10 @@ public class User
|
||||
// Subscriptions
|
||||
|
||||
public Subscription? CurrentSubscription { get; set; } = null;
|
||||
public DateTime SubscriptionSince { get; set; } = DateTime.Now;
|
||||
public int SubscriptionDuration { get; set; }
|
||||
public DateTime SubscriptionSince { get; set; } = DateTime.UtcNow;
|
||||
public DateTime SubscriptionExpires { get; set; } = DateTime.UtcNow;
|
||||
|
||||
// Ip logs
|
||||
public string RegisterIp { get; set; } = "";
|
||||
public string LastIp { get; set; } = "";
|
||||
}
|
||||
7
Moonlight/App/Database/Entities/WhitelistIp.cs
Normal file
7
Moonlight/App/Database/Entities/WhitelistIp.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Moonlight.App.Database.Entities;
|
||||
|
||||
public class WhitelistIp
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Ip { get; set; } = "";
|
||||
}
|
||||
1077
Moonlight/App/Database/Migrations/20230623235512_AddedServerListLayoutToUser.Designer.cs
generated
Normal file
1077
Moonlight/App/Database/Migrations/20230623235512_AddedServerListLayoutToUser.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Moonlight.App.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddedServerListLayoutToUser : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "ServerListLayoutJson",
|
||||
table: "Users",
|
||||
type: "longtext",
|
||||
nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ServerListLayoutJson",
|
||||
table: "Users");
|
||||
}
|
||||
}
|
||||
}
|
||||
1080
Moonlight/App/Database/Migrations/20230625190428_AddedStreamerMode.Designer.cs
generated
Normal file
1080
Moonlight/App/Database/Migrations/20230625190428_AddedStreamerMode.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Moonlight.App.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddedStreamerMode : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<bool>(
|
||||
name: "StreamerMode",
|
||||
table: "Users",
|
||||
type: "tinyint(1)",
|
||||
nullable: false,
|
||||
defaultValue: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "StreamerMode",
|
||||
table: "Users");
|
||||
}
|
||||
}
|
||||
}
|
||||
1088
Moonlight/App/Database/Migrations/20230703175432_AddedIpLogsForUser.Designer.cs
generated
Normal file
1088
Moonlight/App/Database/Migrations/20230703175432_AddedIpLogsForUser.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Moonlight.App.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddedIpLogsForUser : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "LastIp",
|
||||
table: "Users",
|
||||
type: "longtext",
|
||||
nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "RegisterIp",
|
||||
table: "Users",
|
||||
type: "longtext",
|
||||
nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LastIp",
|
||||
table: "Users");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "RegisterIp",
|
||||
table: "Users");
|
||||
}
|
||||
}
|
||||
}
|
||||
1105
Moonlight/App/Database/Migrations/20230705171914_AddedStripeIntegration.Designer.cs
generated
Normal file
1105
Moonlight/App/Database/Migrations/20230705171914_AddedStripeIntegration.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Moonlight.App.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddedStripeIntegration : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SubscriptionDuration",
|
||||
table: "Users");
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "SubscriptionExpires",
|
||||
table: "Users",
|
||||
type: "datetime(6)",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "Currency",
|
||||
table: "Subscriptions",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "Duration",
|
||||
table: "Subscriptions",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<double>(
|
||||
name: "Price",
|
||||
table: "Subscriptions",
|
||||
type: "double",
|
||||
nullable: false,
|
||||
defaultValue: 0.0);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "StripePriceId",
|
||||
table: "Subscriptions",
|
||||
type: "longtext",
|
||||
nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "StripeProductId",
|
||||
table: "Subscriptions",
|
||||
type: "longtext",
|
||||
nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "SubscriptionExpires",
|
||||
table: "Users");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Currency",
|
||||
table: "Subscriptions");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Duration",
|
||||
table: "Subscriptions");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Price",
|
||||
table: "Subscriptions");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "StripePriceId",
|
||||
table: "Subscriptions");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "StripeProductId",
|
||||
table: "Subscriptions");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "SubscriptionDuration",
|
||||
table: "Users",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
1109
Moonlight/App/Database/Migrations/20230715095531_AddPermissions.Designer.cs
generated
Normal file
1109
Moonlight/App/Database/Migrations/20230715095531_AddPermissions.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,28 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Moonlight.App.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddPermissions : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<byte[]>(
|
||||
name: "Permissions",
|
||||
table: "Users",
|
||||
type: "longblob",
|
||||
nullable: false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Permissions",
|
||||
table: "Users");
|
||||
}
|
||||
}
|
||||
}
|
||||
1139
Moonlight/App/Database/Migrations/20230715214550_AddPermissionGroup.Designer.cs
generated
Normal file
1139
Moonlight/App/Database/Migrations/20230715214550_AddPermissionGroup.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,68 @@
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Moonlight.App.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddPermissionGroup : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "PermissionGroupId",
|
||||
table: "Users",
|
||||
type: "int",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "PermissionGroups",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
Name = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Permissions = table.Column<byte[]>(type: "longblob", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_PermissionGroups", x => x.Id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Users_PermissionGroupId",
|
||||
table: "Users",
|
||||
column: "PermissionGroupId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Users_PermissionGroups_PermissionGroupId",
|
||||
table: "Users",
|
||||
column: "PermissionGroupId",
|
||||
principalTable: "PermissionGroups",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Users_PermissionGroups_PermissionGroupId",
|
||||
table: "Users");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "PermissionGroups");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Users_PermissionGroupId",
|
||||
table: "Users");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "PermissionGroupId",
|
||||
table: "Users");
|
||||
}
|
||||
}
|
||||
}
|
||||
1068
Moonlight/App/Database/Migrations/20230718123232_RemovedOldLogsAndAddedErrorLog.Designer.cs
generated
Normal file
1068
Moonlight/App/Database/Migrations/20230718123232_RemovedOldLogsAndAddedErrorLog.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,111 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Moonlight.App.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class RemovedOldLogsAndAddedErrorLog : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "AuditLog");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ErrorLog");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "SecurityLog");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "SecurityLogs",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
Text = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_SecurityLogs", x => x.Id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "SecurityLogs");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AuditLog",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
Ip = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
JsonData = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
System = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
Type = table.Column<int>(type: "int", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AuditLog", x => x.Id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ErrorLog",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
Class = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
Ip = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
JsonData = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
Stacktrace = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
System = table.Column<bool>(type: "tinyint(1)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ErrorLog", x => x.Id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "SecurityLog",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
Ip = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
JsonData = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
System = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
Type = table.Column<int>(type: "int", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_SecurityLog", x => x.Id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
}
|
||||
}
|
||||
1107
Moonlight/App/Database/Migrations/20230721201950_AddIpRules.Designer.cs
generated
Normal file
1107
Moonlight/App/Database/Migrations/20230721201950_AddIpRules.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Moonlight.App.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddIpRules : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "BlocklistIps",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
Ip = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
ExpiresAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
Packets = table.Column<long>(type: "bigint", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_BlocklistIps", x => x.Id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "WhitelistIps",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
Ip = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_WhitelistIps", x => x.Id);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "BlocklistIps");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "WhitelistIps");
|
||||
}
|
||||
}
|
||||
}
|
||||
1233
Moonlight/App/Database/Migrations/20230803012947_AddNewTicketModels.Designer.cs
generated
Normal file
1233
Moonlight/App/Database/Migrations/20230803012947_AddNewTicketModels.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Moonlight.App.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddNewTicketModels : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Tickets",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
IssueTopic = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
IssueDescription = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
IssueTries = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
CreatedById = table.Column<int>(type: "int", nullable: false),
|
||||
AssignedToId = table.Column<int>(type: "int", nullable: true),
|
||||
Priority = table.Column<int>(type: "int", nullable: false),
|
||||
Status = table.Column<int>(type: "int", nullable: false),
|
||||
Subject = table.Column<int>(type: "int", nullable: false),
|
||||
SubjectId = table.Column<int>(type: "int", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Tickets", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Tickets_Users_AssignedToId",
|
||||
column: x => x.AssignedToId,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id");
|
||||
table.ForeignKey(
|
||||
name: "FK_Tickets_Users_CreatedById",
|
||||
column: x => x.CreatedById,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "TicketMessages",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
Content = table.Column<string>(type: "longtext", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
AttachmentUrl = table.Column<string>(type: "longtext", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
SenderId = table.Column<int>(type: "int", nullable: true),
|
||||
IsSystemMessage = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
IsEdited = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
IsSupportMessage = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
TicketId = table.Column<int>(type: "int", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_TicketMessages", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_TicketMessages_Tickets_TicketId",
|
||||
column: x => x.TicketId,
|
||||
principalTable: "Tickets",
|
||||
principalColumn: "Id");
|
||||
table.ForeignKey(
|
||||
name: "FK_TicketMessages_Users_SenderId",
|
||||
column: x => x.SenderId,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id");
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_TicketMessages_SenderId",
|
||||
table: "TicketMessages",
|
||||
column: "SenderId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_TicketMessages_TicketId",
|
||||
table: "TicketMessages",
|
||||
column: "TicketId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Tickets_AssignedToId",
|
||||
table: "Tickets",
|
||||
column: "AssignedToId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Tickets_CreatedById",
|
||||
table: "Tickets",
|
||||
column: "CreatedById");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "TicketMessages");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Tickets");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,30 @@ namespace Moonlight.App.Database.Migrations
|
||||
.HasAnnotation("ProductVersion", "7.0.3")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.BlocklistIp", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Ip")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<long>("Packets")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("BlocklistIps");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.CloudPanel", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -241,95 +265,6 @@ namespace Moonlight.App.Database.Migrations
|
||||
b.ToTable("LoadingMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.AuditLogEntry", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Ip")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("JsonData")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("System")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("AuditLog");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.ErrorLogEntry", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Class")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Ip")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("JsonData")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Stacktrace")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("System")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("ErrorLog");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.LogsEntries.SecurityLogEntry", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Ip")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("JsonData")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<bool>("System")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SecurityLog");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.MySqlDatabase", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -475,6 +410,25 @@ namespace Moonlight.App.Database.Migrations
|
||||
b.ToTable("NotificationClients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.PermissionGroup", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<byte[]>("Permissions")
|
||||
.IsRequired()
|
||||
.HasColumnType("longblob");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("PermissionGroups");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.Revoke", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -490,6 +444,24 @@ namespace Moonlight.App.Database.Migrations
|
||||
b.ToTable("Revokes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.SecurityLog", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Text")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("SecurityLogs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -663,10 +635,16 @@ namespace Moonlight.App.Database.Migrations
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Currency")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("Duration")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("LimitsJson")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
@@ -675,6 +653,17 @@ namespace Moonlight.App.Database.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<double>("Price")
|
||||
.HasColumnType("double");
|
||||
|
||||
b.Property<string>("StripePriceId")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("StripeProductId")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Subscriptions");
|
||||
@@ -725,6 +714,97 @@ namespace Moonlight.App.Database.Migrations
|
||||
b.ToTable("SupportChatMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.Ticket", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("AssignedToId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int>("CreatedById")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("IssueDescription")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("IssueTopic")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("IssueTries")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int>("Priority")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("Subject")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SubjectId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AssignedToId");
|
||||
|
||||
b.HasIndex("CreatedById");
|
||||
|
||||
b.ToTable("Tickets");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.TicketMessage", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("AttachmentUrl")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Content")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<bool>("IsEdited")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("IsSupportMessage")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<bool>("IsSystemMessage")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<int?>("SenderId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int?>("TicketId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SenderId");
|
||||
|
||||
b.HasIndex("TicketId");
|
||||
|
||||
b.ToTable("TicketMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -766,6 +846,10 @@ namespace Moonlight.App.Database.Migrations
|
||||
b.Property<bool>("HasRated")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("LastIp")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("LastName")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
@@ -777,9 +861,24 @@ namespace Moonlight.App.Database.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<int?>("PermissionGroupId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<byte[]>("Permissions")
|
||||
.IsRequired()
|
||||
.HasColumnType("longblob");
|
||||
|
||||
b.Property<int>("Rating")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("RegisterIp")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("ServerListLayoutJson")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("State")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
@@ -787,8 +886,11 @@ namespace Moonlight.App.Database.Migrations
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("SubscriptionDuration")
|
||||
.HasColumnType("int");
|
||||
b.Property<bool>("StreamerMode")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<DateTime>("SubscriptionExpires")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<DateTime>("SubscriptionSince")
|
||||
.HasColumnType("datetime(6)");
|
||||
@@ -813,6 +915,8 @@ namespace Moonlight.App.Database.Migrations
|
||||
|
||||
b.HasIndex("CurrentSubscriptionId");
|
||||
|
||||
b.HasIndex("PermissionGroupId");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
@@ -853,6 +957,21 @@ namespace Moonlight.App.Database.Migrations
|
||||
b.ToTable("WebSpaces");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.WhitelistIp", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Ip")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("WhitelistIps");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.DdosAttack", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.App.Database.Entities.Node", "Node")
|
||||
@@ -1011,13 +1130,49 @@ namespace Moonlight.App.Database.Migrations
|
||||
b.Navigation("Sender");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.Ticket", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.App.Database.Entities.User", "AssignedTo")
|
||||
.WithMany()
|
||||
.HasForeignKey("AssignedToId");
|
||||
|
||||
b.HasOne("Moonlight.App.Database.Entities.User", "CreatedBy")
|
||||
.WithMany()
|
||||
.HasForeignKey("CreatedById")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("AssignedTo");
|
||||
|
||||
b.Navigation("CreatedBy");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.TicketMessage", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.App.Database.Entities.User", "Sender")
|
||||
.WithMany()
|
||||
.HasForeignKey("SenderId");
|
||||
|
||||
b.HasOne("Moonlight.App.Database.Entities.Ticket", null)
|
||||
.WithMany("Messages")
|
||||
.HasForeignKey("TicketId");
|
||||
|
||||
b.Navigation("Sender");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.User", b =>
|
||||
{
|
||||
b.HasOne("Moonlight.App.Database.Entities.Subscription", "CurrentSubscription")
|
||||
.WithMany()
|
||||
.HasForeignKey("CurrentSubscriptionId");
|
||||
|
||||
b.HasOne("Moonlight.App.Database.Entities.PermissionGroup", "PermissionGroup")
|
||||
.WithMany()
|
||||
.HasForeignKey("PermissionGroupId");
|
||||
|
||||
b.Navigation("CurrentSubscription");
|
||||
|
||||
b.Navigation("PermissionGroup");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b =>
|
||||
@@ -1060,6 +1215,11 @@ namespace Moonlight.App.Database.Migrations
|
||||
b.Navigation("Variables");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.Ticket", b =>
|
||||
{
|
||||
b.Navigation("Messages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b =>
|
||||
{
|
||||
b.Navigation("Databases");
|
||||
|
||||
30
Moonlight/App/Extensions/JSRuntimeExtensions.cs
Normal file
30
Moonlight/App/Extensions/JSRuntimeExtensions.cs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
119
Moonlight/App/Helpers/BackupHelper.cs
Normal file
119
Moonlight/App/Helpers/BackupHelper.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO.Compression;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Moonlight.App.Database;
|
||||
using Moonlight.App.Services;
|
||||
using MySql.Data.MySqlClient;
|
||||
|
||||
namespace Moonlight.App.Helpers;
|
||||
|
||||
public class BackupHelper
|
||||
{
|
||||
public async Task CreateBackup(string path)
|
||||
{
|
||||
Logger.Info("Started moonlight backup creation");
|
||||
Logger.Info($"This backup will be saved to '{path}'");
|
||||
|
||||
var stopWatch = new Stopwatch();
|
||||
stopWatch.Start();
|
||||
|
||||
var cachePath = PathBuilder.Dir("storage", "backups", "cache");
|
||||
|
||||
Directory.CreateDirectory(cachePath);
|
||||
|
||||
//
|
||||
// Exporting database
|
||||
//
|
||||
|
||||
Logger.Info("Exporting database");
|
||||
|
||||
var configService = new ConfigService(new());
|
||||
var dataContext = new DataContext(configService);
|
||||
|
||||
await using MySqlConnection conn = new MySqlConnection(dataContext.Database.GetConnectionString());
|
||||
await using MySqlCommand cmd = new MySqlCommand();
|
||||
using MySqlBackup mb = new MySqlBackup(cmd);
|
||||
|
||||
cmd.Connection = conn;
|
||||
await conn.OpenAsync();
|
||||
mb.ExportToFile(PathBuilder.File(cachePath, "database.sql"));
|
||||
await conn.CloseAsync();
|
||||
|
||||
//
|
||||
// Saving config
|
||||
//
|
||||
|
||||
Logger.Info("Saving configuration");
|
||||
File.Copy(
|
||||
PathBuilder.File("storage", "configs", "config.json"),
|
||||
PathBuilder.File(cachePath, "config.json"));
|
||||
|
||||
//
|
||||
// Saving all storage items needed to restore the panel
|
||||
//
|
||||
|
||||
Logger.Info("Saving resources");
|
||||
CopyDirectory(
|
||||
PathBuilder.Dir("storage", "resources"),
|
||||
PathBuilder.Dir(cachePath, "resources"));
|
||||
|
||||
Logger.Info("Saving logs");
|
||||
CopyDirectory(
|
||||
PathBuilder.Dir("storage", "logs"),
|
||||
PathBuilder.Dir(cachePath, "logs"));
|
||||
|
||||
Logger.Info("Saving uploads");
|
||||
CopyDirectory(
|
||||
PathBuilder.Dir("storage", "uploads"),
|
||||
PathBuilder.Dir(cachePath, "uploads"));
|
||||
|
||||
//
|
||||
// Compressing the backup to a single file
|
||||
//
|
||||
|
||||
Logger.Info("Compressing");
|
||||
ZipFile.CreateFromDirectory(cachePath,
|
||||
path,
|
||||
CompressionLevel.Fastest,
|
||||
false);
|
||||
|
||||
Directory.Delete(cachePath, true);
|
||||
|
||||
stopWatch.Stop();
|
||||
Logger.Info($"Backup successfully created. Took {stopWatch.Elapsed.TotalSeconds} seconds");
|
||||
}
|
||||
|
||||
private void CopyDirectory(string sourceDirName, string destDirName, bool copySubDirs = true)
|
||||
{
|
||||
DirectoryInfo dir = new DirectoryInfo(sourceDirName);
|
||||
|
||||
if (!dir.Exists)
|
||||
{
|
||||
throw new DirectoryNotFoundException($"Source directory does not exist or could not be found: {sourceDirName}");
|
||||
}
|
||||
|
||||
if (!Directory.Exists(destDirName))
|
||||
{
|
||||
Directory.CreateDirectory(destDirName);
|
||||
}
|
||||
|
||||
FileInfo[] files = dir.GetFiles();
|
||||
|
||||
foreach (FileInfo file in files)
|
||||
{
|
||||
string tempPath = Path.Combine(destDirName, file.Name);
|
||||
file.CopyTo(tempPath, false);
|
||||
}
|
||||
|
||||
if (copySubDirs)
|
||||
{
|
||||
DirectoryInfo[] dirs = dir.GetDirectories();
|
||||
|
||||
foreach (DirectoryInfo subdir in dirs)
|
||||
{
|
||||
string tempPath = Path.Combine(destDirName, subdir.Name);
|
||||
CopyDirectory(subdir.FullName, tempPath, copySubDirs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
88
Moonlight/App/Helpers/BitHelper.cs
Normal file
88
Moonlight/App/Helpers/BitHelper.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
namespace Moonlight.App.Helpers;
|
||||
|
||||
public class BitHelper
|
||||
{
|
||||
public static bool ReadBit(byte[] byteArray, int bitIndex)
|
||||
{
|
||||
if (bitIndex < 0)
|
||||
throw new ArgumentOutOfRangeException("bitIndex");
|
||||
|
||||
int byteIndex = bitIndex / 8;
|
||||
if (byteIndex >= byteArray.Length)
|
||||
throw new ArgumentOutOfRangeException("bitIndex");
|
||||
|
||||
int bitNumber = bitIndex % 8;
|
||||
byte mask = (byte)(1 << bitNumber);
|
||||
|
||||
return (byteArray[byteIndex] & mask) != 0;
|
||||
}
|
||||
|
||||
public static byte[] WriteBit(byte[] byteArray, int bitIndex, bool value)
|
||||
{
|
||||
if (bitIndex < 0)
|
||||
throw new ArgumentOutOfRangeException("bitIndex");
|
||||
|
||||
int byteIndex = bitIndex / 8;
|
||||
byte[] resultArray;
|
||||
|
||||
if (byteIndex >= byteArray.Length)
|
||||
{
|
||||
// Create a new array with increased size and copy elements from old array
|
||||
resultArray = new byte[byteIndex + 1];
|
||||
Array.Copy(byteArray, resultArray, byteArray.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a new array and copy elements from old array
|
||||
resultArray = new byte[byteArray.Length];
|
||||
Array.Copy(byteArray, resultArray, byteArray.Length);
|
||||
}
|
||||
|
||||
int bitNumber = bitIndex % 8;
|
||||
byte mask = (byte)(1 << bitNumber);
|
||||
|
||||
if (value)
|
||||
resultArray[byteIndex] |= mask; // Set the bit to 1
|
||||
else
|
||||
resultArray[byteIndex] &= (byte)~mask; // Set the bit to 0
|
||||
|
||||
return resultArray;
|
||||
}
|
||||
|
||||
public static byte[] OverwriteByteArrays(byte[] targetArray, byte[] overwriteArray)
|
||||
{
|
||||
int targetLength = targetArray.Length;
|
||||
int overwriteLength = overwriteArray.Length;
|
||||
|
||||
int maxLength = Math.Max(targetLength, overwriteLength);
|
||||
|
||||
byte[] resultArray = new byte[maxLength];
|
||||
|
||||
for (int i = 0; i < maxLength; i++)
|
||||
{
|
||||
byte targetByte = i < targetLength ? targetArray[i] : (byte)0;
|
||||
byte overwriteByte = i < overwriteLength ? overwriteArray[i] : (byte)0;
|
||||
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
bool overwriteBit = (overwriteByte & (1 << j)) != 0;
|
||||
if (i < targetLength)
|
||||
{
|
||||
bool targetBit = (targetByte & (1 << j)) != 0;
|
||||
if (overwriteBit)
|
||||
{
|
||||
targetByte = targetBit ? (byte)(targetByte | (1 << j)) : (byte)(targetByte & ~(1 << j));
|
||||
}
|
||||
}
|
||||
else if (overwriteBit)
|
||||
{
|
||||
targetByte |= (byte)(1 << j);
|
||||
}
|
||||
}
|
||||
|
||||
resultArray[i] = targetByte;
|
||||
}
|
||||
|
||||
return resultArray;
|
||||
}
|
||||
}
|
||||
6
Moonlight/App/Helpers/BlurAttribute.cs
Normal file
6
Moonlight/App/Helpers/BlurAttribute.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Moonlight.App.Helpers;
|
||||
|
||||
public class BlurAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
56
Moonlight/App/Helpers/ByteSizeValue.cs
Normal file
56
Moonlight/App/Helpers/ByteSizeValue.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
namespace Moonlight.App.Helpers;
|
||||
|
||||
public class ByteSizeValue
|
||||
{
|
||||
public long Bytes { get; set; }
|
||||
|
||||
public long KiloBytes
|
||||
{
|
||||
get => Bytes / 1024;
|
||||
set => Bytes = value * 1024;
|
||||
}
|
||||
|
||||
public long MegaBytes
|
||||
{
|
||||
get => KiloBytes / 1024;
|
||||
set => KiloBytes = value * 1024;
|
||||
}
|
||||
|
||||
public long GigaBytes
|
||||
{
|
||||
get => MegaBytes / 1024;
|
||||
set => MegaBytes = value * 1024;
|
||||
}
|
||||
|
||||
public static ByteSizeValue FromBytes(long bytes)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
Bytes = bytes
|
||||
};
|
||||
}
|
||||
|
||||
public static ByteSizeValue FromKiloBytes(long kiloBytes)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
KiloBytes = kiloBytes
|
||||
};
|
||||
}
|
||||
|
||||
public static ByteSizeValue FromMegaBytes(long megaBytes)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
MegaBytes = megaBytes
|
||||
};
|
||||
}
|
||||
|
||||
public static ByteSizeValue FromGigaBytes(long gigaBytes)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
GigaBytes = gigaBytes
|
||||
};
|
||||
}
|
||||
}
|
||||
12
Moonlight/App/Helpers/ComponentHelper.cs
Normal file
12
Moonlight/App/Helpers/ComponentHelper.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Moonlight.App.Helpers;
|
||||
|
||||
public class ComponentHelper
|
||||
{
|
||||
public static RenderFragment FromType(Type type) => builder =>
|
||||
{
|
||||
builder.OpenComponent(0, type);
|
||||
builder.CloseComponent();
|
||||
};
|
||||
}
|
||||
@@ -45,7 +45,9 @@ public class DatabaseCheckupService
|
||||
{
|
||||
Logger.Info($"{migrations.Length} migrations pending. Updating now");
|
||||
|
||||
await BackupDatabase();
|
||||
var backupHelper = new BackupHelper();
|
||||
await backupHelper.CreateBackup(
|
||||
PathBuilder.File("storage", "backups", $"{new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds()}.zip"));
|
||||
|
||||
Logger.Info("Applying migrations");
|
||||
|
||||
@@ -58,55 +60,4 @@ public class DatabaseCheckupService
|
||||
Logger.Info("Database is up-to-date. No migrations have been performed");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task BackupDatabase()
|
||||
{
|
||||
Logger.Info("Creating backup from database");
|
||||
|
||||
var configService = new ConfigService(new StorageService());
|
||||
var dateTimeService = new DateTimeService();
|
||||
|
||||
var config = configService
|
||||
.GetSection("Moonlight")
|
||||
.GetSection("Database");
|
||||
|
||||
var connectionString = $"host={config.GetValue<string>("Host")};" +
|
||||
$"port={config.GetValue<int>("Port")};" +
|
||||
$"database={config.GetValue<string>("Database")};" +
|
||||
$"uid={config.GetValue<string>("Username")};" +
|
||||
$"pwd={config.GetValue<string>("Password")}";
|
||||
|
||||
string file = PathBuilder.File("storage", "backups", $"{dateTimeService.GetCurrentUnix()}-mysql.sql");
|
||||
|
||||
Logger.Info($"Saving it to: {file}");
|
||||
Logger.Info("Starting backup...");
|
||||
|
||||
try
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
await using MySqlConnection conn = new MySqlConnection(connectionString);
|
||||
await using MySqlCommand cmd = new MySqlCommand();
|
||||
using MySqlBackup mb = new MySqlBackup(cmd);
|
||||
|
||||
cmd.Connection = conn;
|
||||
await conn.OpenAsync();
|
||||
mb.ExportToFile(file);
|
||||
await conn.CloseAsync();
|
||||
|
||||
sw.Stop();
|
||||
Logger.Info($"Done. {sw.Elapsed.TotalSeconds}s");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Fatal("-----------------------------------------------");
|
||||
Logger.Fatal("Unable to create backup for moonlight database");
|
||||
Logger.Fatal("Moonlight will start anyways in 30 seconds");
|
||||
Logger.Fatal("-----------------------------------------------");
|
||||
Logger.Fatal(e);
|
||||
|
||||
Thread.Sleep(TimeSpan.FromSeconds(30));
|
||||
}
|
||||
}
|
||||
}
|
||||
71
Moonlight/App/Helpers/EggConverter.cs
Normal file
71
Moonlight/App/Helpers/EggConverter.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System.Text;
|
||||
using Moonlight.App.Database.Entities;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Moonlight.App.Helpers;
|
||||
|
||||
public static class EggConverter
|
||||
{
|
||||
public static Image Convert(string json)
|
||||
{
|
||||
var result = new Image();
|
||||
|
||||
var data = new ConfigurationBuilder().AddJsonStream(
|
||||
new MemoryStream(Encoding.ASCII.GetBytes(json))
|
||||
).Build();
|
||||
|
||||
result.Allocations = 1;
|
||||
result.Description = data.GetValue<string>("description") ?? "";
|
||||
result.Uuid = Guid.NewGuid();
|
||||
result.Startup = data.GetValue<string>("startup") ?? "";
|
||||
result.Name = data.GetValue<string>("name") ?? "Ptero Egg";
|
||||
|
||||
foreach (var variable in data.GetSection("variables").GetChildren())
|
||||
{
|
||||
result.Variables.Add(new()
|
||||
{
|
||||
Key = variable.GetValue<string>("env_variable") ?? "",
|
||||
DefaultValue = variable.GetValue<string>("default_value") ?? ""
|
||||
});
|
||||
}
|
||||
|
||||
var configData = data.GetSection("config");
|
||||
|
||||
result.ConfigFiles = configData.GetValue<string>("files") ?? "{}";
|
||||
|
||||
var dImagesData = JObject.Parse(json);
|
||||
var dImages = (JObject)dImagesData["docker_images"]!;
|
||||
|
||||
foreach (var dockerImage in dImages)
|
||||
{
|
||||
var di = new DockerImage()
|
||||
{
|
||||
Default = dockerImage.Key == dImages.Properties().Last().Name,
|
||||
Name = dockerImage.Value!.ToString()
|
||||
};
|
||||
|
||||
result.DockerImages.Add(di);
|
||||
}
|
||||
|
||||
var installSection = data.GetSection("scripts").GetSection("installation");
|
||||
|
||||
result.InstallEntrypoint = installSection.GetValue<string>("entrypoint") ?? "bash";
|
||||
result.InstallScript = installSection.GetValue<string>("script") ?? "";
|
||||
result.InstallDockerImage = installSection.GetValue<string>("container") ?? "";
|
||||
|
||||
var rawJson = configData.GetValue<string>("startup");
|
||||
|
||||
var startupData = new ConfigurationBuilder().AddJsonStream(
|
||||
new MemoryStream(Encoding.ASCII.GetBytes(rawJson!))
|
||||
).Build();
|
||||
|
||||
result.StartupDetection = startupData.GetValue<string>("done", "") ?? "";
|
||||
result.StopCommand = configData.GetValue<string>("stop") ?? "";
|
||||
|
||||
result.TagsJson = "[]";
|
||||
result.BackgroundImageUrl = "";
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -43,19 +43,22 @@ public class WingsFileAccess : FileAccess
|
||||
$"api/servers/{Server.Uuid}/files/list-directory?directory={CurrentPath}"
|
||||
);
|
||||
|
||||
var x = new List<FileData>();
|
||||
var result = new List<FileData>();
|
||||
|
||||
foreach (var response in res)
|
||||
foreach (var resGrouped in res.GroupBy(x => x.Directory))
|
||||
{
|
||||
x.Add(new()
|
||||
foreach (var resItem in resGrouped.OrderBy(x => x.Name))
|
||||
{
|
||||
Name = response.Name,
|
||||
Size = response.File ? response.Size : 0,
|
||||
IsFile = response.File,
|
||||
result.Add(new()
|
||||
{
|
||||
Name = resItem.Name,
|
||||
Size = resItem.File ? resItem.Size : 0,
|
||||
IsFile = resItem.File,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return x.ToArray();
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
public override Task Cd(string dir)
|
||||
@@ -111,7 +114,7 @@ public class WingsFileAccess : FileAccess
|
||||
request.AddParameter("name", "files");
|
||||
request.AddParameter("filename", name);
|
||||
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", () =>
|
||||
{
|
||||
return new StreamProgressHelper(dataStream)
|
||||
|
||||
@@ -1,9 +1,36 @@
|
||||
using Moonlight.App.Services;
|
||||
using System.Text;
|
||||
using Moonlight.App.Services;
|
||||
|
||||
namespace Moonlight.App.Helpers;
|
||||
|
||||
public static class Formatter
|
||||
{
|
||||
public static string ReplaceEnd(string input, string substringToReplace, string newSubstring)
|
||||
{
|
||||
int lastIndexOfSubstring = input.LastIndexOf(substringToReplace);
|
||||
if (lastIndexOfSubstring >= 0)
|
||||
{
|
||||
input = input.Remove(lastIndexOfSubstring, substringToReplace.Length).Insert(lastIndexOfSubstring, newSubstring);
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
public static string ConvertCamelCaseToSpaces(string input)
|
||||
{
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
foreach (char c in input)
|
||||
{
|
||||
if (char.IsUpper(c))
|
||||
{
|
||||
output.Append(' ');
|
||||
}
|
||||
|
||||
output.Append(c);
|
||||
}
|
||||
|
||||
return output.ToString().Trim();
|
||||
}
|
||||
public static string FormatUptime(double uptime)
|
||||
{
|
||||
TimeSpan t = TimeSpan.FromMilliseconds(uptime);
|
||||
|
||||
@@ -1,46 +1,70 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using Moonlight.App.Database;
|
||||
using Moonlight.App.Services;
|
||||
using Moonlight.App.Services.Files;
|
||||
using Serilog;
|
||||
|
||||
namespace Moonlight.App.Helpers;
|
||||
|
||||
public static class Logger
|
||||
{
|
||||
// The private static instance of the config service, because we have no di here
|
||||
private static ConfigService ConfigService = new(new StorageService());
|
||||
|
||||
#region String method calls
|
||||
public static void Verbose(string message, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Verbose("{Message}", message);
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(message);
|
||||
}
|
||||
|
||||
public static void Info(string message, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Information("{Message}", message);
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(message);
|
||||
}
|
||||
|
||||
public static void Debug(string message, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Debug("{Message}", message);
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(message);
|
||||
}
|
||||
|
||||
public static void Error(string message, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Error("{Message}", message);
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(message);
|
||||
}
|
||||
|
||||
public static void Warn(string message, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Warning("{Message}", message);
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(message);
|
||||
}
|
||||
|
||||
public static void Fatal(string message, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Fatal("{Message}", message);
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(message);
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -49,36 +73,54 @@ public static class Logger
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Verbose(exception, "");
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(exception);
|
||||
}
|
||||
|
||||
public static void Info(Exception exception, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Information(exception, "");
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(exception);
|
||||
}
|
||||
|
||||
public static void Debug(Exception exception, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Debug(exception, "");
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(exception);
|
||||
}
|
||||
|
||||
public static void Error(Exception exception, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Error(exception, "");
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(exception);
|
||||
}
|
||||
|
||||
public static void Warn(Exception exception, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Warning(exception, "");
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(exception);
|
||||
}
|
||||
|
||||
public static void Fatal(Exception exception, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Fatal(exception, "");
|
||||
|
||||
if(channel == "security")
|
||||
LogSecurityInDb(exception);
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -105,4 +147,25 @@ public static class Logger
|
||||
|
||||
return fullName;
|
||||
}
|
||||
|
||||
|
||||
private static void LogSecurityInDb(Exception exception)
|
||||
{
|
||||
LogSecurityInDb(exception.ToStringDemystified());
|
||||
}
|
||||
private static void LogSecurityInDb(string text)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
var dataContext = new DataContext(ConfigService);
|
||||
|
||||
dataContext.SecurityLogs.Add(new()
|
||||
{
|
||||
Text = text
|
||||
});
|
||||
|
||||
dataContext.SaveChanges();
|
||||
dataContext.Dispose();
|
||||
});
|
||||
}
|
||||
}
|
||||
51
Moonlight/App/Helpers/PropBinder.cs
Normal file
51
Moonlight/App/Helpers/PropBinder.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace Moonlight.App.Helpers;
|
||||
|
||||
public class PropBinder
|
||||
{
|
||||
private PropertyInfo PropertyInfo;
|
||||
private object DataObject;
|
||||
|
||||
public PropBinder(PropertyInfo propertyInfo, object dataObject)
|
||||
{
|
||||
PropertyInfo = propertyInfo;
|
||||
DataObject = dataObject;
|
||||
}
|
||||
|
||||
public string StringValue
|
||||
{
|
||||
get => (string)PropertyInfo.GetValue(DataObject)!;
|
||||
set => PropertyInfo.SetValue(DataObject, value);
|
||||
}
|
||||
|
||||
public int IntValue
|
||||
{
|
||||
get => (int)PropertyInfo.GetValue(DataObject)!;
|
||||
set => PropertyInfo.SetValue(DataObject, value);
|
||||
}
|
||||
|
||||
public long LongValue
|
||||
{
|
||||
get => (long)PropertyInfo.GetValue(DataObject)!;
|
||||
set => PropertyInfo.SetValue(DataObject, value);
|
||||
}
|
||||
|
||||
public bool BoolValue
|
||||
{
|
||||
get => (bool)PropertyInfo.GetValue(DataObject)!;
|
||||
set => PropertyInfo.SetValue(DataObject, value);
|
||||
}
|
||||
|
||||
public DateTime DateTimeValue
|
||||
{
|
||||
get => (DateTime)PropertyInfo.GetValue(DataObject)!;
|
||||
set => PropertyInfo.SetValue(DataObject, value);
|
||||
}
|
||||
|
||||
public double DoubleValue
|
||||
{
|
||||
get => (double)PropertyInfo.GetValue(DataObject)!;
|
||||
set => PropertyInfo.SetValue(DataObject, value);
|
||||
}
|
||||
}
|
||||
66
Moonlight/App/Helpers/Retry.cs
Normal file
66
Moonlight/App/Helpers/Retry.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
namespace Moonlight.App.Helpers;
|
||||
|
||||
public class Retry
|
||||
{
|
||||
private List<Type> RetryExceptionTypes;
|
||||
private List<Func<Exception, bool>> RetryFilters;
|
||||
private int RetryTimes = 1;
|
||||
|
||||
public Retry()
|
||||
{
|
||||
RetryExceptionTypes = new();
|
||||
RetryFilters = new();
|
||||
}
|
||||
|
||||
public Retry Times(int times)
|
||||
{
|
||||
RetryTimes = times;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Retry At(Func<Exception, bool> filter)
|
||||
{
|
||||
RetryFilters.Add(filter);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Retry At<T>()
|
||||
{
|
||||
RetryExceptionTypes.Add(typeof(T));
|
||||
return this;
|
||||
}
|
||||
|
||||
public async Task Call(Func<Task> method)
|
||||
{
|
||||
int triesLeft = RetryTimes;
|
||||
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
await method.Invoke();
|
||||
return;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if(triesLeft < 1) // Throw if no tries left
|
||||
throw;
|
||||
|
||||
if (RetryExceptionTypes.Any(x => x.FullName == e.GetType().FullName))
|
||||
{
|
||||
triesLeft--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (RetryFilters.Any(x => x.Invoke(e)))
|
||||
{
|
||||
triesLeft--;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Throw if not filtered -> unknown/unhandled
|
||||
throw;
|
||||
}
|
||||
} while (triesLeft >= 0);
|
||||
}
|
||||
}
|
||||
@@ -243,6 +243,7 @@ public class WingsConsole : IDisposable
|
||||
}
|
||||
}
|
||||
catch(JsonReaderException){}
|
||||
catch(JsonSerializationException){}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (!Disconnecting)
|
||||
|
||||
@@ -20,7 +20,7 @@ public class WingsConsoleHelper
|
||||
{
|
||||
ServerRepository = serverRepository;
|
||||
|
||||
AppUrl = configService.GetSection("Moonlight").GetValue<string>("AppUrl");
|
||||
AppUrl = configService.Get().Moonlight.AppUrl;
|
||||
}
|
||||
|
||||
public async Task ConnectWings(WingsConsole console, Server server)
|
||||
|
||||
@@ -15,7 +15,7 @@ public class WingsJwtHelper
|
||||
{
|
||||
ConfigService = configService;
|
||||
|
||||
AppUrl = ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl");
|
||||
AppUrl = ConfigService.Get().Moonlight.AppUrl;
|
||||
}
|
||||
|
||||
public string Generate(string secret, Action<Dictionary<string, string>> claimsAction)
|
||||
|
||||
@@ -25,7 +25,7 @@ public class AvatarController : Controller
|
||||
|
||||
try
|
||||
{
|
||||
var url = GravatarController.GetImageUrl(user.Email, 100);
|
||||
var url = GravatarController.GetImageUrl(user.Email.ToLower(), 100);
|
||||
|
||||
using var client = new HttpClient();
|
||||
var res = await client.GetByteArrayAsync(url);
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moonlight.App.Services;
|
||||
using Moonlight.App.Services.Sessions;
|
||||
using Stripe;
|
||||
using Stripe.Checkout;
|
||||
|
||||
namespace Moonlight.App.Http.Controllers.Api.Moonlight;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/moonlight/billing")]
|
||||
public class BillingController : Controller
|
||||
{
|
||||
private readonly IdentityService IdentityService;
|
||||
private readonly BillingService BillingService;
|
||||
|
||||
public BillingController(
|
||||
IdentityService identityService,
|
||||
BillingService billingService)
|
||||
{
|
||||
IdentityService = identityService;
|
||||
BillingService = billingService;
|
||||
}
|
||||
|
||||
[HttpGet("cancel")]
|
||||
public async Task<ActionResult> Cancel()
|
||||
{
|
||||
var user = IdentityService.User;
|
||||
|
||||
if (user == null)
|
||||
return Redirect("/login");
|
||||
|
||||
return Redirect("/profile/subscriptions/close");
|
||||
}
|
||||
|
||||
[HttpGet("success")]
|
||||
public async Task<ActionResult> Success()
|
||||
{
|
||||
var user = IdentityService.User;
|
||||
|
||||
if (user == null)
|
||||
return Redirect("/login");
|
||||
|
||||
await BillingService.CompleteCheckout(user);
|
||||
|
||||
return Redirect("/profile/subscriptions/close");
|
||||
}
|
||||
}
|
||||
@@ -30,14 +30,14 @@ public class DiscordBotController : Controller
|
||||
ServerService = serverService;
|
||||
|
||||
var config = configService
|
||||
.GetSection("Moonlight")
|
||||
.GetSection("DiscordBotApi");
|
||||
.Get()
|
||||
.Moonlight.DiscordBotApi;
|
||||
|
||||
Enable = config.GetValue<bool>("Enable");
|
||||
Enable = config.Enable;
|
||||
|
||||
if (Enable)
|
||||
{
|
||||
Token = config.GetValue<string>("Token");
|
||||
Token = config.Token;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ public class RegisterController : Controller
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<TokenRegister>> Register()
|
||||
{
|
||||
var user = await IdentityService.Get();
|
||||
var user = IdentityService.User;
|
||||
|
||||
if (user == null)
|
||||
return NotFound();
|
||||
|
||||
@@ -54,7 +54,7 @@ public class OAuth2Controller : Controller
|
||||
{
|
||||
try
|
||||
{
|
||||
var currentUser = await IdentityService.Get();
|
||||
var currentUser = IdentityService.User;
|
||||
|
||||
if (currentUser != null)
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@ using Moonlight.App.Database.Entities;
|
||||
using Moonlight.App.Events;
|
||||
using Moonlight.App.Http.Requests.Daemon;
|
||||
using Moonlight.App.Repositories;
|
||||
using Moonlight.App.Services.Background;
|
||||
|
||||
namespace Moonlight.App.Http.Controllers.Api.Remote;
|
||||
|
||||
@@ -10,19 +11,17 @@ namespace Moonlight.App.Http.Controllers.Api.Remote;
|
||||
[Route("api/remote/ddos")]
|
||||
public class DdosController : Controller
|
||||
{
|
||||
private readonly NodeRepository NodeRepository;
|
||||
private readonly EventSystem Event;
|
||||
private readonly DdosAttackRepository DdosAttackRepository;
|
||||
private readonly Repository<Node> NodeRepository;
|
||||
private readonly DdosProtectionService DdosProtectionService;
|
||||
|
||||
public DdosController(NodeRepository nodeRepository, EventSystem eventSystem, DdosAttackRepository ddosAttackRepository)
|
||||
public DdosController(Repository<Node> nodeRepository, DdosProtectionService ddosProtectionService)
|
||||
{
|
||||
NodeRepository = nodeRepository;
|
||||
Event = eventSystem;
|
||||
DdosAttackRepository = ddosAttackRepository;
|
||||
DdosProtectionService = ddosProtectionService;
|
||||
}
|
||||
|
||||
[HttpPost("update")]
|
||||
public async Task<ActionResult> Update([FromBody] DdosStatus ddosStatus)
|
||||
[HttpPost("start")]
|
||||
public async Task<ActionResult> Start([FromBody] DdosStart ddosStart)
|
||||
{
|
||||
var tokenData = Request.Headers.Authorization.ToString().Replace("Bearer ", "");
|
||||
var id = tokenData.Split(".")[0];
|
||||
@@ -36,17 +35,25 @@ public class DdosController : Controller
|
||||
if (token != node.Token)
|
||||
return Unauthorized();
|
||||
|
||||
var ddosAttack = new DdosAttack()
|
||||
await DdosProtectionService.ProcessDdosSignal(ddosStart.Ip, ddosStart.Packets);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
[HttpPost("stop")]
|
||||
public async Task<ActionResult> Stop([FromBody] DdosStop ddosStop)
|
||||
{
|
||||
Ongoing = ddosStatus.Ongoing,
|
||||
Data = ddosStatus.Data,
|
||||
Ip = ddosStatus.Ip,
|
||||
Node = node
|
||||
};
|
||||
var tokenData = Request.Headers.Authorization.ToString().Replace("Bearer ", "");
|
||||
var id = tokenData.Split(".")[0];
|
||||
var token = tokenData.Split(".")[1];
|
||||
|
||||
ddosAttack = DdosAttackRepository.Add(ddosAttack);
|
||||
var node = NodeRepository.Get().FirstOrDefault(x => x.TokenId == id);
|
||||
|
||||
await Event.Emit("node.ddos", ddosAttack);
|
||||
if (node == null)
|
||||
return NotFound();
|
||||
|
||||
if (token != node.Token)
|
||||
return Unauthorized();
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
7
Moonlight/App/Http/Requests/Daemon/DdosStart.cs
Normal file
7
Moonlight/App/Http/Requests/Daemon/DdosStart.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Moonlight.App.Http.Requests.Daemon;
|
||||
|
||||
public class DdosStart
|
||||
{
|
||||
public string Ip { get; set; } = "";
|
||||
public long Packets { get; set; }
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace Moonlight.App.Http.Requests.Daemon;
|
||||
|
||||
public class DdosStatus
|
||||
{
|
||||
public bool Ongoing { get; set; }
|
||||
public long Data { get; set; }
|
||||
public string Ip { get; set; } = "";
|
||||
}
|
||||
7
Moonlight/App/Http/Requests/Daemon/DdosStop.cs
Normal file
7
Moonlight/App/Http/Requests/Daemon/DdosStop.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Moonlight.App.Http.Requests.Daemon;
|
||||
|
||||
public class DdosStop
|
||||
{
|
||||
public string Ip { get; set; } = "";
|
||||
public long Traffic { get; set; }
|
||||
}
|
||||
@@ -27,19 +27,39 @@ public class LogMigrator : ILogger
|
||||
switch (logLevel)
|
||||
{
|
||||
case LogLevel.Critical:
|
||||
Logger.Fatal($"[{Name}] {formatter(state, exception)}");
|
||||
Logger.Fatal(formatter(state, exception));
|
||||
|
||||
if(exception != null)
|
||||
Logger.Fatal(exception);
|
||||
|
||||
break;
|
||||
case LogLevel.Warning:
|
||||
Logger.Warn($"[{Name}] {formatter(state, exception)}");
|
||||
Logger.Warn(formatter(state, exception));
|
||||
|
||||
if(exception != null)
|
||||
Logger.Warn(exception);
|
||||
|
||||
break;
|
||||
case LogLevel.Debug:
|
||||
Logger.Debug($"[{Name}] {formatter(state, exception)}");
|
||||
Logger.Debug(formatter(state, exception));
|
||||
|
||||
if(exception != null)
|
||||
Logger.Debug(exception);
|
||||
|
||||
break;
|
||||
case LogLevel.Error:
|
||||
Logger.Error($"[{Name}] {formatter(state, exception)}");
|
||||
Logger.Error(formatter(state, exception));
|
||||
|
||||
if(exception != null)
|
||||
Logger.Error(exception);
|
||||
|
||||
break;
|
||||
case LogLevel.Information:
|
||||
Logger.Info($"[{Name}] {formatter(state, exception)}");
|
||||
Logger.Info(formatter(state, exception));
|
||||
|
||||
if(exception != null)
|
||||
Logger.Info(exception);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
71
Moonlight/App/LogMigrator/SentryDiagnosticsLogger.cs
Normal file
71
Moonlight/App/LogMigrator/SentryDiagnosticsLogger.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
21
Moonlight/App/Models/Forms/CreateTicketDataModel.cs
Normal file
21
Moonlight/App/Models/Forms/CreateTicketDataModel.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Moonlight.App.Models.Misc;
|
||||
|
||||
namespace Moonlight.App.Models.Forms;
|
||||
|
||||
public class CreateTicketDataModel
|
||||
{
|
||||
[Required(ErrorMessage = "You need to specify a issue topic")]
|
||||
[MinLength(5, ErrorMessage = "The issue topic needs to be longer than 5 characters")]
|
||||
public string IssueTopic { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "You need to specify a issue description")]
|
||||
[MinLength(10, ErrorMessage = "The issue description needs to be longer than 10 characters")]
|
||||
public string IssueDescription { get; set; }
|
||||
|
||||
[Required(ErrorMessage = "You need to specify your tries to solve this issue")]
|
||||
public string IssueTries { get; set; }
|
||||
|
||||
public TicketSubject Subject { get; set; }
|
||||
public int SubjectId { get; set; }
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Moonlight.App.Models.Misc;
|
||||
|
||||
namespace Moonlight.App.Models.Forms;
|
||||
|
||||
@@ -10,4 +11,8 @@ public class SubscriptionDataModel
|
||||
|
||||
[Required(ErrorMessage = "You need to enter a description")]
|
||||
public string Description { get; set; } = "";
|
||||
|
||||
public double Price { get; set; } = 0;
|
||||
public Currency Currency { get; set; } = Currency.USD;
|
||||
public int Duration { get; set; } = 30;
|
||||
}
|
||||
34
Moonlight/App/Models/Forms/UserEditDataModel.cs
Normal file
34
Moonlight/App/Models/Forms/UserEditDataModel.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Moonlight.App.Database.Entities;
|
||||
using Moonlight.App.Models.Misc;
|
||||
|
||||
namespace Moonlight.App.Models.Forms;
|
||||
|
||||
public class UserEditDataModel
|
||||
{
|
||||
[Required]
|
||||
public string FirstName { get; set; } = "";
|
||||
|
||||
[Required]
|
||||
public string LastName { get; set; } = "";
|
||||
|
||||
[Required]
|
||||
public string Email { get; set; } = "";
|
||||
|
||||
[Required]
|
||||
public string Address { get; set; } = "";
|
||||
|
||||
[Required]
|
||||
public string City { get; set; } = "";
|
||||
|
||||
[Required]
|
||||
public string State { get; set; } = "";
|
||||
|
||||
[Required]
|
||||
public string Country { get; set; } = "";
|
||||
|
||||
public bool Admin { get; set; }
|
||||
public bool TotpEnabled { get; set; }
|
||||
public ulong DiscordId { get; set; }
|
||||
public PermissionGroup? PermissionGroup { get; set; }
|
||||
}
|
||||
6
Moonlight/App/Models/Forms/UserPreferencesDataModel.cs
Normal file
6
Moonlight/App/Models/Forms/UserPreferencesDataModel.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Moonlight.App.Models.Forms;
|
||||
|
||||
public class UserPreferencesDataModel
|
||||
{
|
||||
public bool StreamerMode { get; set; } = false;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
namespace Moonlight.App.Models.Misc;
|
||||
|
||||
public enum AuditLogType
|
||||
{
|
||||
Login,
|
||||
Register,
|
||||
ChangePassword,
|
||||
ChangePowerState,
|
||||
CreateBackup,
|
||||
RestoreBackup,
|
||||
DeleteBackup,
|
||||
DownloadBackup,
|
||||
CreateServer,
|
||||
ReinstallServer,
|
||||
CancelSubscription,
|
||||
ApplySubscriptionCode,
|
||||
EnableTotp,
|
||||
DisableTotp,
|
||||
AddDomainRecord,
|
||||
UpdateDomainRecord,
|
||||
DeleteDomainRecord,
|
||||
PasswordReset,
|
||||
CleanupEnabled,
|
||||
CleanupDisabled,
|
||||
CleanupTriggered,
|
||||
PasswordChange,
|
||||
}
|
||||
7
Moonlight/App/Models/Misc/Currency.cs
Normal file
7
Moonlight/App/Models/Misc/Currency.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Moonlight.App.Models.Misc;
|
||||
|
||||
public enum Currency
|
||||
{
|
||||
USD = 1,
|
||||
EUR = 2
|
||||
}
|
||||
9
Moonlight/App/Models/Misc/MailTemplate.cs
Normal file
9
Moonlight/App/Models/Misc/MailTemplate.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Moonlight.App.Helpers.Files;
|
||||
|
||||
namespace Moonlight.App.Models.Misc;
|
||||
|
||||
public class MailTemplate // This is just for the blazor table at /admin/system/mail
|
||||
{
|
||||
public string Name { get; set; } = "";
|
||||
public FileData File { get; set; }
|
||||
}
|
||||
6
Moonlight/App/Models/Misc/OfficialMoonlightPlugin.cs
Normal file
6
Moonlight/App/Models/Misc/OfficialMoonlightPlugin.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Moonlight.App.Models.Misc;
|
||||
|
||||
public class OfficialMoonlightPlugin
|
||||
{
|
||||
public string Name { get; set; }
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Moonlight.App.Models.Misc;
|
||||
|
||||
public enum SecurityLogType
|
||||
{
|
||||
ManipulatedJwt,
|
||||
PathTransversal,
|
||||
SftpBruteForce,
|
||||
LoginFail
|
||||
}
|
||||
7
Moonlight/App/Models/Misc/ServerGroup.cs
Normal file
7
Moonlight/App/Models/Misc/ServerGroup.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Moonlight.App.Models.Misc;
|
||||
|
||||
public class ServerGroup
|
||||
{
|
||||
public string Name { get; set; } = "";
|
||||
public List<string> Servers { get; set; } = new();
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Moonlight.App.Database.Entities;
|
||||
using Moonlight.App.Services.Interop;
|
||||
|
||||
namespace Moonlight.App.Models.Misc;
|
||||
|
||||
public class Session
|
||||
{
|
||||
public string Ip { get; set; } = "N/A";
|
||||
public string Url { get; set; } = "N/A";
|
||||
public string Device { get; set; } = "N/A";
|
||||
public User? User { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public NavigationManager Navigation { get; set; }
|
||||
public AlertService AlertService { get; set; }
|
||||
}
|
||||
9
Moonlight/App/Models/Misc/TicketPriority.cs
Normal file
9
Moonlight/App/Models/Misc/TicketPriority.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Moonlight.App.Models.Misc;
|
||||
|
||||
public enum TicketPriority
|
||||
{
|
||||
Low = 0,
|
||||
Medium = 1,
|
||||
High = 2,
|
||||
Critical = 3
|
||||
}
|
||||
9
Moonlight/App/Models/Misc/TicketStatus.cs
Normal file
9
Moonlight/App/Models/Misc/TicketStatus.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Moonlight.App.Models.Misc;
|
||||
|
||||
public enum TicketStatus
|
||||
{
|
||||
Closed = 0,
|
||||
Open = 1,
|
||||
WaitingForUser = 2,
|
||||
Pending = 3
|
||||
}
|
||||
9
Moonlight/App/Models/Misc/TicketSubject.cs
Normal file
9
Moonlight/App/Models/Misc/TicketSubject.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Moonlight.App.Models.Misc;
|
||||
|
||||
public enum TicketSubject
|
||||
{
|
||||
Webspace = 0,
|
||||
Server = 1,
|
||||
Domain = 2,
|
||||
Other = 3
|
||||
}
|
||||
@@ -86,6 +86,13 @@ public class DiscordOAuth2Provider : OAuth2Provider
|
||||
|
||||
var email = getData.GetValue<string>("email");
|
||||
var id = getData.GetValue<ulong>("id");
|
||||
var verified = getData.GetValue<bool>("verified");
|
||||
|
||||
if (!verified)
|
||||
{
|
||||
Logger.Warn("A user tried to use an unverified discord account to login", "security");
|
||||
throw new DisplayException("You can only use verified discord accounts for oauth signin");
|
||||
}
|
||||
|
||||
// Handle data
|
||||
|
||||
|
||||
10
Moonlight/App/Perms/Permission.cs
Normal file
10
Moonlight/App/Perms/Permission.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Moonlight.App.Perms;
|
||||
|
||||
public class Permission
|
||||
{
|
||||
public int Index { get; set; } = 0;
|
||||
public string Name { get; set; } = "";
|
||||
public string Description { get; set; } = "";
|
||||
|
||||
public static implicit operator int(Permission permission) => permission.Index;
|
||||
}
|
||||
11
Moonlight/App/Perms/PermissionRequired.cs
Normal file
11
Moonlight/App/Perms/PermissionRequired.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace Moonlight.App.Perms;
|
||||
|
||||
public class PermissionRequired : Attribute
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
|
||||
public PermissionRequired(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
55
Moonlight/App/Perms/PermissionStorage.cs
Normal file
55
Moonlight/App/Perms/PermissionStorage.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System.Data;
|
||||
using Moonlight.App.Helpers;
|
||||
|
||||
namespace Moonlight.App.Perms;
|
||||
|
||||
public class PermissionStorage
|
||||
{
|
||||
public byte[] Data;
|
||||
public bool IsReadyOnly;
|
||||
|
||||
public PermissionStorage(byte[] data, bool isReadyOnly = false)
|
||||
{
|
||||
Data = data;
|
||||
IsReadyOnly = isReadyOnly;
|
||||
}
|
||||
|
||||
public bool this[Permission permission]
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
return BitHelper.ReadBit(Data, permission.Index);
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Verbose("Error reading permissions. (Can be intentional)");
|
||||
Logger.Verbose(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
if (IsReadyOnly)
|
||||
throw new ReadOnlyException();
|
||||
|
||||
Data = BitHelper.WriteBit(Data, permission.Index, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasAnyPermissions()
|
||||
{
|
||||
foreach (var permission in Permissions.GetAllPermissions())
|
||||
{
|
||||
if (this[permission])
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
438
Moonlight/App/Perms/Permissions.cs
Normal file
438
Moonlight/App/Perms/Permissions.cs
Normal file
@@ -0,0 +1,438 @@
|
||||
namespace Moonlight.App.Perms;
|
||||
|
||||
public static class Permissions
|
||||
{
|
||||
public static Permission AdminDashboard = new()
|
||||
{
|
||||
Index = 0,
|
||||
Name = "Admin Dashboard",
|
||||
Description = "Access the main admin dashboard page"
|
||||
};
|
||||
|
||||
public static Permission AdminStatistics = new()
|
||||
{
|
||||
Index = 1,
|
||||
Name = "Admin Statistics",
|
||||
Description = "View statistical information about the moonlight instance"
|
||||
};
|
||||
|
||||
public static Permission AdminSysPlugins = new()
|
||||
{
|
||||
Index = 2,
|
||||
Name = "Admin system plugins",
|
||||
Description = "View and install plugins"
|
||||
};
|
||||
|
||||
public static Permission AdminDomains = new()
|
||||
{
|
||||
Index = 4,
|
||||
Name = "Admin Domains",
|
||||
Description = "Manage domains in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminNewDomain = new()
|
||||
{
|
||||
Index = 5,
|
||||
Name = "Admin New Domain",
|
||||
Description = "Create a new domain in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSharedDomains = new()
|
||||
{
|
||||
Index = 6,
|
||||
Name = "Admin Shared Domains",
|
||||
Description = "Manage shared domains in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminNewSharedDomain = new()
|
||||
{
|
||||
Index = 7,
|
||||
Name = "Admin New Shared Domain",
|
||||
Description = "Create a new shared domain in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminNodeEdit = new()
|
||||
{
|
||||
Index = 9,
|
||||
Name = "Admin Node Edit",
|
||||
Description = "Edit node settings in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminNodes = new()
|
||||
{
|
||||
Index = 10,
|
||||
Name = "Admin Node",
|
||||
Description = "Access the node management page in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminNewNode = new()
|
||||
{
|
||||
Index = 11,
|
||||
Name = "Admin New Node",
|
||||
Description = "Create a new node in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminNodeSetup = new()
|
||||
{
|
||||
Index = 12,
|
||||
Name = "Admin Node Setup",
|
||||
Description = "Set up a node in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminNodeView = new()
|
||||
{
|
||||
Index = 13,
|
||||
Name = "Admin Node View",
|
||||
Description = "View node details in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminNotificationDebugging = new()
|
||||
{
|
||||
Index = 14,
|
||||
Name = "Admin Notification Debugging",
|
||||
Description = "Manage debugging notifications in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminServerCleanup = new()
|
||||
{
|
||||
Index = 15,
|
||||
Name = "Admin Server Cleanup",
|
||||
Description = "Perform server cleanup tasks in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminServerEdit = new()
|
||||
{
|
||||
Index = 16,
|
||||
Name = "Admin Server Edit",
|
||||
Description = "Edit server settings in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminServers = new()
|
||||
{
|
||||
Index = 17,
|
||||
Name = "Admin Server",
|
||||
Description = "Access the server management page in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminServerManager = new()
|
||||
{
|
||||
Index = 18,
|
||||
Name = "Admin Server Manager",
|
||||
Description = "Manage servers in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminNewServer = new()
|
||||
{
|
||||
Index = 19,
|
||||
Name = "Admin New Server",
|
||||
Description = "Create a new server in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminServerImageEdit = new()
|
||||
{
|
||||
Index = 20,
|
||||
Name = "Admin Server Image Edit",
|
||||
Description = "Edit server image settings in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminServerImages = new()
|
||||
{
|
||||
Index = 21,
|
||||
Name = "Admin Server Images",
|
||||
Description = "Access the server image management page in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminServerImageNew = new()
|
||||
{
|
||||
Index = 22,
|
||||
Name = "Admin Server Image New",
|
||||
Description = "Create a new server image in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminServerViewAllocations = new()
|
||||
{
|
||||
Index = 23,
|
||||
Name = "Admin Server View Allocations",
|
||||
Description = "View server allocations in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminServerViewArchive = new()
|
||||
{
|
||||
Index = 24,
|
||||
Name = "Admin Server View Archive",
|
||||
Description = "View server archive in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminServerViewDebug = new()
|
||||
{
|
||||
Index = 25,
|
||||
Name = "Admin Server View Debug",
|
||||
Description = "View server debugging information in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminServerViewImage = new()
|
||||
{
|
||||
Index = 26,
|
||||
Name = "Admin Server View Image",
|
||||
Description = "View server image details in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminServerViewIndex = new()
|
||||
{
|
||||
Index = 27,
|
||||
Name = "Admin Server View",
|
||||
Description = "Access the server view page in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminServerViewOverview = new()
|
||||
{
|
||||
Index = 28,
|
||||
Name = "Admin Server View Overview",
|
||||
Description = "View server overview in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminServerViewResources = new()
|
||||
{
|
||||
Index = 29,
|
||||
Name = "Admin Server View Resources",
|
||||
Description = "View server resources in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSubscriptionEdit = new()
|
||||
{
|
||||
Index = 30,
|
||||
Name = "Admin Subscription Edit",
|
||||
Description = "Edit subscription settings in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSubscriptions = new()
|
||||
{
|
||||
Index = 31,
|
||||
Name = "Admin Subscriptions",
|
||||
Description = "Access the subscription management page in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminNewSubscription = new()
|
||||
{
|
||||
Index = 32,
|
||||
Name = "Admin New Subscription",
|
||||
Description = "Create a new subscription in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSupport = new()
|
||||
{
|
||||
Index = 33,
|
||||
Name = "Admin Support",
|
||||
Description = "Access the support page in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSupportView = new()
|
||||
{
|
||||
Index = 34,
|
||||
Name = "Admin Support View",
|
||||
Description = "View support details in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSysConfiguration = new()
|
||||
{
|
||||
Index = 35,
|
||||
Name = "Admin system Configuration",
|
||||
Description = "Access system configuration settings in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSysDiscordBot = new()
|
||||
{
|
||||
Index = 36,
|
||||
Name = "Admin system Discord Bot",
|
||||
Description = "Manage Discord bot settings in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSystem = new()
|
||||
{
|
||||
Index = 37,
|
||||
Name = "Admin system",
|
||||
Description = "Access the system management page in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSysMail = new()
|
||||
{
|
||||
Index = 38,
|
||||
Name = "Admin system Mail",
|
||||
Description = "Manage mail settings in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSecurityMalware = new()
|
||||
{
|
||||
Index = 39,
|
||||
Name = "Admin security Malware",
|
||||
Description = "Manage malware settings in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSysResources = new()
|
||||
{
|
||||
Index = 40,
|
||||
Name = "Admin system Resources",
|
||||
Description = "View system resources in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSecurity = new()
|
||||
{
|
||||
Index = 41,
|
||||
Name = "Admin Security",
|
||||
Description = "View security logs in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSysSentry = new()
|
||||
{
|
||||
Index = 42,
|
||||
Name = "Admin system Sentry",
|
||||
Description = "Manage Sentry settings in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSysNewsEdit = new()
|
||||
{
|
||||
Index = 43,
|
||||
Name = "Admin system News Edit",
|
||||
Description = "Edit system news in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSysNews = new()
|
||||
{
|
||||
Index = 44,
|
||||
Name = "Admin system News",
|
||||
Description = "Access the system news management page in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSysNewsNew = new()
|
||||
{
|
||||
Index = 45,
|
||||
Name = "Admin system News New",
|
||||
Description = "Create new system news in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminUserEdit = new()
|
||||
{
|
||||
Index = 46,
|
||||
Name = "Admin User Edit",
|
||||
Description = "Edit user settings in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminUsers = new()
|
||||
{
|
||||
Index = 47,
|
||||
Name = "Admin Users",
|
||||
Description = "Access the user management page in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminNewUser = new()
|
||||
{
|
||||
Index = 48,
|
||||
Name = "Admin New User",
|
||||
Description = "Create a new user in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminUserSessions = new()
|
||||
{
|
||||
Index = 49,
|
||||
Name = "Admin User Sessions",
|
||||
Description = "View user sessions in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminUserView = new()
|
||||
{
|
||||
Index = 50,
|
||||
Name = "Admin User View",
|
||||
Description = "View user details in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminWebspaces = new()
|
||||
{
|
||||
Index = 51,
|
||||
Name = "Admin Webspaces",
|
||||
Description = "Access the webspaces management page in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminNewWebspace = new()
|
||||
{
|
||||
Index = 52,
|
||||
Name = "Admin New Webspace",
|
||||
Description = "Create a new webspace in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminWebspacesServerEdit = new()
|
||||
{
|
||||
Index = 53,
|
||||
Name = "Admin Webspaces Server Edit",
|
||||
Description = "Edit webspace server settings in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminWebspacesServers = new()
|
||||
{
|
||||
Index = 54,
|
||||
Name = "Admin Webspaces Servers",
|
||||
Description = "Access the webspace server management page in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminWebspacesServerNew = new()
|
||||
{
|
||||
Index = 55,
|
||||
Name = "Admin Webspaces Server New",
|
||||
Description = "Create a new webspace server in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSecurityIpBans = new()
|
||||
{
|
||||
Index = 56,
|
||||
Name = "Admin security ip bans",
|
||||
Description = "Manage ip bans in the admin area"
|
||||
};
|
||||
|
||||
public static Permission AdminSecurityPermissionGroups = new()
|
||||
{
|
||||
Index = 57,
|
||||
Name = "Admin security permission groups",
|
||||
Description = "View, add and delete permission groups"
|
||||
};
|
||||
|
||||
public static Permission AdminSecurityLogs = new()
|
||||
{
|
||||
Index = 58,
|
||||
Name = "Admin security logs",
|
||||
Description = "View the security logs"
|
||||
};
|
||||
|
||||
public static Permission AdminSecurityDdos = new()
|
||||
{
|
||||
Index = 59,
|
||||
Name = "Admin security ddos",
|
||||
Description = "Manage the integrated ddos protection"
|
||||
};
|
||||
|
||||
public static Permission? FromString(string name)
|
||||
{
|
||||
var type = typeof(Permissions);
|
||||
|
||||
var field = type
|
||||
.GetFields()
|
||||
.FirstOrDefault(x => x.FieldType == typeof(Permission) && x.Name == name);
|
||||
|
||||
if (field != null)
|
||||
{
|
||||
var value = field.GetValue(null);
|
||||
return value as Permission;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Permission[] GetAllPermissions()
|
||||
{
|
||||
var type = typeof(Permissions);
|
||||
|
||||
return type
|
||||
.GetFields()
|
||||
.Where(x => x.FieldType == typeof(Permission))
|
||||
.Select(x => (x.GetValue(null) as Permission)!)
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
15
Moonlight/App/Plugin/MoonlightPlugin.cs
Normal file
15
Moonlight/App/Plugin/MoonlightPlugin.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Moonlight.App.Plugin.UI.Servers;
|
||||
using Moonlight.App.Plugin.UI.Webspaces;
|
||||
|
||||
namespace Moonlight.App.Plugin;
|
||||
|
||||
public abstract class MoonlightPlugin
|
||||
{
|
||||
public string Name { get; set; } = "N/A";
|
||||
public string Author { get; set; } = "N/A";
|
||||
public string Version { get; set; } = "N/A";
|
||||
|
||||
public Func<ServerPageContext, Task>? OnBuildServerPage { get; set; }
|
||||
public Func<WebspacePageContext, Task>? OnBuildWebspacePage { get; set; }
|
||||
public Func<IServiceCollection, Task>? OnBuildServices { get; set; }
|
||||
}
|
||||
12
Moonlight/App/Plugin/UI/Servers/ServerPageContext.cs
Normal file
12
Moonlight/App/Plugin/UI/Servers/ServerPageContext.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Moonlight.App.Database.Entities;
|
||||
|
||||
namespace Moonlight.App.Plugin.UI.Servers;
|
||||
|
||||
public class ServerPageContext
|
||||
{
|
||||
public List<ServerTab> Tabs { get; set; } = new();
|
||||
public List<ServerSetting> Settings { get; set; } = new();
|
||||
public Server Server { get; set; }
|
||||
public User User { get; set; }
|
||||
public string[] ImageTags { get; set; }
|
||||
}
|
||||
9
Moonlight/App/Plugin/UI/Servers/ServerSetting.cs
Normal file
9
Moonlight/App/Plugin/UI/Servers/ServerSetting.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Moonlight.App.Plugin.UI.Servers;
|
||||
|
||||
public class ServerSetting
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public RenderFragment Component { get; set; }
|
||||
}
|
||||
11
Moonlight/App/Plugin/UI/Servers/ServerTab.cs
Normal file
11
Moonlight/App/Plugin/UI/Servers/ServerTab.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Moonlight.App.Plugin.UI.Servers;
|
||||
|
||||
public class ServerTab
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Route { get; set; }
|
||||
public string Icon { get; set; }
|
||||
public RenderFragment Component { get; set; }
|
||||
}
|
||||
10
Moonlight/App/Plugin/UI/Webspaces/WebspacePageContext.cs
Normal file
10
Moonlight/App/Plugin/UI/Webspaces/WebspacePageContext.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Moonlight.App.Database.Entities;
|
||||
|
||||
namespace Moonlight.App.Plugin.UI.Webspaces;
|
||||
|
||||
public class WebspacePageContext
|
||||
{
|
||||
public List<WebspaceTab> Tabs { get; set; } = new();
|
||||
public User User { get; set; }
|
||||
public WebSpace WebSpace { get; set; }
|
||||
}
|
||||
10
Moonlight/App/Plugin/UI/Webspaces/WebspaceTab.cs
Normal file
10
Moonlight/App/Plugin/UI/Webspaces/WebspaceTab.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Moonlight.App.Plugin.UI.Webspaces;
|
||||
|
||||
public class WebspaceTab
|
||||
{
|
||||
public string Name { get; set; } = "N/A";
|
||||
public string Route { get; set; } = "/";
|
||||
public RenderFragment Component { get; set; }
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Moonlight.App.Database;
|
||||
using Moonlight.App.Database.Entities.LogsEntries;
|
||||
|
||||
namespace Moonlight.App.Repositories.LogEntries;
|
||||
|
||||
public class AuditLogEntryRepository : IDisposable
|
||||
{
|
||||
private readonly DataContext DataContext;
|
||||
|
||||
public AuditLogEntryRepository(DataContext dataContext)
|
||||
{
|
||||
DataContext = dataContext;
|
||||
}
|
||||
|
||||
public AuditLogEntry Add(AuditLogEntry entry)
|
||||
{
|
||||
var x = DataContext.AuditLog.Add(entry);
|
||||
DataContext.SaveChanges();
|
||||
return x.Entity;
|
||||
}
|
||||
|
||||
public DbSet<AuditLogEntry> Get()
|
||||
{
|
||||
return DataContext.AuditLog;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DataContext.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Moonlight.App.Database;
|
||||
using Moonlight.App.Database.Entities.LogsEntries;
|
||||
|
||||
namespace Moonlight.App.Repositories.LogEntries;
|
||||
|
||||
public class ErrorLogEntryRepository : IDisposable
|
||||
{
|
||||
private readonly DataContext DataContext;
|
||||
|
||||
public ErrorLogEntryRepository(DataContext dataContext)
|
||||
{
|
||||
DataContext = dataContext;
|
||||
}
|
||||
|
||||
public ErrorLogEntry Add(ErrorLogEntry errorLogEntry)
|
||||
{
|
||||
var x = DataContext.ErrorLog.Add(errorLogEntry);
|
||||
DataContext.SaveChanges();
|
||||
return x.Entity;
|
||||
}
|
||||
|
||||
public DbSet<ErrorLogEntry> Get()
|
||||
{
|
||||
return DataContext.ErrorLog;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DataContext.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Moonlight.App.Database;
|
||||
using Moonlight.App.Database.Entities.LogsEntries;
|
||||
|
||||
namespace Moonlight.App.Repositories.LogEntries;
|
||||
|
||||
public class SecurityLogEntryRepository : IDisposable
|
||||
{
|
||||
private readonly DataContext DataContext;
|
||||
|
||||
public SecurityLogEntryRepository(DataContext dataContext)
|
||||
{
|
||||
DataContext = dataContext;
|
||||
}
|
||||
|
||||
public SecurityLogEntry Add(SecurityLogEntry securityLogEntry)
|
||||
{
|
||||
var x = DataContext.SecurityLog.Add(securityLogEntry);
|
||||
DataContext.SaveChanges();
|
||||
return x.Entity;
|
||||
}
|
||||
|
||||
public DbSet<SecurityLogEntry> Get()
|
||||
{
|
||||
return DataContext.SecurityLog;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DataContext.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
using Moonlight.App.Models.Misc;
|
||||
|
||||
namespace Moonlight.App.Repositories;
|
||||
|
||||
public class SessionRepository
|
||||
{
|
||||
private readonly List<Session> Sessions;
|
||||
|
||||
public SessionRepository()
|
||||
{
|
||||
Sessions = new();
|
||||
}
|
||||
|
||||
public Session[] Get()
|
||||
{
|
||||
lock (Sessions)
|
||||
{
|
||||
return Sessions.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(Session session)
|
||||
{
|
||||
lock (Sessions)
|
||||
{
|
||||
Sessions.Add(session);
|
||||
}
|
||||
}
|
||||
|
||||
public void Delete(Session session)
|
||||
{
|
||||
lock (Sessions)
|
||||
{
|
||||
Sessions.Remove(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,15 +43,15 @@ public class CleanupService
|
||||
CompletedAt = DateTimeService.GetCurrent();
|
||||
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");
|
||||
return;
|
||||
}
|
||||
|
||||
Timer = new(TimeSpan.FromMinutes(config.GetValue<int>("Wait")));
|
||||
Timer = new(TimeSpan.FromMinutes(config.Wait));
|
||||
|
||||
Task.Run(Run);
|
||||
}
|
||||
@@ -63,12 +63,12 @@ public class CleanupService
|
||||
IsRunning = true;
|
||||
|
||||
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 minMemory = config.GetValue<int>("Memory");
|
||||
var maxUptime = config.GetValue<int>("Uptime");
|
||||
var minUptime = config.GetValue<int>("MinUptime");
|
||||
var maxCpu = config.Cpu;
|
||||
var minMemory = config.Memory;
|
||||
var maxUptime = config.Uptime;
|
||||
var minUptime = config.MinUptime;
|
||||
|
||||
var nodeRepository = scope.ServiceProvider.GetRequiredService<NodeRepository>();
|
||||
var nodeService = scope.ServiceProvider.GetRequiredService<NodeService>();
|
||||
|
||||
129
Moonlight/App/Services/Background/DdosProtectionService.cs
Normal file
129
Moonlight/App/Services/Background/DdosProtectionService.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using Moonlight.App.ApiClients.Daemon;
|
||||
using Moonlight.App.Database.Entities;
|
||||
using Moonlight.App.Events;
|
||||
using Moonlight.App.Helpers;
|
||||
using Moonlight.App.Repositories;
|
||||
|
||||
namespace Moonlight.App.Services.Background;
|
||||
|
||||
public class DdosProtectionService
|
||||
{
|
||||
private readonly IServiceScopeFactory ServiceScopeFactory;
|
||||
|
||||
public DdosProtectionService(IServiceScopeFactory serviceScopeFactory)
|
||||
{
|
||||
ServiceScopeFactory = serviceScopeFactory;
|
||||
|
||||
Task.Run(UnBlocker);
|
||||
}
|
||||
|
||||
private async Task UnBlocker()
|
||||
{
|
||||
var periodicTimer = new PeriodicTimer(TimeSpan.FromMinutes(5));
|
||||
|
||||
while (true)
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var blocklistIpRepo = scope.ServiceProvider.GetRequiredService<Repository<BlocklistIp>>();
|
||||
|
||||
var ips = blocklistIpRepo
|
||||
.Get()
|
||||
.ToArray();
|
||||
|
||||
foreach (var ip in ips)
|
||||
{
|
||||
if (DateTime.UtcNow > ip.ExpiresAt)
|
||||
{
|
||||
blocklistIpRepo.Delete(ip);
|
||||
}
|
||||
}
|
||||
|
||||
var newCount = blocklistIpRepo
|
||||
.Get()
|
||||
.Count();
|
||||
|
||||
if (newCount != ips.Length)
|
||||
{
|
||||
await RebuildNodeFirewalls();
|
||||
}
|
||||
|
||||
await periodicTimer.WaitForNextTickAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RebuildNodeFirewalls()
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var blocklistIpRepo = scope.ServiceProvider.GetRequiredService<Repository<BlocklistIp>>();
|
||||
var nodeRepo = scope.ServiceProvider.GetRequiredService<Repository<Node>>();
|
||||
var nodeService = scope.ServiceProvider.GetRequiredService<NodeService>();
|
||||
|
||||
var ips = blocklistIpRepo
|
||||
.Get()
|
||||
.Select(x => x.Ip)
|
||||
.ToArray();
|
||||
|
||||
foreach (var node in nodeRepo.Get().ToArray())
|
||||
{
|
||||
try
|
||||
{
|
||||
await nodeService.RebuildFirewall(node, ips);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warn($"Error rebuilding firewall on node {node.Name}");
|
||||
Logger.Warn(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ProcessDdosSignal(string ip, long packets)
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
|
||||
var blocklistRepo = scope.ServiceProvider.GetRequiredService<Repository<BlocklistIp>>();
|
||||
var whitelistRepo = scope.ServiceProvider.GetRequiredService<Repository<WhitelistIp>>();
|
||||
|
||||
var whitelistIps = whitelistRepo.Get().ToArray();
|
||||
|
||||
if(whitelistIps.Any(x => x.Ip == ip))
|
||||
return;
|
||||
|
||||
var blocklistIps = blocklistRepo.Get().ToArray();
|
||||
|
||||
if(blocklistIps.Any(x => x.Ip == ip))
|
||||
return;
|
||||
|
||||
await BlocklistIp(ip, packets);
|
||||
}
|
||||
|
||||
public async Task BlocklistIp(string ip, long packets)
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var blocklistRepo = scope.ServiceProvider.GetRequiredService<Repository<BlocklistIp>>();
|
||||
var configService = scope.ServiceProvider.GetRequiredService<ConfigService>();
|
||||
var eventSystem = scope.ServiceProvider.GetRequiredService<EventSystem>();
|
||||
|
||||
var blocklistIp = blocklistRepo.Add(new()
|
||||
{
|
||||
Ip = ip,
|
||||
Packets = packets,
|
||||
ExpiresAt = DateTime.UtcNow.AddMinutes(configService.Get().Moonlight.Security.BlockIpDuration),
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
|
||||
await RebuildNodeFirewalls();
|
||||
await eventSystem.Emit("ddos.add", blocklistIp);
|
||||
}
|
||||
|
||||
public async Task UnBlocklistIp(string ip)
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var blocklistRepo = scope.ServiceProvider.GetRequiredService<Repository<BlocklistIp>>();
|
||||
|
||||
var blocklist = blocklistRepo.Get().First(x => x.Ip == ip);
|
||||
blocklistRepo.Delete(blocklist);
|
||||
|
||||
await RebuildNodeFirewalls();
|
||||
}
|
||||
}
|
||||
@@ -22,19 +22,21 @@ public class DiscordNotificationService
|
||||
Event = eventSystem;
|
||||
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");
|
||||
|
||||
Client = new(config.GetValue<string>("WebHook"));
|
||||
AppUrl = configService.GetSection("Moonlight").GetValue<string>("AppUrl");
|
||||
Client = new(config.WebHook);
|
||||
AppUrl = configService.Get().Moonlight.AppUrl;
|
||||
|
||||
Event.On<User>("supportChat.new", this, OnNewSupportChat);
|
||||
Event.On<SupportChatMessage>("supportChat.message", this, OnSupportChatMessage);
|
||||
Event.On<User>("supportChat.close", this, OnSupportChatClose);
|
||||
Event.On<User>("user.rating", this, OnUserRated);
|
||||
Event.On<User>("billing.completed", this, OnBillingCompleted);
|
||||
Event.On<BlocklistIp>("ddos.add", this, OnIpBlockListed);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -42,6 +44,33 @@ public class DiscordNotificationService
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OnIpBlockListed(BlocklistIp blocklistIp)
|
||||
{
|
||||
await SendNotification("", builder =>
|
||||
{
|
||||
builder.Color = Color.Red;
|
||||
builder.Title = "New ddos attack detected";
|
||||
|
||||
builder.AddField("IP", blocklistIp.Ip);
|
||||
builder.AddField("Packets", blocklistIp.Packets);
|
||||
});
|
||||
}
|
||||
|
||||
private async Task OnBillingCompleted(User user)
|
||||
{
|
||||
await SendNotification("", builder =>
|
||||
{
|
||||
builder.Color = Color.Red;
|
||||
builder.Title = "New payment received";
|
||||
|
||||
builder.AddField("User", user.Email);
|
||||
builder.AddField("Firstname", user.FirstName);
|
||||
builder.AddField("Lastname", user.LastName);
|
||||
builder.AddField("Amount", user.CurrentSubscription!.Price);
|
||||
builder.AddField("Currency", user.CurrentSubscription!.Currency);
|
||||
});
|
||||
}
|
||||
|
||||
private async Task OnUserRated(User user)
|
||||
{
|
||||
await SendNotification("", builder =>
|
||||
|
||||
@@ -19,6 +19,7 @@ public class MalwareScanService
|
||||
private readonly IServiceScopeFactory ServiceScopeFactory;
|
||||
|
||||
public bool IsRunning { get; private set; }
|
||||
public bool ScanAllServers { get; set; }
|
||||
public readonly Dictionary<Server, MalwareScanResult[]> ScanResults;
|
||||
public string Status { get; private set; } = "N/A";
|
||||
|
||||
@@ -26,7 +27,6 @@ public class MalwareScanService
|
||||
{
|
||||
ServiceScopeFactory = serviceScopeFactory;
|
||||
Event = eventSystem;
|
||||
|
||||
ScanResults = new();
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ public class MalwareScanService
|
||||
|
||||
private async Task Run()
|
||||
{
|
||||
// Clean results
|
||||
IsRunning = true;
|
||||
Status = "Clearing last results";
|
||||
await Event.Emit("malwareScan.status", IsRunning);
|
||||
@@ -53,6 +54,55 @@ public class MalwareScanService
|
||||
|
||||
await Event.Emit("malwareScan.result");
|
||||
|
||||
// Load servers to scan
|
||||
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
|
||||
// Load services from di scope
|
||||
NodeRepository = scope.ServiceProvider.GetRequiredService<Repository<Node>>();
|
||||
ServerRepository = scope.ServiceProvider.GetRequiredService<Repository<Server>>();
|
||||
NodeService = scope.ServiceProvider.GetRequiredService<NodeService>();
|
||||
ServerService = scope.ServiceProvider.GetRequiredService<ServerService>();
|
||||
|
||||
Status = "Fetching servers to scan";
|
||||
await Event.Emit("malwareScan.status", IsRunning);
|
||||
|
||||
Server[] servers;
|
||||
|
||||
if (ScanAllServers)
|
||||
servers = ServerRepository.Get().ToArray();
|
||||
else
|
||||
servers = await GetOnlineServers();
|
||||
|
||||
// Perform scan
|
||||
|
||||
int i = 1;
|
||||
foreach (var server in servers)
|
||||
{
|
||||
Status = $"[{i} / {servers.Length}] Scanning server {server.Name}";
|
||||
await Event.Emit("malwareScan.status", IsRunning);
|
||||
|
||||
var results = await PerformScanOnServer(server);
|
||||
|
||||
if (results.Any())
|
||||
{
|
||||
lock (ScanResults)
|
||||
{
|
||||
ScanResults.Add(server, results);
|
||||
}
|
||||
|
||||
await Event.Emit("malwareScan.result");
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
IsRunning = false;
|
||||
await Event.Emit("malwareScan.status", IsRunning);
|
||||
}
|
||||
|
||||
private async Task<Server[]> GetOnlineServers()
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
|
||||
// Load services from di scope
|
||||
@@ -103,42 +153,10 @@ public class MalwareScanService
|
||||
}
|
||||
}
|
||||
|
||||
// Perform scan
|
||||
var resultsMapped = new Dictionary<Server, MalwareScanResult[]>();
|
||||
foreach (var mapping in containerServerMapped)
|
||||
{
|
||||
Logger.Verbose($"Scanning server {mapping.Key.Name} for malware");
|
||||
|
||||
Status = $"Scanning server {mapping.Key.Name} for malware";
|
||||
await Event.Emit("malwareScan.status", IsRunning);
|
||||
|
||||
var results = await PerformScanOnServer(mapping.Key, mapping.Value);
|
||||
|
||||
if (results.Any())
|
||||
{
|
||||
resultsMapped.Add(mapping.Key, results);
|
||||
Logger.Verbose($"{results.Length} findings on server {mapping.Key.Name}");
|
||||
}
|
||||
return containerServerMapped.Keys.ToArray();
|
||||
}
|
||||
|
||||
Logger.Verbose($"Scan complete. Detected {resultsMapped.Count} servers with findings");
|
||||
|
||||
IsRunning = false;
|
||||
Status = $"Scan complete. Detected {resultsMapped.Count} servers with findings";
|
||||
await Event.Emit("malwareScan.status", IsRunning);
|
||||
|
||||
lock (ScanResults)
|
||||
{
|
||||
foreach (var mapping in resultsMapped)
|
||||
{
|
||||
ScanResults.Add(mapping.Key, mapping.Value);
|
||||
}
|
||||
}
|
||||
|
||||
await Event.Emit("malwareScan.result");
|
||||
}
|
||||
|
||||
private async Task<MalwareScanResult[]> PerformScanOnServer(Server server, Container container)
|
||||
private async Task<MalwareScanResult[]> PerformScanOnServer(Server server)
|
||||
{
|
||||
var results = new List<MalwareScanResult>();
|
||||
|
||||
@@ -162,6 +180,29 @@ public class MalwareScanService
|
||||
}
|
||||
}
|
||||
|
||||
async Task ScanMinerJar()
|
||||
{
|
||||
var access = await ServerService.CreateFileAccess(server, null!);
|
||||
var fileElements = await access.Ls();
|
||||
|
||||
if (fileElements.Any(x => x.Name == "libraries" && !x.IsFile))
|
||||
{
|
||||
await access.Cd("libraries");
|
||||
|
||||
fileElements = await access.Ls();
|
||||
|
||||
if (fileElements.Any(x => x.Name == "jdk" && !x.IsFile))
|
||||
{
|
||||
results.Add(new ()
|
||||
{
|
||||
Title = "Found Miner",
|
||||
Description = "Detected suspicious library directory which may contain a script for miners",
|
||||
Author = "Marcel Baumgartner"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async Task ScanFakePlayerPlugins()
|
||||
{
|
||||
var access = await ServerService.CreateFileAccess(server, null!);
|
||||
@@ -190,6 +231,7 @@ public class MalwareScanService
|
||||
// Execute scans
|
||||
await ScanSelfBot();
|
||||
await ScanFakePlayerPlugins();
|
||||
await ScanMinerJar();
|
||||
|
||||
return results.ToArray();
|
||||
}
|
||||
|
||||
62
Moonlight/App/Services/Background/TelemetryService.cs
Normal file
62
Moonlight/App/Services/Background/TelemetryService.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using Moonlight.App.ApiClients.Telemetry;
|
||||
using Moonlight.App.ApiClients.Telemetry.Requests;
|
||||
using Moonlight.App.Database.Entities;
|
||||
using Moonlight.App.Helpers;
|
||||
using Moonlight.App.Repositories;
|
||||
|
||||
namespace Moonlight.App.Services.Background;
|
||||
|
||||
public class TelemetryService
|
||||
{
|
||||
private readonly IServiceScopeFactory ServiceScopeFactory;
|
||||
private readonly ConfigService ConfigService;
|
||||
|
||||
public TelemetryService(
|
||||
ConfigService configService,
|
||||
IServiceScopeFactory serviceScopeFactory)
|
||||
{
|
||||
ServiceScopeFactory = serviceScopeFactory;
|
||||
ConfigService = configService;
|
||||
|
||||
if(!ConfigService.DebugMode)
|
||||
Task.Run(Run);
|
||||
}
|
||||
|
||||
private async Task Run()
|
||||
{
|
||||
var timer = new PeriodicTimer(TimeSpan.FromMinutes(15));
|
||||
|
||||
while (true)
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
|
||||
var serversRepo = scope.ServiceProvider.GetRequiredService<Repository<Server>>();
|
||||
var nodesRepo = scope.ServiceProvider.GetRequiredService<Repository<Node>>();
|
||||
var usersRepo = scope.ServiceProvider.GetRequiredService<Repository<User>>();
|
||||
var webspacesRepo = scope.ServiceProvider.GetRequiredService<Repository<WebSpace>>();
|
||||
var databaseRepo = scope.ServiceProvider.GetRequiredService<Repository<MySqlDatabase>>();
|
||||
|
||||
var apiHelper = scope.ServiceProvider.GetRequiredService<TelemetryApiHelper>();
|
||||
|
||||
try
|
||||
{
|
||||
await apiHelper.Post("telemetry", new TelemetryData()
|
||||
{
|
||||
Servers = serversRepo.Get().Count(),
|
||||
Databases = databaseRepo.Get().Count(),
|
||||
Nodes = nodesRepo.Get().Count(),
|
||||
Users = usersRepo.Get().Count(),
|
||||
Webspaces = webspacesRepo.Get().Count(),
|
||||
AppUrl = ConfigService.Get().Moonlight.AppUrl
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warn("Error sending telemetry");
|
||||
Logger.Warn(e);
|
||||
}
|
||||
|
||||
await timer.WaitForNextTickAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
37
Moonlight/App/Services/Background/TempMailService.cs
Normal file
37
Moonlight/App/Services/Background/TempMailService.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System.Net.Mail;
|
||||
using Moonlight.App.Helpers;
|
||||
|
||||
namespace Moonlight.App.Services.Background;
|
||||
|
||||
public class TempMailService
|
||||
{
|
||||
private string[] Domains = Array.Empty<string>();
|
||||
|
||||
public TempMailService()
|
||||
{
|
||||
Task.Run(Init);
|
||||
}
|
||||
|
||||
private async Task Init()
|
||||
{
|
||||
var client = new HttpClient();
|
||||
var text = await client.GetStringAsync("https://raw.githubusercontent.com/disposable-email-domains/disposable-email-domains/master/disposable_email_blocklist.conf");
|
||||
|
||||
Domains = text
|
||||
.Split("\n")
|
||||
.Select(x => x.Trim())
|
||||
.ToArray();
|
||||
|
||||
Logger.Info($"Fetched {Domains.Length} temp mail domains");
|
||||
}
|
||||
|
||||
public Task<bool> IsTempMail(string mail)
|
||||
{
|
||||
var address = new MailAddress(mail);
|
||||
|
||||
if (Domains.Contains(address.Host))
|
||||
return Task.FromResult(true);
|
||||
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user