Compare commits
127 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
efed2a6a5c | ||
|
|
6f138c2c51 | ||
|
|
6c43e6a533 | ||
|
|
0379afd831 | ||
|
|
b8bfdb7729 | ||
|
|
72f60ec97c | ||
|
|
1b40250750 | ||
|
|
cdf2988cb6 | ||
|
|
76415b4a0a | ||
|
|
52d00baf2b | ||
|
|
cd41db510e | ||
|
|
1afd4e8b92 | ||
|
|
ef37088c7a | ||
|
|
e71495533b | ||
|
|
e2a6d70f6a | ||
|
|
e95853b09c | ||
|
|
0537ca115e | ||
|
|
432e441972 | ||
|
|
1dae5150bd | ||
|
|
72c6f636ee | ||
|
|
e54d04277d | ||
|
|
f559f08e8d | ||
|
|
2674fb3fa7 | ||
|
|
d5d77ae7da | ||
|
|
3ae905038c | ||
|
|
6707d722e0 | ||
|
|
bc9fecf6d9 | ||
| 0fde9a5005 | |||
|
|
74541d7f87 |
8
.gitattributes
vendored
8
.gitattributes
vendored
@@ -1,2 +1,10 @@
|
|||||||
# Auto detect text files and perform LF normalization
|
# Auto detect text files and perform LF normalization
|
||||||
* text=auto
|
* text=auto
|
||||||
|
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
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Logging.Net;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using RestSharp;
|
using RestSharp;
|
||||||
|
|
||||||
namespace Moonlight.App.ApiClients.Modrinth;
|
namespace Moonlight.App.ApiClients.Modrinth;
|
||||||
@@ -48,7 +47,7 @@ public class ModrinthApiHelper
|
|||||||
|
|
||||||
var request = new RestRequest(url)
|
var request = new RestRequest(url)
|
||||||
{
|
{
|
||||||
Timeout = 60 * 15
|
Timeout = 300000
|
||||||
};
|
};
|
||||||
|
|
||||||
request.AddHeader("Content-Type", "application/json");
|
request.AddHeader("Content-Type", "application/json");
|
||||||
|
|||||||
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)
|
var request = new RestRequest(url)
|
||||||
{
|
{
|
||||||
Timeout = 60 * 15
|
Timeout = 300000
|
||||||
};
|
};
|
||||||
|
|
||||||
request.AddHeader("Content-Type", "application/json");
|
request.AddHeader("Content-Type", "application/json");
|
||||||
|
|||||||
334
Moonlight/App/Configuration/ConfigV1.cs
Normal file
334
Moonlight/App/Configuration/ConfigV1.cs
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
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("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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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("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 Microsoft.EntityFrameworkCore;
|
||||||
using Moonlight.App.Database.Entities;
|
using Moonlight.App.Database.Entities;
|
||||||
using Moonlight.App.Database.Entities.LogsEntries;
|
|
||||||
using Moonlight.App.Database.Entities.Notification;
|
using Moonlight.App.Database.Entities.Notification;
|
||||||
using Moonlight.App.Database.Interceptors;
|
using Moonlight.App.Database.Interceptors;
|
||||||
using Moonlight.App.Models.Misc;
|
|
||||||
using Moonlight.App.Services;
|
using Moonlight.App.Services;
|
||||||
|
|
||||||
namespace Moonlight.App.Database;
|
namespace Moonlight.App.Database;
|
||||||
@@ -27,10 +25,6 @@ public class DataContext : DbContext
|
|||||||
public DbSet<ServerVariable> ServerVariables { get; set; }
|
public DbSet<ServerVariable> ServerVariables { get; set; }
|
||||||
public DbSet<User> Users { get; set; }
|
public DbSet<User> Users { get; set; }
|
||||||
public DbSet<LoadingMessage> LoadingMessages { 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<SharedDomain> SharedDomains { get; set; }
|
||||||
public DbSet<Domain> Domains { get; set; }
|
public DbSet<Domain> Domains { get; set; }
|
||||||
public DbSet<Revoke> Revokes { get; set; }
|
public DbSet<Revoke> Revokes { get; set; }
|
||||||
@@ -46,20 +40,22 @@ public class DataContext : DbContext
|
|||||||
public DbSet<WebSpace> WebSpaces { get; set; }
|
public DbSet<WebSpace> WebSpaces { get; set; }
|
||||||
public DbSet<SupportChatMessage> SupportChatMessages { get; set; }
|
public DbSet<SupportChatMessage> SupportChatMessages { get; set; }
|
||||||
public DbSet<IpBan> IpBans { get; set; }
|
public DbSet<IpBan> IpBans { get; set; }
|
||||||
|
public DbSet<PermissionGroup> PermissionGroups { get; set; }
|
||||||
|
public DbSet<SecurityLog> SecurityLogs { get; set; }
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
{
|
{
|
||||||
if (!optionsBuilder.IsConfigured)
|
if (!optionsBuilder.IsConfigured)
|
||||||
{
|
{
|
||||||
var config = ConfigService
|
var config = ConfigService
|
||||||
.GetSection("Moonlight")
|
.Get()
|
||||||
.GetSection("Database");
|
.Moonlight.Database;
|
||||||
|
|
||||||
var connectionString = $"host={config.GetValue<string>("Host")};" +
|
var connectionString = $"host={config.Host};" +
|
||||||
$"port={config.GetValue<int>("Port")};" +
|
$"port={config.Port};" +
|
||||||
$"database={config.GetValue<string>("Database")};" +
|
$"database={config.Database};" +
|
||||||
$"uid={config.GetValue<string>("Username")};" +
|
$"uid={config.Username};" +
|
||||||
$"pwd={config.GetValue<string>("Password")}";
|
$"pwd={config.Password}";
|
||||||
|
|
||||||
optionsBuilder.UseMySql(
|
optionsBuilder.UseMySql(
|
||||||
connectionString,
|
connectionString,
|
||||||
|
|||||||
@@ -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 class Subscription
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public string Name { get; set; } = "";
|
public string Name { get; set; } = "";
|
||||||
public string Description { 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 string LimitsJson { get; set; } = "";
|
||||||
|
public int Duration { get; set; } = 30;
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Moonlight.App.Models.Misc;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Moonlight.App.Models.Misc;
|
||||||
|
|
||||||
namespace Moonlight.App.Database.Entities;
|
namespace Moonlight.App.Database.Entities;
|
||||||
|
|
||||||
@@ -24,6 +25,8 @@ public class User
|
|||||||
|
|
||||||
public string Country { get; set; } = "";
|
public string Country { get; set; } = "";
|
||||||
|
|
||||||
|
public string ServerListLayoutJson { get; set; } = "";
|
||||||
|
|
||||||
// States
|
// States
|
||||||
|
|
||||||
public UserStatus Status { get; set; } = UserStatus.Unverified;
|
public UserStatus Status { get; set; } = UserStatus.Unverified;
|
||||||
@@ -31,11 +34,14 @@ public class User
|
|||||||
public bool SupportPending { get; set; } = false;
|
public bool SupportPending { get; set; } = false;
|
||||||
public bool HasRated { get; set; } = false;
|
public bool HasRated { get; set; } = false;
|
||||||
public int Rating { get; set; } = 0;
|
public int Rating { get; set; } = 0;
|
||||||
|
public bool StreamerMode { get; set; } = false;
|
||||||
|
|
||||||
// Security
|
// Security
|
||||||
public bool TotpEnabled { get; set; } = false;
|
public bool TotpEnabled { get; set; } = false;
|
||||||
public string TotpSecret { get; set; } = "";
|
public string TotpSecret { get; set; } = "";
|
||||||
public DateTime TokenValidTime { get; set; } = DateTime.UtcNow;
|
public DateTime TokenValidTime { get; set; } = DateTime.UtcNow;
|
||||||
|
public byte[] Permissions { get; set; } = Array.Empty<byte>();
|
||||||
|
public PermissionGroup? PermissionGroup { get; set; }
|
||||||
|
|
||||||
// Discord
|
// Discord
|
||||||
public ulong DiscordId { get; set; }
|
public ulong DiscordId { get; set; }
|
||||||
@@ -48,6 +54,10 @@ public class User
|
|||||||
// Subscriptions
|
// Subscriptions
|
||||||
|
|
||||||
public Subscription? CurrentSubscription { get; set; } = null;
|
public Subscription? CurrentSubscription { get; set; } = null;
|
||||||
public DateTime SubscriptionSince { get; set; } = DateTime.Now;
|
public DateTime SubscriptionSince { get; set; } = DateTime.UtcNow;
|
||||||
public int SubscriptionDuration { get; set; }
|
public DateTime SubscriptionExpires { get; set; } = DateTime.UtcNow;
|
||||||
|
|
||||||
|
// Ip logs
|
||||||
|
public string RegisterIp { get; set; } = "";
|
||||||
|
public string LastIp { get; set; } = "";
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Data.Common;
|
using System.Data.Common;
|
||||||
using Logging.Net;
|
|
||||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||||
|
using Moonlight.App.Helpers;
|
||||||
|
|
||||||
namespace Moonlight.App.Database.Interceptors;
|
namespace Moonlight.App.Database.Interceptors;
|
||||||
|
|
||||||
|
|||||||
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -241,95 +241,6 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
b.ToTable("LoadingMessages");
|
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 =>
|
modelBuilder.Entity("Moonlight.App.Database.Entities.MySqlDatabase", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -475,6 +386,25 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
b.ToTable("NotificationClients");
|
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 =>
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Revoke", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -490,6 +420,24 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
b.ToTable("Revokes");
|
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 =>
|
modelBuilder.Entity("Moonlight.App.Database.Entities.Server", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@@ -663,10 +611,16 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("Currency")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<string>("Description")
|
b.Property<string>("Description")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int>("Duration")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<string>("LimitsJson")
|
b.Property<string>("LimitsJson")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
@@ -675,6 +629,17 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("longtext");
|
.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.HasKey("Id");
|
||||||
|
|
||||||
b.ToTable("Subscriptions");
|
b.ToTable("Subscriptions");
|
||||||
@@ -766,6 +731,10 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
b.Property<bool>("HasRated")
|
b.Property<bool>("HasRated")
|
||||||
.HasColumnType("tinyint(1)");
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<string>("LastIp")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
b.Property<string>("LastName")
|
b.Property<string>("LastName")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
@@ -777,9 +746,24 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int?>("PermissionGroupId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<byte[]>("Permissions")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longblob");
|
||||||
|
|
||||||
b.Property<int>("Rating")
|
b.Property<int>("Rating")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("RegisterIp")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<string>("ServerListLayoutJson")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
b.Property<string>("State")
|
b.Property<string>("State")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("longtext");
|
.HasColumnType("longtext");
|
||||||
@@ -787,8 +771,11 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
b.Property<int>("Status")
|
b.Property<int>("Status")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<int>("SubscriptionDuration")
|
b.Property<bool>("StreamerMode")
|
||||||
.HasColumnType("int");
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("SubscriptionExpires")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
b.Property<DateTime>("SubscriptionSince")
|
b.Property<DateTime>("SubscriptionSince")
|
||||||
.HasColumnType("datetime(6)");
|
.HasColumnType("datetime(6)");
|
||||||
@@ -813,6 +800,8 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
|
|
||||||
b.HasIndex("CurrentSubscriptionId");
|
b.HasIndex("CurrentSubscriptionId");
|
||||||
|
|
||||||
|
b.HasIndex("PermissionGroupId");
|
||||||
|
|
||||||
b.ToTable("Users");
|
b.ToTable("Users");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1017,7 +1006,13 @@ namespace Moonlight.App.Database.Migrations
|
|||||||
.WithMany()
|
.WithMany()
|
||||||
.HasForeignKey("CurrentSubscriptionId");
|
.HasForeignKey("CurrentSubscriptionId");
|
||||||
|
|
||||||
|
b.HasOne("Moonlight.App.Database.Entities.PermissionGroup", "PermissionGroup")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("PermissionGroupId");
|
||||||
|
|
||||||
b.Navigation("CurrentSubscription");
|
b.Navigation("CurrentSubscription");
|
||||||
|
|
||||||
|
b.Navigation("PermissionGroup");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b =>
|
modelBuilder.Entity("Moonlight.App.Database.Entities.WebSpace", b =>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Logging.Net;
|
using Moonlight.App.Helpers;
|
||||||
|
|
||||||
namespace Moonlight.App.Events;
|
namespace Moonlight.App.Events;
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ namespace Moonlight.App.Exceptions;
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
public class DisplayException : Exception
|
public class DisplayException : Exception
|
||||||
{
|
{
|
||||||
|
public bool DoNotTranslate { get; set; } = false;
|
||||||
|
|
||||||
//
|
//
|
||||||
// For guidelines regarding the creation of new exception types, see
|
// For guidelines regarding the creation of new exception types, see
|
||||||
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp
|
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp
|
||||||
@@ -20,6 +22,11 @@ public class DisplayException : Exception
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DisplayException(string message, bool doNotTranslate) : base(message)
|
||||||
|
{
|
||||||
|
DoNotTranslate = doNotTranslate;
|
||||||
|
}
|
||||||
|
|
||||||
public DisplayException(string message, Exception inner) : base(message, inner)
|
public DisplayException(string message, Exception inner) : base(message, inner)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,252 +0,0 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using Logging.Net;
|
|
||||||
using Logging.Net.Loggers.SB;
|
|
||||||
using Moonlight.App.Models.Misc;
|
|
||||||
using ILogger = Logging.Net.ILogger;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Helpers;
|
|
||||||
|
|
||||||
public class CacheLogger : ILogger
|
|
||||||
{
|
|
||||||
private SBLogger SbLogger = new();
|
|
||||||
private List<LogEntry> Messages = new();
|
|
||||||
|
|
||||||
public LogEntry[] GetMessages()
|
|
||||||
{
|
|
||||||
lock (Messages)
|
|
||||||
{
|
|
||||||
var result = new LogEntry[Messages.Count];
|
|
||||||
Messages.CopyTo(result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear(int messages)
|
|
||||||
{
|
|
||||||
lock (Messages)
|
|
||||||
{
|
|
||||||
Messages.RemoveRange(0, Math.Min(messages, Messages.Count));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Info(string? s)
|
|
||||||
{
|
|
||||||
if (s == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock (Messages)
|
|
||||||
{
|
|
||||||
Messages.Add(new()
|
|
||||||
{
|
|
||||||
Level = "info",
|
|
||||||
Message = s
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
SbLogger.Info(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Debug(string? s)
|
|
||||||
{
|
|
||||||
if (s == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock (Messages)
|
|
||||||
{
|
|
||||||
Messages.Add(new()
|
|
||||||
{
|
|
||||||
Level = "debug",
|
|
||||||
Message = s
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
SbLogger.Debug(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Warn(string? s)
|
|
||||||
{
|
|
||||||
if (s == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock (Messages)
|
|
||||||
{
|
|
||||||
Messages.Add(new()
|
|
||||||
{
|
|
||||||
Level = "warn",
|
|
||||||
Message = s
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
SbLogger.Warn(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Error(string? s)
|
|
||||||
{
|
|
||||||
if (s == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock (Messages)
|
|
||||||
{
|
|
||||||
Messages.Add(new()
|
|
||||||
{
|
|
||||||
Level = "error",
|
|
||||||
Message = s
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
SbLogger.Error(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Fatal(string? s)
|
|
||||||
{
|
|
||||||
if (s == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lock (Messages)
|
|
||||||
{
|
|
||||||
Messages.Add(new()
|
|
||||||
{
|
|
||||||
Level = "fatal",
|
|
||||||
Message = s
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
SbLogger.Fatal(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InfoEx(Exception ex)
|
|
||||||
{
|
|
||||||
lock (Messages)
|
|
||||||
{
|
|
||||||
Messages.Add(new()
|
|
||||||
{
|
|
||||||
Level = "info",
|
|
||||||
Message = ex.ToStringDemystified()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
SbLogger.InfoEx(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DebugEx(Exception ex)
|
|
||||||
{
|
|
||||||
lock (Messages)
|
|
||||||
{
|
|
||||||
Messages.Add(new()
|
|
||||||
{
|
|
||||||
Level = "debug",
|
|
||||||
Message = ex.ToStringDemystified()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
SbLogger.DebugEx(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WarnEx(Exception ex)
|
|
||||||
{
|
|
||||||
lock (Messages)
|
|
||||||
{
|
|
||||||
Messages.Add(new()
|
|
||||||
{
|
|
||||||
Level = "warn",
|
|
||||||
Message = ex.ToStringDemystified()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
SbLogger.WarnEx(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ErrorEx(Exception ex)
|
|
||||||
{
|
|
||||||
lock (Messages)
|
|
||||||
{
|
|
||||||
Messages.Add(new()
|
|
||||||
{
|
|
||||||
Level = "error",
|
|
||||||
Message = ex.ToStringDemystified()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
SbLogger.ErrorEx(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void FatalEx(Exception ex)
|
|
||||||
{
|
|
||||||
lock (Messages)
|
|
||||||
{
|
|
||||||
Messages.Add(new()
|
|
||||||
{
|
|
||||||
Level = "fatal",
|
|
||||||
Message = ex.ToStringDemystified()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
SbLogger.FatalEx(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public LoggingConfiguration GetErrorConfiguration()
|
|
||||||
{
|
|
||||||
return SbLogger.GetErrorConfiguration();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetErrorConfiguration(LoggingConfiguration configuration)
|
|
||||||
{
|
|
||||||
SbLogger.SetErrorConfiguration(configuration);
|
|
||||||
}
|
|
||||||
|
|
||||||
public LoggingConfiguration GetFatalConfiguration()
|
|
||||||
{
|
|
||||||
return SbLogger.GetFatalConfiguration();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetFatalConfiguration(LoggingConfiguration configuration)
|
|
||||||
{
|
|
||||||
SbLogger.SetFatalConfiguration(configuration);
|
|
||||||
}
|
|
||||||
|
|
||||||
public LoggingConfiguration GetWarnConfiguration()
|
|
||||||
{
|
|
||||||
return SbLogger.GetWarnConfiguration();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetWarnConfiguration(LoggingConfiguration configuration)
|
|
||||||
{
|
|
||||||
SbLogger.SetWarnConfiguration(configuration);
|
|
||||||
}
|
|
||||||
|
|
||||||
public LoggingConfiguration GetInfoConfiguration()
|
|
||||||
{
|
|
||||||
return SbLogger.GetInfoConfiguration();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetInfoConfiguration(LoggingConfiguration configuration)
|
|
||||||
{
|
|
||||||
SbLogger.SetInfoConfiguration(configuration);
|
|
||||||
}
|
|
||||||
|
|
||||||
public LoggingConfiguration GetDebugConfiguration()
|
|
||||||
{
|
|
||||||
return SbLogger.GetDebugConfiguration();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetDebugConfiguration(LoggingConfiguration configuration)
|
|
||||||
{
|
|
||||||
SbLogger.SetDebugConfiguration(configuration);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ILoggingAddition GetAddition()
|
|
||||||
{
|
|
||||||
return SbLogger.GetAddition();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetAddition(ILoggingAddition addition)
|
|
||||||
{
|
|
||||||
SbLogger.SetAddition(addition);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool LogCallingClass
|
|
||||||
{
|
|
||||||
get => SbLogger.LogCallingClass;
|
|
||||||
set => SbLogger.LogCallingClass = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Logging.Net;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Moonlight.App.Database;
|
using Moonlight.App.Database;
|
||||||
using Moonlight.App.Services;
|
using Moonlight.App.Services;
|
||||||
@@ -67,34 +66,45 @@ public class DatabaseCheckupService
|
|||||||
var configService = new ConfigService(new StorageService());
|
var configService = new ConfigService(new StorageService());
|
||||||
var dateTimeService = new DateTimeService();
|
var dateTimeService = new DateTimeService();
|
||||||
|
|
||||||
var config = configService
|
var config = configService.Get().Moonlight.Database;
|
||||||
.GetSection("Moonlight")
|
|
||||||
.GetSection("Database");
|
|
||||||
|
|
||||||
var connectionString = $"host={config.GetValue<string>("Host")};" +
|
var connectionString = $"host={config.Host};" +
|
||||||
$"port={config.GetValue<int>("Port")};" +
|
$"port={config.Port};" +
|
||||||
$"database={config.GetValue<string>("Database")};" +
|
$"database={config.Database};" +
|
||||||
$"uid={config.GetValue<string>("Username")};" +
|
$"uid={config.Username};" +
|
||||||
$"pwd={config.GetValue<string>("Password")}";
|
$"pwd={config.Password}";
|
||||||
|
|
||||||
string file = PathBuilder.File("storage", "backups", $"{dateTimeService.GetCurrentUnix()}-mysql.sql");
|
string file = PathBuilder.File("storage", "backups", $"{dateTimeService.GetCurrentUnix()}-mysql.sql");
|
||||||
|
|
||||||
Logger.Info($"Saving it to: {file}");
|
Logger.Info($"Saving it to: {file}");
|
||||||
Logger.Info("Starting backup...");
|
Logger.Info("Starting backup...");
|
||||||
|
|
||||||
var sw = new Stopwatch();
|
try
|
||||||
sw.Start();
|
{
|
||||||
|
var sw = new Stopwatch();
|
||||||
|
sw.Start();
|
||||||
|
|
||||||
await using MySqlConnection conn = new MySqlConnection(connectionString);
|
await using MySqlConnection conn = new MySqlConnection(connectionString);
|
||||||
await using MySqlCommand cmd = new MySqlCommand();
|
await using MySqlCommand cmd = new MySqlCommand();
|
||||||
using MySqlBackup mb = new MySqlBackup(cmd);
|
using MySqlBackup mb = new MySqlBackup(cmd);
|
||||||
|
|
||||||
cmd.Connection = conn;
|
cmd.Connection = conn;
|
||||||
await conn.OpenAsync();
|
await conn.OpenAsync();
|
||||||
mb.ExportToFile(file);
|
mb.ExportToFile(file);
|
||||||
await conn.CloseAsync();
|
await conn.CloseAsync();
|
||||||
|
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
Logger.Info($"Done. {sw.Elapsed.TotalSeconds}s");
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using Logging.Net;
|
using Renci.SshNet;
|
||||||
using Renci.SshNet;
|
|
||||||
using ConnectionInfo = Renci.SshNet.ConnectionInfo;
|
using ConnectionInfo = Renci.SshNet.ConnectionInfo;
|
||||||
|
|
||||||
namespace Moonlight.App.Helpers.Files;
|
namespace Moonlight.App.Helpers.Files;
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ public class WingsFileAccess : FileAccess
|
|||||||
request.AddParameter("name", "files");
|
request.AddParameter("name", "files");
|
||||||
request.AddParameter("filename", name);
|
request.AddParameter("filename", name);
|
||||||
request.AddHeader("Content-Type", "multipart/form-data");
|
request.AddHeader("Content-Type", "multipart/form-data");
|
||||||
request.AddHeader("Origin", ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl"));
|
request.AddHeader("Origin", ConfigService.Get().Moonlight.AppUrl);
|
||||||
request.AddFile("files", () =>
|
request.AddFile("files", () =>
|
||||||
{
|
{
|
||||||
return new StreamProgressHelper(dataStream)
|
return new StreamProgressHelper(dataStream)
|
||||||
|
|||||||
@@ -1,9 +1,36 @@
|
|||||||
using Moonlight.App.Services;
|
using System.Text;
|
||||||
|
using Moonlight.App.Services;
|
||||||
|
|
||||||
namespace Moonlight.App.Helpers;
|
namespace Moonlight.App.Helpers;
|
||||||
|
|
||||||
public static class Formatter
|
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)
|
public static string FormatUptime(double uptime)
|
||||||
{
|
{
|
||||||
TimeSpan t = TimeSpan.FromMilliseconds(uptime);
|
TimeSpan t = TimeSpan.FromMilliseconds(uptime);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Logging.Net;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Helpers;
|
namespace Moonlight.App.Helpers;
|
||||||
|
|
||||||
|
|||||||
171
Moonlight/App/Helpers/Logger.cs
Normal file
171
Moonlight/App/Helpers/Logger.cs
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
#region Exception method calls
|
||||||
|
public static void Verbose(Exception exception, string channel = "default")
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
private static string GetNameOfCallingClass(int skipFrames = 4)
|
||||||
|
{
|
||||||
|
string fullName;
|
||||||
|
Type declaringType;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
MethodBase method = new StackFrame(skipFrames, false).GetMethod();
|
||||||
|
declaringType = method.DeclaringType;
|
||||||
|
if (declaringType == null)
|
||||||
|
{
|
||||||
|
return method.Name;
|
||||||
|
}
|
||||||
|
skipFrames++;
|
||||||
|
if (declaringType.Name.Contains("<"))
|
||||||
|
fullName = declaringType.ReflectedType.Name;
|
||||||
|
else
|
||||||
|
fullName = declaringType.Name;
|
||||||
|
}
|
||||||
|
while (declaringType.Module.Name.Equals("mscorlib.dll", StringComparison.OrdinalIgnoreCase) | fullName.Contains("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();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Logging.Net;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Helpers;
|
namespace Moonlight.App.Helpers;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using Logging.Net;
|
namespace Moonlight.App.Helpers;
|
||||||
|
|
||||||
namespace Moonlight.App.Helpers;
|
|
||||||
|
|
||||||
public static class ParseHelper
|
public static class ParseHelper
|
||||||
{
|
{
|
||||||
|
|||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Logging.Net;
|
|
||||||
using Moonlight.App.Helpers.Wings.Data;
|
using Moonlight.App.Helpers.Wings.Data;
|
||||||
using Moonlight.App.Helpers.Wings.Enums;
|
using Moonlight.App.Helpers.Wings.Enums;
|
||||||
using Moonlight.App.Helpers.Wings.Events;
|
using Moonlight.App.Helpers.Wings.Events;
|
||||||
@@ -244,6 +243,7 @@ public class WingsConsole : IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(JsonReaderException){}
|
catch(JsonReaderException){}
|
||||||
|
catch(JsonSerializationException){}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
if (!Disconnecting)
|
if (!Disconnecting)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class WingsConsoleHelper
|
|||||||
{
|
{
|
||||||
ServerRepository = serverRepository;
|
ServerRepository = serverRepository;
|
||||||
|
|
||||||
AppUrl = configService.GetSection("Moonlight").GetValue<string>("AppUrl");
|
AppUrl = configService.Get().Moonlight.AppUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ConnectWings(WingsConsole console, Server server)
|
public async Task ConnectWings(WingsConsole console, Server server)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public class WingsJwtHelper
|
|||||||
{
|
{
|
||||||
ConfigService = configService;
|
ConfigService = configService;
|
||||||
|
|
||||||
AppUrl = ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl");
|
AppUrl = ConfigService.Get().Moonlight.AppUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Generate(string secret, Action<Dictionary<string, string>> claimsAction)
|
public string Generate(string secret, Action<Dictionary<string, string>> claimsAction)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public class AvatarController : Controller
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var url = GravatarController.GetImageUrl(user.Email, 100);
|
var url = GravatarController.GetImageUrl(user.Email.ToLower(), 100);
|
||||||
|
|
||||||
using var client = new HttpClient();
|
using var client = new HttpClient();
|
||||||
var res = await client.GetByteArrayAsync(url);
|
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;
|
ServerService = serverService;
|
||||||
|
|
||||||
var config = configService
|
var config = configService
|
||||||
.GetSection("Moonlight")
|
.Get()
|
||||||
.GetSection("DiscordBotApi");
|
.Moonlight.DiscordBotApi;
|
||||||
|
|
||||||
Enable = config.GetValue<bool>("Enable");
|
Enable = config.Enable;
|
||||||
|
|
||||||
if (Enable)
|
if (Enable)
|
||||||
{
|
{
|
||||||
Token = config.GetValue<string>("Token");
|
Token = config.Token;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,145 +2,166 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Moonlight.App.Database.Entities;
|
||||||
using Moonlight.App.Database.Entities.Notification;
|
using Moonlight.App.Database.Entities.Notification;
|
||||||
using Moonlight.App.Models.Notifications;
|
using Moonlight.App.Models.Notifications;
|
||||||
using Moonlight.App.Repositories;
|
using Moonlight.App.Repositories;
|
||||||
using Moonlight.App.Services;
|
using Moonlight.App.Services;
|
||||||
using Moonlight.App.Services.Notifications;
|
using Moonlight.App.Services.Notifications;
|
||||||
using Moonlight.App.Services.Sessions;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Moonlight.App.Http.Controllers.Api.Moonlight.Notifications;
|
namespace Moonlight.App.Http.Controllers.Api.Moonlight.Notifications;
|
||||||
|
|
||||||
public class ListenController : ControllerBase
|
[ApiController]
|
||||||
|
[Route("api/moonlight/notification/listen")]
|
||||||
|
public class ListenController : Controller
|
||||||
{
|
{
|
||||||
internal WebSocket ws;
|
private WebSocket WebSocket;
|
||||||
private bool active = true;
|
|
||||||
private bool isAuth = false;
|
|
||||||
private NotificationClient Client;
|
private NotificationClient Client;
|
||||||
|
private CancellationTokenSource CancellationTokenSource = new();
|
||||||
|
|
||||||
|
private User? CurrentUser;
|
||||||
|
|
||||||
private readonly IdentityService IdentityService;
|
|
||||||
private readonly NotificationRepository NotificationRepository;
|
|
||||||
private readonly OneTimeJwtService OneTimeJwtService;
|
private readonly OneTimeJwtService OneTimeJwtService;
|
||||||
private readonly NotificationClientService NotificationClientService;
|
|
||||||
private readonly NotificationServerService NotificationServerService;
|
private readonly NotificationServerService NotificationServerService;
|
||||||
|
private readonly Repository<NotificationClient> NotificationClientRepository;
|
||||||
|
|
||||||
public ListenController(IdentityService identityService,
|
public ListenController(
|
||||||
NotificationRepository notificationRepository,
|
|
||||||
OneTimeJwtService oneTimeJwtService,
|
OneTimeJwtService oneTimeJwtService,
|
||||||
NotificationClientService notificationClientService,
|
NotificationServerService notificationServerService, Repository<NotificationClient> notificationClientRepository)
|
||||||
NotificationServerService notificationServerService)
|
|
||||||
{
|
{
|
||||||
IdentityService = identityService;
|
|
||||||
NotificationRepository = notificationRepository;
|
|
||||||
OneTimeJwtService = oneTimeJwtService;
|
OneTimeJwtService = oneTimeJwtService;
|
||||||
NotificationClientService = notificationClientService;
|
|
||||||
NotificationServerService = notificationServerService;
|
NotificationServerService = notificationServerService;
|
||||||
|
NotificationClientRepository = notificationClientRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("/api/moonlight/notifications/listen")]
|
[Route("/api/moonlight/notifications/listen")]
|
||||||
public async Task Get()
|
public async Task<ActionResult> Get()
|
||||||
{
|
{
|
||||||
if (HttpContext.WebSockets.IsWebSocketRequest)
|
if (HttpContext.WebSockets.IsWebSocketRequest)
|
||||||
{
|
{
|
||||||
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
|
WebSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
|
||||||
ws = webSocket;
|
|
||||||
await Echo();
|
await ProcessWebsocket();
|
||||||
|
|
||||||
|
return new EmptyResult();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
HttpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
|
return StatusCode(400);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Echo()
|
private async Task ProcessWebsocket()
|
||||||
{
|
{
|
||||||
while (active)
|
while (!CancellationTokenSource.Token.IsCancellationRequested && WebSocket.State == WebSocketState.Open)
|
||||||
{
|
{
|
||||||
byte[] bytes = new byte[1024 * 16];
|
try
|
||||||
var asg = new ArraySegment<byte>(bytes);
|
|
||||||
var res = await ws.ReceiveAsync(asg, CancellationToken.None);
|
|
||||||
|
|
||||||
var text = Encoding.UTF8.GetString(bytes).Trim('\0');
|
|
||||||
|
|
||||||
var obj = JsonConvert.DeserializeObject<BasicWSModel>(text);
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(obj.Action))
|
|
||||||
{
|
{
|
||||||
await HandleRequest(text, obj.Action);
|
byte[] buffer = new byte[1024 * 16];
|
||||||
}
|
_ = await WebSocket.ReceiveAsync(buffer, CancellationTokenSource.Token);
|
||||||
|
var text = Encoding.UTF8.GetString(buffer).Trim('\0');
|
||||||
|
|
||||||
active = ws.State == WebSocketState.Open;
|
var basicWsModel = JsonConvert.DeserializeObject<BasicWSModel>(text) ?? new();
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(basicWsModel.Action))
|
||||||
|
{
|
||||||
|
await HandleRequest(text, basicWsModel.Action);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WebSocket.State != WebSocketState.Open)
|
||||||
|
{
|
||||||
|
CancellationTokenSource.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (WebSocketException e)
|
||||||
|
{
|
||||||
|
CancellationTokenSource.Cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await NotificationServerService.UnRegisterClient(Client);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleRequest(string text, string action)
|
private async Task HandleRequest(string text, string action)
|
||||||
{
|
{
|
||||||
if (!isAuth && action == "login")
|
if (CurrentUser == null && action != "login")
|
||||||
await Login(text);
|
|
||||||
else if (!isAuth)
|
|
||||||
await ws.SendAsync(Encoding.UTF8.GetBytes("{\"error\": \"Unauthorised\"}"), WebSocketMessageType.Text,
|
|
||||||
WebSocketMessageFlags.EndOfMessage, CancellationToken.None);
|
|
||||||
else switch (action)
|
|
||||||
{
|
{
|
||||||
|
await Send("{\"error\": \"Unauthorised\"}");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case "login":
|
||||||
|
await Login(text);
|
||||||
|
break;
|
||||||
case "received":
|
case "received":
|
||||||
await Received(text);
|
await Received(text);
|
||||||
break;
|
break;
|
||||||
case "read":
|
case "read":
|
||||||
await Read(text);
|
await Read(text);
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task Send(string text)
|
||||||
|
{
|
||||||
|
await WebSocket.SendAsync(
|
||||||
|
Encoding.UTF8.GetBytes(text),
|
||||||
|
WebSocketMessageType.Text,
|
||||||
|
WebSocketMessageFlags.EndOfMessage, CancellationTokenSource.Token
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task Login(string json)
|
private async Task Login(string json)
|
||||||
{
|
{
|
||||||
var jwt = JsonConvert.DeserializeObject<Login>(json).token;
|
var loginModel = JsonConvert.DeserializeObject<Login>(json) ?? new();
|
||||||
|
|
||||||
var dict = await OneTimeJwtService.Validate(jwt);
|
var dict = await OneTimeJwtService.Validate(loginModel.Token);
|
||||||
|
|
||||||
if (dict == null)
|
if (dict == null)
|
||||||
{
|
{
|
||||||
string error = "{\"status\":false}";
|
await Send("{\"status\":false}");
|
||||||
var bytes = Encoding.UTF8.GetBytes(error);
|
|
||||||
await ws.SendAsync(bytes, WebSocketMessageType.Text, WebSocketMessageFlags.EndOfMessage, CancellationToken.None);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var _clientId = dict["clientId"];
|
if (!int.TryParse(dict["clientId"], out int clientId))
|
||||||
var clientId = int.Parse(_clientId);
|
{
|
||||||
|
await Send("{\"status\":false}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var client = NotificationRepository.GetClients().Include(x => x.User).First(x => x.Id == clientId);
|
Client = NotificationClientRepository
|
||||||
|
.Get()
|
||||||
|
.Include(x => x.User)
|
||||||
|
.First(x => x.Id == clientId);
|
||||||
|
|
||||||
Client = client;
|
CurrentUser = Client.User;
|
||||||
await InitWebsocket();
|
|
||||||
|
|
||||||
string success = "{\"status\":true}";
|
await NotificationServerService.RegisterClient(WebSocket, Client);
|
||||||
var byt = Encoding.UTF8.GetBytes(success);
|
|
||||||
await ws.SendAsync(byt, WebSocketMessageType.Text, WebSocketMessageFlags.EndOfMessage, CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task InitWebsocket()
|
await Send("{\"status\":true}");
|
||||||
{
|
|
||||||
NotificationClientService.listenController = this;
|
|
||||||
NotificationClientService.WebsocketReady(Client);
|
|
||||||
|
|
||||||
isAuth = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Received(string json)
|
private async Task Received(string json)
|
||||||
{
|
{
|
||||||
var id = JsonConvert.DeserializeObject<NotificationById>(json).notification;
|
var id = JsonConvert.DeserializeObject<NotificationById>(json).Notification;
|
||||||
|
|
||||||
//TODO: Implement ws notification received
|
//TODO: Implement ws notification received
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Read(string json)
|
private async Task Read(string json)
|
||||||
{
|
{
|
||||||
var id = JsonConvert.DeserializeObject<NotificationById>(json).notification;
|
var model = JsonConvert.DeserializeObject<NotificationById>(json) ?? new();
|
||||||
|
|
||||||
await NotificationServerService.SendAction(NotificationClientService.User,
|
await NotificationServerService.SendAction(
|
||||||
JsonConvert.SerializeObject(new NotificationById() {Action = "hide", notification = id}));
|
CurrentUser!,
|
||||||
|
JsonConvert.SerializeObject(
|
||||||
|
new NotificationById()
|
||||||
|
{
|
||||||
|
Action = "hide", Notification = model.Notification
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,7 @@ public class RegisterController : Controller
|
|||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<ActionResult<TokenRegister>> Register()
|
public async Task<ActionResult<TokenRegister>> Register()
|
||||||
{
|
{
|
||||||
var user = await IdentityService.Get();
|
var user = IdentityService.User;
|
||||||
|
|
||||||
if (user == null)
|
if (user == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using Logging.Net;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Moonlight.App.Helpers;
|
||||||
using Moonlight.App.Services;
|
using Moonlight.App.Services;
|
||||||
using Moonlight.App.Services.Sessions;
|
using Moonlight.App.Services.Sessions;
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ public class OAuth2Controller : Controller
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var currentUser = await IdentityService.Get();
|
var currentUser = IdentityService.User;
|
||||||
|
|
||||||
if (currentUser != null)
|
if (currentUser != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,12 +1,6 @@
|
|||||||
using System.Text;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Logging.Net;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Moonlight.App.Helpers;
|
using Moonlight.App.Helpers;
|
||||||
using Moonlight.App.Models.Misc;
|
|
||||||
using Moonlight.App.Services;
|
|
||||||
using Moonlight.App.Services.Files;
|
using Moonlight.App.Services.Files;
|
||||||
using Moonlight.App.Services.LogServices;
|
|
||||||
using Moonlight.App.Services.Sessions;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Http.Controllers.Api.Moonlight;
|
namespace Moonlight.App.Http.Controllers.Api.Moonlight;
|
||||||
|
|
||||||
@@ -14,13 +8,10 @@ namespace Moonlight.App.Http.Controllers.Api.Moonlight;
|
|||||||
[Route("api/moonlight/resources")]
|
[Route("api/moonlight/resources")]
|
||||||
public class ResourcesController : Controller
|
public class ResourcesController : Controller
|
||||||
{
|
{
|
||||||
private readonly SecurityLogService SecurityLogService;
|
|
||||||
private readonly BucketService BucketService;
|
private readonly BucketService BucketService;
|
||||||
|
|
||||||
public ResourcesController(SecurityLogService securityLogService,
|
public ResourcesController(BucketService bucketService)
|
||||||
BucketService bucketService)
|
|
||||||
{
|
{
|
||||||
SecurityLogService = securityLogService;
|
|
||||||
BucketService = bucketService;
|
BucketService = bucketService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,10 +20,7 @@ public class ResourcesController : Controller
|
|||||||
{
|
{
|
||||||
if (name.Contains(".."))
|
if (name.Contains(".."))
|
||||||
{
|
{
|
||||||
await SecurityLogService.Log(SecurityLogType.PathTransversal, x =>
|
Logger.Warn($"Detected an attempted path transversal. Path: {name}", "security");
|
||||||
{
|
|
||||||
x.Add<string>(name);
|
|
||||||
});
|
|
||||||
|
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
@@ -52,10 +40,7 @@ public class ResourcesController : Controller
|
|||||||
{
|
{
|
||||||
if (name.Contains(".."))
|
if (name.Contains(".."))
|
||||||
{
|
{
|
||||||
await SecurityLogService.Log(SecurityLogType.PathTransversal, x =>
|
Logger.Warn($"Detected an attempted path transversal. Path: {name}", "security");
|
||||||
{
|
|
||||||
x.Add<string>(name);
|
|
||||||
});
|
|
||||||
|
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
@@ -75,10 +60,7 @@ public class ResourcesController : Controller
|
|||||||
{
|
{
|
||||||
if (name.Contains(".."))
|
if (name.Contains(".."))
|
||||||
{
|
{
|
||||||
await SecurityLogService.Log(SecurityLogType.PathTransversal, x =>
|
Logger.Warn($"Detected an attempted path transversal. Path: {name}", "security");
|
||||||
{
|
|
||||||
x.Add<string>(name);
|
|
||||||
});
|
|
||||||
|
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
using Logging.Net;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Moonlight.App.Database.Entities;
|
using Moonlight.App.Database.Entities;
|
||||||
using Moonlight.App.Events;
|
using Moonlight.App.Events;
|
||||||
using Moonlight.App.Http.Requests.Daemon;
|
using Moonlight.App.Http.Requests.Daemon;
|
||||||
using Moonlight.App.Repositories;
|
using Moonlight.App.Repositories;
|
||||||
using Moonlight.App.Services;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Http.Controllers.Api.Remote;
|
namespace Moonlight.App.Http.Controllers.Api.Remote;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Logging.Net;
|
using Moonlight.App.Helpers;
|
||||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
|
||||||
|
|
||||||
namespace Moonlight.App.LogMigrator;
|
namespace Moonlight.App.LogMigrator;
|
||||||
|
|
||||||
@@ -28,19 +27,39 @@ public class LogMigrator : ILogger
|
|||||||
switch (logLevel)
|
switch (logLevel)
|
||||||
{
|
{
|
||||||
case LogLevel.Critical:
|
case LogLevel.Critical:
|
||||||
Logger.Fatal($"[{Name}] {formatter(state, exception)}");
|
Logger.Fatal(formatter(state, exception));
|
||||||
|
|
||||||
|
if(exception != null)
|
||||||
|
Logger.Fatal(exception);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case LogLevel.Warning:
|
case LogLevel.Warning:
|
||||||
Logger.Warn($"[{Name}] {formatter(state, exception)}");
|
Logger.Warn(formatter(state, exception));
|
||||||
|
|
||||||
|
if(exception != null)
|
||||||
|
Logger.Warn(exception);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case LogLevel.Debug:
|
case LogLevel.Debug:
|
||||||
Logger.Debug($"[{Name}] {formatter(state, exception)}");
|
Logger.Debug(formatter(state, exception));
|
||||||
|
|
||||||
|
if(exception != null)
|
||||||
|
Logger.Debug(exception);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case LogLevel.Error:
|
case LogLevel.Error:
|
||||||
Logger.Error($"[{Name}] {formatter(state, exception)}");
|
Logger.Error(formatter(state, exception));
|
||||||
|
|
||||||
|
if(exception != null)
|
||||||
|
Logger.Error(exception);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case LogLevel.Information:
|
case LogLevel.Information:
|
||||||
Logger.Info($"[{Name}] {formatter(state, exception)}");
|
Logger.Info(formatter(state, exception));
|
||||||
|
|
||||||
|
if(exception != null)
|
||||||
|
Logger.Info(exception);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Moonlight.App.Models.Misc;
|
||||||
|
|
||||||
namespace Moonlight.App.Models.Forms;
|
namespace Moonlight.App.Models.Forms;
|
||||||
|
|
||||||
@@ -10,4 +11,8 @@ public class SubscriptionDataModel
|
|||||||
|
|
||||||
[Required(ErrorMessage = "You need to enter a description")]
|
[Required(ErrorMessage = "You need to enter a description")]
|
||||||
public string Description { get; set; } = "";
|
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;
|
||||||
|
}
|
||||||
19
Moonlight/App/Models/Misc/ActiveNotificationClient.cs
Normal file
19
Moonlight/App/Models/Misc/ActiveNotificationClient.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System.Net.WebSockets;
|
||||||
|
using System.Text;
|
||||||
|
using Moonlight.App.Database.Entities.Notification;
|
||||||
|
|
||||||
|
namespace Moonlight.App.Models.Misc;
|
||||||
|
|
||||||
|
public class ActiveNotificationClient
|
||||||
|
{
|
||||||
|
public WebSocket WebSocket { get; set; }
|
||||||
|
public NotificationClient Client { get; set; }
|
||||||
|
|
||||||
|
public async Task SendAction(string action)
|
||||||
|
{
|
||||||
|
await WebSocket.SendAsync(
|
||||||
|
Encoding.UTF8.GetBytes(action),
|
||||||
|
WebSocketMessageType.Text,
|
||||||
|
WebSocketMessageFlags.EndOfMessage, CancellationToken.None);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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; }
|
||||||
|
}
|
||||||
8
Moonlight/App/Models/Misc/MalwareScanResult.cs
Normal file
8
Moonlight/App/Models/Misc/MalwareScanResult.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Moonlight.App.Models.Misc;
|
||||||
|
|
||||||
|
public class MalwareScanResult
|
||||||
|
{
|
||||||
|
public string Title { get; set; } = "";
|
||||||
|
public string Description { get; set; } = "";
|
||||||
|
public string Author { 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; }
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
namespace Moonlight.App.Models.Notifications;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Moonlight.App.Models.Notifications;
|
||||||
|
|
||||||
public class Login : BasicWSModel
|
public class Login : BasicWSModel
|
||||||
{
|
{
|
||||||
public string token { get; set; }
|
[JsonProperty("token")] public string Token { get; set; } = "";
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
namespace Moonlight.App.Models.Notifications;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Moonlight.App.Models.Notifications;
|
||||||
|
|
||||||
public class NotificationById : BasicWSModel
|
public class NotificationById : BasicWSModel
|
||||||
{
|
{
|
||||||
public int notification { get; set; }
|
[JsonProperty("notification")]
|
||||||
|
public int Notification { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using Logging.Net;
|
|
||||||
using Moonlight.App.Database.Entities;
|
using Moonlight.App.Database.Entities;
|
||||||
using Moonlight.App.Exceptions;
|
using Moonlight.App.Exceptions;
|
||||||
using Moonlight.App.Helpers;
|
using Moonlight.App.Helpers;
|
||||||
@@ -87,6 +86,13 @@ public class DiscordOAuth2Provider : OAuth2Provider
|
|||||||
|
|
||||||
var email = getData.GetValue<string>("email");
|
var email = getData.GetValue<string>("email");
|
||||||
var id = getData.GetValue<ulong>("id");
|
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
|
// Handle data
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using Logging.Net;
|
|
||||||
using Moonlight.App.ApiClients.Google.Requests;
|
using Moonlight.App.ApiClients.Google.Requests;
|
||||||
using Moonlight.App.Database.Entities;
|
using Moonlight.App.Database.Entities;
|
||||||
using Moonlight.App.Exceptions;
|
using Moonlight.App.Exceptions;
|
||||||
|
|||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
431
Moonlight/App/Perms/Permissions.cs
Normal file
431
Moonlight/App/Perms/Permissions.cs
Normal file
@@ -0,0 +1,431 @@
|
|||||||
|
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 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 AdminNodeDdos = new()
|
||||||
|
{
|
||||||
|
Index = 8,
|
||||||
|
Name = "Admin Node DDoS",
|
||||||
|
Description = "Manage DDoS protection for nodes 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? 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using Logging.Net;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using MineStatLib;
|
using MineStatLib;
|
||||||
using Moonlight.App.ApiClients.Daemon.Resources;
|
using Moonlight.App.ApiClients.Daemon.Resources;
|
||||||
using Moonlight.App.ApiClients.Wings;
|
using Moonlight.App.ApiClients.Wings;
|
||||||
@@ -44,17 +43,15 @@ public class CleanupService
|
|||||||
CompletedAt = DateTimeService.GetCurrent();
|
CompletedAt = DateTimeService.GetCurrent();
|
||||||
IsRunning = false;
|
IsRunning = false;
|
||||||
|
|
||||||
var config = ConfigService.GetSection("Moonlight").GetSection("Cleanup");
|
var config = ConfigService.Get().Moonlight.Cleanup;
|
||||||
|
|
||||||
/*
|
if (!config.Enable || ConfigService.DebugMode)
|
||||||
* if (!config.GetValue<bool>("Enable") || ConfigService.DebugMode)
|
|
||||||
{
|
{
|
||||||
Logger.Info("Disabling cleanup service");
|
Logger.Info("Disabling cleanup service");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
Timer = new(TimeSpan.FromMinutes(config.GetValue<int>("Wait")));
|
Timer = new(TimeSpan.FromMinutes(config.Wait));
|
||||||
|
|
||||||
Task.Run(Run);
|
Task.Run(Run);
|
||||||
}
|
}
|
||||||
@@ -66,12 +63,12 @@ public class CleanupService
|
|||||||
IsRunning = true;
|
IsRunning = true;
|
||||||
|
|
||||||
using var scope = ServiceScopeFactory.CreateScope();
|
using var scope = ServiceScopeFactory.CreateScope();
|
||||||
var config = ConfigService.GetSection("Moonlight").GetSection("Cleanup");
|
var config = ConfigService.Get().Moonlight.Cleanup;
|
||||||
|
|
||||||
var maxCpu = config.GetValue<int>("Cpu");
|
var maxCpu = config.Cpu;
|
||||||
var minMemory = config.GetValue<int>("Memory");
|
var minMemory = config.Memory;
|
||||||
var maxUptime = config.GetValue<int>("Uptime");
|
var maxUptime = config.Uptime;
|
||||||
var minUptime = config.GetValue<int>("MinUptime");
|
var minUptime = config.MinUptime;
|
||||||
|
|
||||||
var nodeRepository = scope.ServiceProvider.GetRequiredService<NodeRepository>();
|
var nodeRepository = scope.ServiceProvider.GetRequiredService<NodeRepository>();
|
||||||
var nodeService = scope.ServiceProvider.GetRequiredService<NodeService>();
|
var nodeService = scope.ServiceProvider.GetRequiredService<NodeService>();
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using Discord;
|
using Discord;
|
||||||
using Discord.Webhook;
|
using Discord.Webhook;
|
||||||
using Logging.Net;
|
|
||||||
using Moonlight.App.Database.Entities;
|
using Moonlight.App.Database.Entities;
|
||||||
using Moonlight.App.Events;
|
using Moonlight.App.Events;
|
||||||
|
using Moonlight.App.Helpers;
|
||||||
using Moonlight.App.Services.Files;
|
using Moonlight.App.Services.Files;
|
||||||
|
|
||||||
namespace Moonlight.App.Services.Background;
|
namespace Moonlight.App.Services.Background;
|
||||||
@@ -22,19 +22,20 @@ public class DiscordNotificationService
|
|||||||
Event = eventSystem;
|
Event = eventSystem;
|
||||||
ResourceService = resourceService;
|
ResourceService = resourceService;
|
||||||
|
|
||||||
var config = configService.GetSection("Moonlight").GetSection("DiscordNotifications");
|
var config = configService.Get().Moonlight.DiscordNotifications;
|
||||||
|
|
||||||
if (config.GetValue<bool>("Enable"))
|
if (config.Enable)
|
||||||
{
|
{
|
||||||
Logger.Info("Discord notifications enabled");
|
Logger.Info("Discord notifications enabled");
|
||||||
|
|
||||||
Client = new(config.GetValue<string>("WebHook"));
|
Client = new(config.WebHook);
|
||||||
AppUrl = configService.GetSection("Moonlight").GetValue<string>("AppUrl");
|
AppUrl = configService.Get().Moonlight.AppUrl;
|
||||||
|
|
||||||
Event.On<User>("supportChat.new", this, OnNewSupportChat);
|
Event.On<User>("supportChat.new", this, OnNewSupportChat);
|
||||||
Event.On<SupportChatMessage>("supportChat.message", this, OnSupportChatMessage);
|
Event.On<SupportChatMessage>("supportChat.message", this, OnSupportChatMessage);
|
||||||
Event.On<User>("supportChat.close", this, OnSupportChatClose);
|
Event.On<User>("supportChat.close", this, OnSupportChatClose);
|
||||||
Event.On<User>("user.rating", this, OnUserRated);
|
Event.On<User>("user.rating", this, OnUserRated);
|
||||||
|
Event.On<User>("billing.completed", this, OnBillingCompleted);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -42,6 +43,21 @@ public class DiscordNotificationService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
private async Task OnUserRated(User user)
|
||||||
{
|
{
|
||||||
await SendNotification("", builder =>
|
await SendNotification("", builder =>
|
||||||
|
|||||||
196
Moonlight/App/Services/Background/MalwareScanService.cs
Normal file
196
Moonlight/App/Services/Background/MalwareScanService.cs
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
using Moonlight.App.ApiClients.Daemon.Resources;
|
||||||
|
using Moonlight.App.Database.Entities;
|
||||||
|
using Moonlight.App.Events;
|
||||||
|
using Moonlight.App.Exceptions;
|
||||||
|
using Moonlight.App.Helpers;
|
||||||
|
using Moonlight.App.Models.Misc;
|
||||||
|
using Moonlight.App.Repositories;
|
||||||
|
|
||||||
|
namespace Moonlight.App.Services.Background;
|
||||||
|
|
||||||
|
public class MalwareScanService
|
||||||
|
{
|
||||||
|
private Repository<Server> ServerRepository;
|
||||||
|
private Repository<Node> NodeRepository;
|
||||||
|
private NodeService NodeService;
|
||||||
|
private ServerService ServerService;
|
||||||
|
|
||||||
|
private readonly EventSystem Event;
|
||||||
|
private readonly IServiceScopeFactory ServiceScopeFactory;
|
||||||
|
|
||||||
|
public bool IsRunning { get; private set; }
|
||||||
|
public readonly Dictionary<Server, MalwareScanResult[]> ScanResults;
|
||||||
|
public string Status { get; private set; } = "N/A";
|
||||||
|
|
||||||
|
public MalwareScanService(IServiceScopeFactory serviceScopeFactory, EventSystem eventSystem)
|
||||||
|
{
|
||||||
|
ServiceScopeFactory = serviceScopeFactory;
|
||||||
|
Event = eventSystem;
|
||||||
|
|
||||||
|
ScanResults = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task Start()
|
||||||
|
{
|
||||||
|
if (IsRunning)
|
||||||
|
throw new DisplayException("Malware scan is already running");
|
||||||
|
|
||||||
|
Task.Run(Run);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Run()
|
||||||
|
{
|
||||||
|
IsRunning = true;
|
||||||
|
Status = "Clearing last results";
|
||||||
|
await Event.Emit("malwareScan.status", IsRunning);
|
||||||
|
|
||||||
|
lock (ScanResults)
|
||||||
|
{
|
||||||
|
ScanResults.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
await Event.Emit("malwareScan.result");
|
||||||
|
|
||||||
|
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>();
|
||||||
|
|
||||||
|
var nodes = NodeRepository.Get().ToArray();
|
||||||
|
var containers = new List<Container>();
|
||||||
|
|
||||||
|
// Fetch and summarize all running containers from all nodes
|
||||||
|
Logger.Verbose("Fetching and summarizing all running containers from all nodes");
|
||||||
|
|
||||||
|
Status = "Fetching and summarizing all running containers from all nodes";
|
||||||
|
await Event.Emit("malwareScan.status", IsRunning);
|
||||||
|
|
||||||
|
foreach (var node in nodes)
|
||||||
|
{
|
||||||
|
var metrics = await NodeService.GetDockerMetrics(node);
|
||||||
|
|
||||||
|
foreach (var container in metrics.Containers)
|
||||||
|
{
|
||||||
|
containers.Add(container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var containerServerMapped = new Dictionary<Server, Container>();
|
||||||
|
|
||||||
|
// Map all the containers to their corresponding server if existing
|
||||||
|
Logger.Verbose("Mapping all the containers to their corresponding server if existing");
|
||||||
|
|
||||||
|
Status = "Mapping all the containers to their corresponding server if existing";
|
||||||
|
await Event.Emit("malwareScan.status", IsRunning);
|
||||||
|
|
||||||
|
foreach (var container in containers)
|
||||||
|
{
|
||||||
|
if (Guid.TryParse(container.Name, out Guid uuid))
|
||||||
|
{
|
||||||
|
var server = ServerRepository
|
||||||
|
.Get()
|
||||||
|
.FirstOrDefault(x => x.Uuid == uuid);
|
||||||
|
|
||||||
|
if(server == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
containerServerMapped.Add(server, container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
var results = new List<MalwareScanResult>();
|
||||||
|
|
||||||
|
// TODO: Move scans to an universal format / api
|
||||||
|
|
||||||
|
// Define scans here
|
||||||
|
|
||||||
|
async Task ScanSelfBot()
|
||||||
|
{
|
||||||
|
var access = await ServerService.CreateFileAccess(server, null!);
|
||||||
|
var fileElements = await access.Ls();
|
||||||
|
|
||||||
|
if (fileElements.Any(x => x.Name == "tokens.txt"))
|
||||||
|
{
|
||||||
|
results.Add(new ()
|
||||||
|
{
|
||||||
|
Title = "Found SelfBot",
|
||||||
|
Description = "Detected suspicious 'tokens.txt' file which may contain tokens for a selfbot",
|
||||||
|
Author = "Marcel Baumgartner"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task ScanFakePlayerPlugins()
|
||||||
|
{
|
||||||
|
var access = await ServerService.CreateFileAccess(server, null!);
|
||||||
|
var fileElements = await access.Ls();
|
||||||
|
|
||||||
|
if (fileElements.Any(x => !x.IsFile && x.Name == "plugins")) // Check for plugins folder
|
||||||
|
{
|
||||||
|
await access.Cd("plugins");
|
||||||
|
fileElements = await access.Ls();
|
||||||
|
|
||||||
|
foreach (var fileElement in fileElements)
|
||||||
|
{
|
||||||
|
if (fileElement.Name.ToLower().Contains("fake"))
|
||||||
|
{
|
||||||
|
results.Add(new()
|
||||||
|
{
|
||||||
|
Title = "Fake player plugin",
|
||||||
|
Description = $"Suspicious plugin file: {fileElement.Name}",
|
||||||
|
Author = "Marcel Baumgartner"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute scans
|
||||||
|
await ScanSelfBot();
|
||||||
|
await ScanFakePlayerPlugins();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
127
Moonlight/App/Services/BillingService.cs
Normal file
127
Moonlight/App/Services/BillingService.cs
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
using System.Globalization;
|
||||||
|
using Moonlight.App.Database.Entities;
|
||||||
|
using Moonlight.App.Events;
|
||||||
|
using Moonlight.App.Exceptions;
|
||||||
|
using Moonlight.App.Repositories;
|
||||||
|
using Moonlight.App.Services.Mail;
|
||||||
|
using Moonlight.App.Services.Sessions;
|
||||||
|
using Stripe.Checkout;
|
||||||
|
using Subscription = Moonlight.App.Database.Entities.Subscription;
|
||||||
|
|
||||||
|
namespace Moonlight.App.Services;
|
||||||
|
|
||||||
|
public class BillingService
|
||||||
|
{
|
||||||
|
private readonly ConfigService ConfigService;
|
||||||
|
private readonly SubscriptionService SubscriptionService;
|
||||||
|
private readonly Repository<Subscription> SubscriptionRepository;
|
||||||
|
private readonly SessionServerService SessionServerService;
|
||||||
|
private readonly EventSystem Event;
|
||||||
|
private readonly MailService MailService;
|
||||||
|
|
||||||
|
public BillingService(
|
||||||
|
ConfigService configService,
|
||||||
|
SubscriptionService subscriptionService,
|
||||||
|
Repository<Subscription> subscriptionRepository,
|
||||||
|
EventSystem eventSystem,
|
||||||
|
SessionServerService sessionServerService,
|
||||||
|
MailService mailService)
|
||||||
|
{
|
||||||
|
ConfigService = configService;
|
||||||
|
SubscriptionService = subscriptionService;
|
||||||
|
SubscriptionRepository = subscriptionRepository;
|
||||||
|
Event = eventSystem;
|
||||||
|
SessionServerService = sessionServerService;
|
||||||
|
MailService = mailService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> StartCheckout(User user, Subscription subscription)
|
||||||
|
{
|
||||||
|
var appUrl = ConfigService.Get().Moonlight.AppUrl;
|
||||||
|
var controllerUrl = appUrl + "/api/moonlight/billing";
|
||||||
|
|
||||||
|
var options = new SessionCreateOptions()
|
||||||
|
{
|
||||||
|
LineItems = new()
|
||||||
|
{
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Price = subscription.StripePriceId,
|
||||||
|
Quantity = 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Mode = "payment",
|
||||||
|
SuccessUrl = controllerUrl + "/success",
|
||||||
|
CancelUrl = controllerUrl + "/cancel",
|
||||||
|
AutomaticTax = new SessionAutomaticTaxOptions()
|
||||||
|
{
|
||||||
|
Enabled = true
|
||||||
|
},
|
||||||
|
CustomerEmail = user.Email.ToLower(),
|
||||||
|
Metadata = new()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"productId",
|
||||||
|
subscription.StripeProductId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var service = new SessionService();
|
||||||
|
|
||||||
|
var session = await service.CreateAsync(options);
|
||||||
|
|
||||||
|
return session.Url;
|
||||||
|
}
|
||||||
|
public async Task CompleteCheckout(User user)
|
||||||
|
{
|
||||||
|
var sessionService = new SessionService();
|
||||||
|
|
||||||
|
var sessionsPerUser = await sessionService.ListAsync(new SessionListOptions()
|
||||||
|
{
|
||||||
|
CustomerDetails = new()
|
||||||
|
{
|
||||||
|
Email = user.Email
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var latestCompletedSession = sessionsPerUser
|
||||||
|
.Where(x => x.Status == "complete")
|
||||||
|
.Where(x => x.PaymentStatus == "paid")
|
||||||
|
.MaxBy(x => x.Created);
|
||||||
|
|
||||||
|
if (latestCompletedSession == null)
|
||||||
|
throw new DisplayException("No completed session found");
|
||||||
|
|
||||||
|
var productId = latestCompletedSession.Metadata["productId"];
|
||||||
|
|
||||||
|
var subscription = SubscriptionRepository
|
||||||
|
.Get()
|
||||||
|
.FirstOrDefault(x => x.StripeProductId == productId);
|
||||||
|
|
||||||
|
if (subscription == null)
|
||||||
|
throw new DisplayException("No subscription for this product found");
|
||||||
|
|
||||||
|
// if (await SubscriptionService.GetActiveSubscription(user) != null)
|
||||||
|
// {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
await SubscriptionService.SetActiveSubscription(user, subscription);
|
||||||
|
|
||||||
|
await MailService.SendMail(user, "checkoutComplete", values =>
|
||||||
|
{
|
||||||
|
values.Add("SubscriptionName", subscription.Name);
|
||||||
|
values.Add("SubscriptionPrice", subscription.Price
|
||||||
|
.ToString(CultureInfo.InvariantCulture));
|
||||||
|
values.Add("SubscriptionCurrency", subscription.Currency
|
||||||
|
.ToString());
|
||||||
|
values.Add("SubscriptionDuration", subscription.Duration
|
||||||
|
.ToString(CultureInfo.InvariantCulture));
|
||||||
|
});
|
||||||
|
|
||||||
|
await Event.Emit("billing.completed", user);
|
||||||
|
|
||||||
|
await SessionServerService.ReloadUserSessions(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,15 @@
|
|||||||
using System.Text;
|
using Moonlight.App.Configuration;
|
||||||
using Logging.Net;
|
|
||||||
using Microsoft.Extensions.Primitives;
|
|
||||||
using Moonlight.App.Helpers;
|
using Moonlight.App.Helpers;
|
||||||
using Moonlight.App.Services.Files;
|
using Moonlight.App.Services.Files;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Moonlight.App.Services;
|
namespace Moonlight.App.Services;
|
||||||
|
|
||||||
public class ConfigService : IConfiguration
|
public class ConfigService
|
||||||
{
|
{
|
||||||
private readonly StorageService StorageService;
|
private readonly StorageService StorageService;
|
||||||
|
private readonly string Path;
|
||||||
private IConfiguration Configuration;
|
private ConfigV1 Configuration;
|
||||||
|
|
||||||
public bool DebugMode { get; private set; } = false;
|
public bool DebugMode { get; private set; } = false;
|
||||||
public bool SqlDebugMode { get; private set; } = false;
|
public bool SqlDebugMode { get; private set; } = false;
|
||||||
@@ -20,6 +19,11 @@ public class ConfigService : IConfiguration
|
|||||||
StorageService = storageService;
|
StorageService = storageService;
|
||||||
StorageService.EnsureCreated();
|
StorageService.EnsureCreated();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("ML_CONFIG_PATH")))
|
||||||
|
Path = Environment.GetEnvironmentVariable("ML_CONFIG_PATH")!;
|
||||||
|
else
|
||||||
|
Path = PathBuilder.File("storage", "configs", "config.json");
|
||||||
|
|
||||||
Reload();
|
Reload();
|
||||||
|
|
||||||
// Env vars
|
// Env vars
|
||||||
@@ -42,37 +46,38 @@ public class ConfigService : IConfiguration
|
|||||||
|
|
||||||
public void Reload()
|
public void Reload()
|
||||||
{
|
{
|
||||||
Logger.Info($"Reading config from '{PathBuilder.File("storage", "configs", "config.json")}'");
|
if (!File.Exists(Path))
|
||||||
|
{
|
||||||
|
File.WriteAllText(Path, "{}");
|
||||||
|
}
|
||||||
|
|
||||||
Configuration = new ConfigurationBuilder().AddJsonStream(
|
Configuration = JsonConvert.DeserializeObject<ConfigV1>(
|
||||||
new MemoryStream(Encoding.ASCII.GetBytes(
|
File.ReadAllText(Path)
|
||||||
File.ReadAllText(
|
) ?? new ConfigV1();
|
||||||
PathBuilder.File("storage", "configs", "config.json")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)).Build();
|
|
||||||
|
|
||||||
Logger.Info("Reloaded configuration file");
|
File.WriteAllText(Path, JsonConvert.SerializeObject(Configuration, Formatting.Indented));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<IConfigurationSection> GetChildren()
|
public void Save(ConfigV1 configV1)
|
||||||
{
|
{
|
||||||
return Configuration.GetChildren();
|
Configuration = configV1;
|
||||||
|
Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IChangeToken GetReloadToken()
|
public void Save()
|
||||||
{
|
{
|
||||||
return Configuration.GetReloadToken();
|
if (!File.Exists(Path))
|
||||||
|
{
|
||||||
|
File.WriteAllText(Path, "{}");
|
||||||
|
}
|
||||||
|
|
||||||
|
File.WriteAllText(Path, JsonConvert.SerializeObject(Configuration, Formatting.Indented));
|
||||||
|
|
||||||
|
Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IConfigurationSection GetSection(string key)
|
public ConfigV1 Get()
|
||||||
{
|
{
|
||||||
return Configuration.GetSection(key);
|
return Configuration;
|
||||||
}
|
|
||||||
|
|
||||||
public string this[string key]
|
|
||||||
{
|
|
||||||
get => Configuration[key];
|
|
||||||
set => Configuration[key] = value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
using Discord;
|
using Discord;
|
||||||
using Discord.WebSocket;
|
using Discord.WebSocket;
|
||||||
using Logging.Net;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Services.DiscordBot.Commands;
|
namespace Moonlight.App.Services.DiscordBot.Commands;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Discord;
|
using Discord;
|
||||||
using Discord.WebSocket;
|
using Discord.WebSocket;
|
||||||
using Logging.Net;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Moonlight.App.Repositories;
|
using Moonlight.App.Repositories;
|
||||||
using Moonlight.App.Repositories.Servers;
|
using Moonlight.App.Repositories.Servers;
|
||||||
@@ -30,7 +29,7 @@ public class ServerListCommand : BaseModule
|
|||||||
{
|
{
|
||||||
embed = dcs.EmbedBuilderModule.StandardEmbed("Sorry ;( \n Please first create and/or link a Account to Discord! \n Press the Button to register/log in.", Color.Red, command.User);
|
embed = dcs.EmbedBuilderModule.StandardEmbed("Sorry ;( \n Please first create and/or link a Account to Discord! \n Press the Button to register/log in.", Color.Red, command.User);
|
||||||
components = new ComponentBuilder();
|
components = new ComponentBuilder();
|
||||||
components.WithButton("Click Here", style: ButtonStyle.Link, url: ConfigService.GetSection("Moonlight").GetValue<String>("AppUrl"));
|
components.WithButton("Click Here", style: ButtonStyle.Link, url: ConfigService.Get().Moonlight.AppUrl);
|
||||||
|
|
||||||
await command.RespondAsync(embed: embed.Build(), components: components.Build(), ephemeral: true);
|
await command.RespondAsync(embed: embed.Build(), components: components.Build(), ephemeral: true);
|
||||||
return;
|
return;
|
||||||
@@ -58,7 +57,7 @@ public class ServerListCommand : BaseModule
|
|||||||
components.WithButton("Panel",
|
components.WithButton("Panel",
|
||||||
emote: Emote.Parse("<a:Earth:1092814004113657927>"),
|
emote: Emote.Parse("<a:Earth:1092814004113657927>"),
|
||||||
style: ButtonStyle.Link,
|
style: ButtonStyle.Link,
|
||||||
url: $"{ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl")}");
|
url: $"{ConfigService.Get().Moonlight.AppUrl}");
|
||||||
|
|
||||||
if (servers.Count > 25)
|
if (servers.Count > 25)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Discord;
|
using Discord;
|
||||||
using Discord.Commands;
|
|
||||||
using Discord.WebSocket;
|
using Discord.WebSocket;
|
||||||
using Logging.Net;
|
using Moonlight.App.Helpers;
|
||||||
using Moonlight.App.Services.DiscordBot.Commands;
|
using Moonlight.App.Services.DiscordBot.Commands;
|
||||||
using Moonlight.App.Services.DiscordBot.Modules;
|
using Moonlight.App.Services.DiscordBot.Modules;
|
||||||
|
|
||||||
@@ -45,10 +44,10 @@ public DiscordBotService(
|
|||||||
ServiceScope = ServiceScopeFactory.CreateScope();
|
ServiceScope = ServiceScopeFactory.CreateScope();
|
||||||
|
|
||||||
var discordConfig = ConfigService
|
var discordConfig = ConfigService
|
||||||
.GetSection("Moonlight")
|
.Get()
|
||||||
.GetSection("DiscordBot");
|
.Moonlight.DiscordBot;
|
||||||
|
|
||||||
if (!discordConfig.GetValue<bool>("Enable"))
|
if (!discordConfig.Enable)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Client.Log += Log;
|
Client.Log += Log;
|
||||||
@@ -68,7 +67,7 @@ public DiscordBotService(
|
|||||||
|
|
||||||
await ActivityStatusModule.UpdateActivityStatusList();
|
await ActivityStatusModule.UpdateActivityStatusList();
|
||||||
|
|
||||||
await Client.LoginAsync(TokenType.Bot, discordConfig.GetValue<string>("Token"));
|
await Client.LoginAsync(TokenType.Bot, discordConfig.Token);
|
||||||
await Client.StartAsync();
|
await Client.StartAsync();
|
||||||
|
|
||||||
await Task.Delay(-1);
|
await Task.Delay(-1);
|
||||||
|
|||||||
@@ -87,8 +87,8 @@ public class EmbedBuilderModule : BaseModule
|
|||||||
int[] randomNumbers = new int[] { 1, 3, 8, 11, 20 };
|
int[] randomNumbers = new int[] { 1, 3, 8, 11, 20 };
|
||||||
|
|
||||||
if (randomNumbers.Contains(random.Next(1, 24)))
|
if (randomNumbers.Contains(random.Next(1, 24)))
|
||||||
return new EmbedAuthorBuilder().WithName(Client.CurrentUser.Username + " - The Rick version").WithUrl(ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl")).WithIconUrl("https://cdn.discordapp.com/attachments/750696464014901268/1092783310129860618/rick.gif");
|
return new EmbedAuthorBuilder().WithName(Client.CurrentUser.Username + " - The Rick version").WithUrl(ConfigService.Get().Moonlight.AppUrl).WithIconUrl("https://cdn.discordapp.com/attachments/750696464014901268/1092783310129860618/rick.gif");
|
||||||
|
|
||||||
return new EmbedAuthorBuilder().WithName(Client.CurrentUser.Username).WithUrl(ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl")).WithIconUrl(Client.CurrentUser.GetAvatarUrl());
|
return new EmbedAuthorBuilder().WithName(Client.CurrentUser.Username).WithUrl(ConfigService.Get().Moonlight.AppUrl).WithIconUrl(Client.CurrentUser.GetAvatarUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Discord.WebSocket;
|
using Discord.WebSocket;
|
||||||
using Logging.Net;
|
using Moonlight.App.Helpers;
|
||||||
|
|
||||||
namespace Moonlight.App.Services.DiscordBot.Modules;
|
namespace Moonlight.App.Services.DiscordBot.Modules;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using Discord;
|
using Discord;
|
||||||
using Discord.WebSocket;
|
using Discord.WebSocket;
|
||||||
using Logging.Net;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Moonlight.App.ApiClients.Wings;
|
using Moonlight.App.ApiClients.Wings;
|
||||||
|
using Moonlight.App.Helpers;
|
||||||
using Moonlight.App.Repositories;
|
using Moonlight.App.Repositories;
|
||||||
using Moonlight.App.Repositories.Servers;
|
using Moonlight.App.Repositories.Servers;
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ public class ServerListComponentHandlerModule : BaseModule
|
|||||||
// stopping
|
// stopping
|
||||||
// offline
|
// offline
|
||||||
// installing
|
// installing
|
||||||
if (!ConfigService.GetSection("Moonlight").GetSection("DiscordBot").GetValue<bool>("PowerActions") && costomId[1] is "Start" or "Restart" or "Stop" or "Kill" or "Update")
|
if (!ConfigService.Get().Moonlight.DiscordBot.PowerActions && costomId[1] is "Start" or "Restart" or "Stop" or "Kill" or "Update")
|
||||||
{
|
{
|
||||||
embed = dcs.EmbedBuilderModule.StandardEmbed($"This feature is disabled for Security reasons! \n If you believe this is a error please contact the Administrators from this panel.", Color.Red, component.User);
|
embed = dcs.EmbedBuilderModule.StandardEmbed($"This feature is disabled for Security reasons! \n If you believe this is a error please contact the Administrators from this panel.", Color.Red, component.User);
|
||||||
await component.RespondAsync(embed: embed.Build(), ephemeral: true);
|
await component.RespondAsync(embed: embed.Build(), ephemeral: true);
|
||||||
@@ -80,7 +80,7 @@ public class ServerListComponentHandlerModule : BaseModule
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ConfigService.GetSection("Moonlight").GetSection("DiscordBot").GetValue<bool>("SendCommands") && costomId[1] is "SendCommand")
|
if (!ConfigService.Get().Moonlight.DiscordBot.SendCommands && costomId[1] is "SendCommand")
|
||||||
{
|
{
|
||||||
embed = dcs.EmbedBuilderModule.StandardEmbed($"This feature is disabled for Security reasons! \n If you believe this is a error please contact the Administrators from this panel.", Color.Red, component.User);
|
embed = dcs.EmbedBuilderModule.StandardEmbed($"This feature is disabled for Security reasons! \n If you believe this is a error please contact the Administrators from this panel.", Color.Red, component.User);
|
||||||
await component.RespondAsync(embed: embed.Build(), ephemeral: true);
|
await component.RespondAsync(embed: embed.Build(), ephemeral: true);
|
||||||
@@ -302,7 +302,7 @@ public class ServerListComponentHandlerModule : BaseModule
|
|||||||
{
|
{
|
||||||
embed = dcs.EmbedBuilderModule.StandardEmbed("Sorry ;( \n Please first create and/or link a Account to Discord! \n Press the Button to register/log in.", Color.Red, component.User);
|
embed = dcs.EmbedBuilderModule.StandardEmbed("Sorry ;( \n Please first create and/or link a Account to Discord! \n Press the Button to register/log in.", Color.Red, component.User);
|
||||||
components = new ComponentBuilder();
|
components = new ComponentBuilder();
|
||||||
components.WithButton("Click Here", style: ButtonStyle.Link, url: ConfigService.GetSection("Moonlight").GetValue<String>("AppUrl"));
|
components.WithButton("Click Here", style: ButtonStyle.Link, url: ConfigService.Get().Moonlight.AppUrl);
|
||||||
|
|
||||||
await component.RespondAsync(embed: embed.Build(), components: components.Build(), ephemeral: true);
|
await component.RespondAsync(embed: embed.Build(), components: components.Build(), ephemeral: true);
|
||||||
return;
|
return;
|
||||||
@@ -332,7 +332,7 @@ public class ServerListComponentHandlerModule : BaseModule
|
|||||||
components.WithButton("Panel",
|
components.WithButton("Panel",
|
||||||
emote: Emote.Parse("<a:Earth:1092814004113657927>"),
|
emote: Emote.Parse("<a:Earth:1092814004113657927>"),
|
||||||
style: ButtonStyle.Link,
|
style: ButtonStyle.Link,
|
||||||
url: $"{ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl")}");
|
url: $"{ConfigService.Get().Moonlight.AppUrl}");
|
||||||
|
|
||||||
components.WithButton("Previous-page",
|
components.WithButton("Previous-page",
|
||||||
emote: Emote.Parse("<:ArrowLeft:1101547474180649030>"),
|
emote: Emote.Parse("<:ArrowLeft:1101547474180649030>"),
|
||||||
@@ -378,7 +378,7 @@ public class ServerListComponentHandlerModule : BaseModule
|
|||||||
|
|
||||||
var components = new ComponentBuilder();
|
var components = new ComponentBuilder();
|
||||||
|
|
||||||
if (ConfigService.GetSection("Moonlight").GetSection("DiscordBot").GetValue<bool>("PowerActions"))
|
if (ConfigService.Get().Moonlight.DiscordBot.PowerActions)
|
||||||
{
|
{
|
||||||
components.WithButton("Start", style: ButtonStyle.Success, customId: $"Sm.Start.{server.Id}", disabled: false);
|
components.WithButton("Start", style: ButtonStyle.Success, customId: $"Sm.Start.{server.Id}", disabled: false);
|
||||||
components.WithButton("Restart", style: ButtonStyle.Primary, customId: $"Sm.Restart.{server.Id}", disabled: false);
|
components.WithButton("Restart", style: ButtonStyle.Primary, customId: $"Sm.Restart.{server.Id}", disabled: false);
|
||||||
@@ -389,14 +389,14 @@ public class ServerListComponentHandlerModule : BaseModule
|
|||||||
components.WithButton("Way2Server",
|
components.WithButton("Way2Server",
|
||||||
emote: Emote.Parse("<a:Earth:1092814004113657927>"),
|
emote: Emote.Parse("<a:Earth:1092814004113657927>"),
|
||||||
style: ButtonStyle.Link,
|
style: ButtonStyle.Link,
|
||||||
url: $"{ConfigService.GetSection("Moonlight").GetValue<string>("AppUrl")}/server/{server.Uuid}");
|
url: $"{ConfigService.Get().Moonlight.AppUrl}/server/{server.Uuid}");
|
||||||
|
|
||||||
components.WithButton("Update",
|
components.WithButton("Update",
|
||||||
emote: Emote.Parse("<:refresh:1101547898803605605>"),
|
emote: Emote.Parse("<:refresh:1101547898803605605>"),
|
||||||
style: ButtonStyle.Secondary,
|
style: ButtonStyle.Secondary,
|
||||||
customId: $"Sm.Update.{server.Id}");
|
customId: $"Sm.Update.{server.Id}");
|
||||||
|
|
||||||
if (ConfigService.GetSection("Moonlight").GetSection("DiscordBot").GetValue<bool>("SendCommands"))
|
if (ConfigService.Get().Moonlight.DiscordBot.SendCommands)
|
||||||
{
|
{
|
||||||
components.WithButton("SendCommand",
|
components.WithButton("SendCommand",
|
||||||
emote: Emote.Parse("<:Console:1101547358157819944>"),
|
emote: Emote.Parse("<:Console:1101547358157819944>"),
|
||||||
|
|||||||
@@ -1,55 +1,58 @@
|
|||||||
using CloudFlare.Client;
|
using CloudFlare.Client;
|
||||||
using CloudFlare.Client.Api.Authentication;
|
using CloudFlare.Client.Api.Authentication;
|
||||||
|
using CloudFlare.Client.Api.Display;
|
||||||
using CloudFlare.Client.Api.Parameters.Data;
|
using CloudFlare.Client.Api.Parameters.Data;
|
||||||
using CloudFlare.Client.Api.Result;
|
using CloudFlare.Client.Api.Result;
|
||||||
using CloudFlare.Client.Api.Zones;
|
using CloudFlare.Client.Api.Zones;
|
||||||
using CloudFlare.Client.Api.Zones.DnsRecord;
|
using CloudFlare.Client.Api.Zones.DnsRecord;
|
||||||
using CloudFlare.Client.Enumerators;
|
using CloudFlare.Client.Enumerators;
|
||||||
using Logging.Net;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Moonlight.App.Database.Entities;
|
using Moonlight.App.Database.Entities;
|
||||||
using Moonlight.App.Exceptions;
|
using Moonlight.App.Exceptions;
|
||||||
|
using Moonlight.App.Helpers;
|
||||||
using Moonlight.App.Models.Misc;
|
using Moonlight.App.Models.Misc;
|
||||||
using Moonlight.App.Repositories.Domains;
|
using Moonlight.App.Repositories.Domains;
|
||||||
using Moonlight.App.Services.LogServices;
|
|
||||||
using DnsRecord = Moonlight.App.Models.Misc.DnsRecord;
|
using DnsRecord = Moonlight.App.Models.Misc.DnsRecord;
|
||||||
|
using MatchType = CloudFlare.Client.Enumerators.MatchType;
|
||||||
|
|
||||||
namespace Moonlight.App.Services;
|
namespace Moonlight.App.Services;
|
||||||
|
|
||||||
public class DomainService
|
public class DomainService
|
||||||
{
|
{
|
||||||
private readonly DomainRepository DomainRepository;
|
private readonly DomainRepository DomainRepository;
|
||||||
|
private readonly ConfigService ConfigService;
|
||||||
private readonly SharedDomainRepository SharedDomainRepository;
|
private readonly SharedDomainRepository SharedDomainRepository;
|
||||||
private readonly CloudFlareClient Client;
|
private readonly CloudFlareClient Client;
|
||||||
private readonly AuditLogService AuditLogService;
|
|
||||||
private readonly string AccountId;
|
private readonly string AccountId;
|
||||||
|
|
||||||
public DomainService(
|
public DomainService(
|
||||||
ConfigService configService,
|
ConfigService configService,
|
||||||
DomainRepository domainRepository,
|
DomainRepository domainRepository,
|
||||||
SharedDomainRepository sharedDomainRepository,
|
SharedDomainRepository sharedDomainRepository)
|
||||||
AuditLogService auditLogService)
|
|
||||||
{
|
{
|
||||||
|
ConfigService = configService;
|
||||||
DomainRepository = domainRepository;
|
DomainRepository = domainRepository;
|
||||||
SharedDomainRepository = sharedDomainRepository;
|
SharedDomainRepository = sharedDomainRepository;
|
||||||
AuditLogService = auditLogService;
|
|
||||||
|
|
||||||
var config = configService
|
var config = configService
|
||||||
.GetSection("Moonlight")
|
.Get()
|
||||||
.GetSection("Domains");
|
.Moonlight.Domains;
|
||||||
|
|
||||||
AccountId = config.GetValue<string>("AccountId");
|
AccountId = config.AccountId;
|
||||||
|
|
||||||
Client = new(
|
Client = new(
|
||||||
new ApiKeyAuthentication(
|
new ApiKeyAuthentication(
|
||||||
config.GetValue<string>("Email"),
|
config.Email,
|
||||||
config.GetValue<string>("Key")
|
config.Key
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<Domain> Create(string domain, SharedDomain sharedDomain, User user)
|
public Task<Domain> Create(string domain, SharedDomain sharedDomain, User user)
|
||||||
{
|
{
|
||||||
|
if (!ConfigService.Get().Moonlight.Domains.Enable)
|
||||||
|
throw new DisplayException("This operation is disabled");
|
||||||
|
|
||||||
if (DomainRepository.Get().Where(x => x.SharedDomain.Id == sharedDomain.Id).Any(x => x.Name == domain))
|
if (DomainRepository.Get().Where(x => x.SharedDomain.Id == sharedDomain.Id).Any(x => x.Name == domain))
|
||||||
throw new DisplayException("A domain with this name does already exist for this shared domain");
|
throw new DisplayException("A domain with this name does already exist for this shared domain");
|
||||||
|
|
||||||
@@ -65,6 +68,9 @@ public class DomainService
|
|||||||
|
|
||||||
public Task Delete(Domain domain)
|
public Task Delete(Domain domain)
|
||||||
{
|
{
|
||||||
|
if (!ConfigService.Get().Moonlight.Domains.Enable)
|
||||||
|
throw new DisplayException("This operation is disabled");
|
||||||
|
|
||||||
DomainRepository.Delete(domain);
|
DomainRepository.Delete(domain);
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
@@ -73,6 +79,9 @@ public class DomainService
|
|||||||
public async Task<Zone[]>
|
public async Task<Zone[]>
|
||||||
GetAvailableDomains() // This method returns all available domains which are not added as a shared domain
|
GetAvailableDomains() // This method returns all available domains which are not added as a shared domain
|
||||||
{
|
{
|
||||||
|
if (!ConfigService.Get().Moonlight.Domains.Enable)
|
||||||
|
return Array.Empty<Zone>();
|
||||||
|
|
||||||
var domains = GetData(
|
var domains = GetData(
|
||||||
await Client.Zones.GetAsync(new()
|
await Client.Zones.GetAsync(new()
|
||||||
{
|
{
|
||||||
@@ -95,11 +104,41 @@ public class DomainService
|
|||||||
|
|
||||||
public async Task<DnsRecord[]> GetDnsRecords(Domain d)
|
public async Task<DnsRecord[]> GetDnsRecords(Domain d)
|
||||||
{
|
{
|
||||||
|
if (!ConfigService.Get().Moonlight.Domains.Enable)
|
||||||
|
return Array.Empty<DnsRecord>();
|
||||||
|
|
||||||
var domain = EnsureData(d);
|
var domain = EnsureData(d);
|
||||||
|
|
||||||
var records = GetData(
|
var records = new List<CloudFlare.Client.Api.Zones.DnsRecord.DnsRecord>();
|
||||||
await Client.Zones.DnsRecords.GetAsync(domain.SharedDomain.CloudflareId)
|
|
||||||
);
|
// Load paginated
|
||||||
|
// TODO: Find an alternative option. This way to load the records is NOT optimal
|
||||||
|
// and can result in long loading time when there are many dns records.
|
||||||
|
// As cloudflare does not offer a way to search dns records which starts
|
||||||
|
// with a specific string we are not able to filter it using the api (client)
|
||||||
|
var initialResponse = await Client.Zones.DnsRecords.GetAsync(domain.SharedDomain.CloudflareId);
|
||||||
|
|
||||||
|
records.AddRange(GetData(initialResponse));
|
||||||
|
|
||||||
|
// Check if there are more pages
|
||||||
|
while (initialResponse.ResultInfo.Page < initialResponse.ResultInfo.TotalPage)
|
||||||
|
{
|
||||||
|
// Get the next page of data
|
||||||
|
var nextPageResponse = await Client.Zones.DnsRecords.GetAsync(
|
||||||
|
domain.SharedDomain.CloudflareId,
|
||||||
|
displayOptions: new()
|
||||||
|
{
|
||||||
|
Page = initialResponse.ResultInfo.Page + 1
|
||||||
|
}
|
||||||
|
);
|
||||||
|
var nextPageRecords = GetData(nextPageResponse);
|
||||||
|
|
||||||
|
// Append the records from the next page to the existing records
|
||||||
|
records.AddRange(nextPageRecords);
|
||||||
|
|
||||||
|
// Update the initial response to the next page response
|
||||||
|
initialResponse = nextPageResponse;
|
||||||
|
}
|
||||||
|
|
||||||
var rname = $"{domain.Name}.{domain.SharedDomain.Name}";
|
var rname = $"{domain.Name}.{domain.SharedDomain.Name}";
|
||||||
var dname = $".{rname}";
|
var dname = $".{rname}";
|
||||||
@@ -121,7 +160,7 @@ public class DomainService
|
|||||||
Type = record.Type
|
Type = record.Type
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (record.Name.EndsWith(rname))
|
else if (record.Name == rname)
|
||||||
{
|
{
|
||||||
result.Add(new()
|
result.Add(new()
|
||||||
{
|
{
|
||||||
@@ -141,65 +180,82 @@ public class DomainService
|
|||||||
|
|
||||||
public async Task AddDnsRecord(Domain d, DnsRecord dnsRecord)
|
public async Task AddDnsRecord(Domain d, DnsRecord dnsRecord)
|
||||||
{
|
{
|
||||||
var domain = EnsureData(d);
|
if (!ConfigService.Get().Moonlight.Domains.Enable)
|
||||||
|
throw new DisplayException("This operation is disabled");
|
||||||
|
|
||||||
var rname = $"{domain.Name}.{domain.SharedDomain.Name}";
|
try
|
||||||
var dname = $".{rname}";
|
|
||||||
|
|
||||||
if (dnsRecord.Type == DnsRecordType.Srv)
|
|
||||||
{
|
{
|
||||||
var parts = dnsRecord.Name.Split(".");
|
var domain = EnsureData(d);
|
||||||
Enum.TryParse(parts[1], out Protocol protocol);
|
|
||||||
|
|
||||||
var valueParts = dnsRecord.Content.Split(" ");
|
var rname = $"{domain.Name}.{domain.SharedDomain.Name}";
|
||||||
|
var dname = $".{rname}";
|
||||||
|
|
||||||
var nameWithoutProt = dnsRecord.Name.Replace($"{parts[0]}.{parts[1]}.", "");
|
if (dnsRecord.Type == DnsRecordType.Srv)
|
||||||
nameWithoutProt = nameWithoutProt.Replace($"{parts[0]}.{parts[1]}", "");
|
|
||||||
var name = nameWithoutProt == "" ? rname : nameWithoutProt + dname;
|
|
||||||
|
|
||||||
var srv = new NewDnsRecord<Srv>()
|
|
||||||
{
|
{
|
||||||
Type = dnsRecord.Type,
|
var parts = dnsRecord.Name.Split(".");
|
||||||
Data = new()
|
|
||||||
|
Protocol protocol = Protocol.Tcp;
|
||||||
|
|
||||||
|
if (parts[1].Contains("udp"))
|
||||||
|
protocol = Protocol.Udp;
|
||||||
|
|
||||||
|
var valueParts = dnsRecord.Content.Split(" ");
|
||||||
|
|
||||||
|
var nameWithoutProt = dnsRecord.Name.Replace($"{parts[0]}.{parts[1]}.", "");
|
||||||
|
nameWithoutProt = nameWithoutProt.Replace($"{parts[0]}.{parts[1]}", "");
|
||||||
|
var name = nameWithoutProt == "" ? rname : nameWithoutProt + dname;
|
||||||
|
|
||||||
|
var srv = new NewDnsRecord<Srv>()
|
||||||
{
|
{
|
||||||
Service = parts[0],
|
Type = dnsRecord.Type,
|
||||||
Protocol = protocol,
|
Data = new()
|
||||||
Name = name,
|
{
|
||||||
Weight = int.Parse(valueParts[0]),
|
Service = parts[0],
|
||||||
Port = int.Parse(valueParts[1]),
|
Protocol = protocol,
|
||||||
Target = valueParts[2],
|
Name = name,
|
||||||
Priority = dnsRecord.Priority
|
Weight = int.Parse(valueParts[0]),
|
||||||
},
|
Port = int.Parse(valueParts[1]),
|
||||||
Proxied = dnsRecord.Proxied,
|
Target = valueParts[2],
|
||||||
Ttl = dnsRecord.Ttl,
|
Priority = dnsRecord.Priority
|
||||||
};
|
},
|
||||||
|
Proxied = dnsRecord.Proxied,
|
||||||
|
Ttl = dnsRecord.Ttl,
|
||||||
|
};
|
||||||
|
|
||||||
GetData(await Client.Zones.DnsRecords.AddAsync(d.SharedDomain.CloudflareId, srv));
|
GetData(await Client.Zones.DnsRecords.AddAsync(d.SharedDomain.CloudflareId, srv));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
var name = string.IsNullOrEmpty(dnsRecord.Name) ? rname : dnsRecord.Name + dname;
|
|
||||||
|
|
||||||
GetData(await Client.Zones.DnsRecords.AddAsync(d.SharedDomain.CloudflareId, new NewDnsRecord()
|
|
||||||
{
|
{
|
||||||
Type = dnsRecord.Type,
|
var name = string.IsNullOrEmpty(dnsRecord.Name) ? rname : dnsRecord.Name + dname;
|
||||||
Priority = dnsRecord.Priority,
|
|
||||||
Content = dnsRecord.Content,
|
GetData(await Client.Zones.DnsRecords.AddAsync(d.SharedDomain.CloudflareId, new NewDnsRecord()
|
||||||
Proxied = dnsRecord.Proxied,
|
{
|
||||||
Ttl = dnsRecord.Ttl,
|
Type = dnsRecord.Type,
|
||||||
Name = name
|
Priority = dnsRecord.Priority,
|
||||||
}));
|
Content = dnsRecord.Content,
|
||||||
|
Proxied = dnsRecord.Proxied,
|
||||||
|
Ttl = dnsRecord.Ttl,
|
||||||
|
Name = name
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OverflowException)
|
||||||
|
{
|
||||||
|
throw new DisplayException("Invalid dns record values");
|
||||||
|
}
|
||||||
|
catch (FormatException)
|
||||||
|
{
|
||||||
|
throw new DisplayException("Invalid dns record values");
|
||||||
}
|
}
|
||||||
|
|
||||||
await AuditLogService.Log(AuditLogType.AddDomainRecord, x =>
|
//TODO: AuditLog
|
||||||
{
|
|
||||||
x.Add<Domain>(d.Id);
|
|
||||||
x.Add<DnsRecord>(dnsRecord.Name);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateDnsRecord(Domain d, DnsRecord dnsRecord)
|
public async Task UpdateDnsRecord(Domain d, DnsRecord dnsRecord)
|
||||||
{
|
{
|
||||||
|
if (!ConfigService.Get().Moonlight.Domains.Enable)
|
||||||
|
throw new DisplayException("This operation is disabled");
|
||||||
|
|
||||||
var domain = EnsureData(d);
|
var domain = EnsureData(d);
|
||||||
|
|
||||||
var rname = $"{domain.Name}.{domain.SharedDomain.Name}";
|
var rname = $"{domain.Name}.{domain.SharedDomain.Name}";
|
||||||
@@ -225,26 +281,21 @@ public class DomainService
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
await AuditLogService.Log(AuditLogType.UpdateDomainRecord, x =>
|
//TODO: AuditLog
|
||||||
{
|
|
||||||
x.Add<Domain>(d.Id);
|
|
||||||
x.Add<DnsRecord>(dnsRecord.Name);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteDnsRecord(Domain d, DnsRecord dnsRecord)
|
public async Task DeleteDnsRecord(Domain d, DnsRecord dnsRecord)
|
||||||
{
|
{
|
||||||
|
if (!ConfigService.Get().Moonlight.Domains.Enable)
|
||||||
|
throw new DisplayException("This operation is disabled");
|
||||||
|
|
||||||
var domain = EnsureData(d);
|
var domain = EnsureData(d);
|
||||||
|
|
||||||
GetData(
|
GetData(
|
||||||
await Client.Zones.DnsRecords.DeleteAsync(domain.SharedDomain.CloudflareId, dnsRecord.Id)
|
await Client.Zones.DnsRecords.DeleteAsync(domain.SharedDomain.CloudflareId, dnsRecord.Id)
|
||||||
);
|
);
|
||||||
|
|
||||||
await AuditLogService.Log(AuditLogType.DeleteDomainRecord, x =>
|
//TODO: AuditLog
|
||||||
{
|
|
||||||
x.Add<Domain>(d.Id);
|
|
||||||
x.Add<DnsRecord>(dnsRecord.Name);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Domain EnsureData(Domain domain)
|
private Domain EnsureData(Domain domain)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ public class ResourceService
|
|||||||
|
|
||||||
public ResourceService(ConfigService configService)
|
public ResourceService(ConfigService configService)
|
||||||
{
|
{
|
||||||
AppUrl = configService.GetSection("Moonlight").GetValue<string>("AppUrl");
|
AppUrl = configService.Get().Moonlight.AppUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Image(string name)
|
public string Image(string name)
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Logging.Net;
|
using Moonlight.App.Helpers;
|
||||||
using Moonlight.App.Helpers;
|
|
||||||
|
|
||||||
namespace Moonlight.App.Services.Files;
|
namespace Moonlight.App.Services.Files;
|
||||||
|
|
||||||
@@ -16,6 +15,7 @@ public class StorageService
|
|||||||
Directory.CreateDirectory(PathBuilder.Dir("storage", "configs"));
|
Directory.CreateDirectory(PathBuilder.Dir("storage", "configs"));
|
||||||
Directory.CreateDirectory(PathBuilder.Dir("storage", "resources"));
|
Directory.CreateDirectory(PathBuilder.Dir("storage", "resources"));
|
||||||
Directory.CreateDirectory(PathBuilder.Dir("storage", "backups"));
|
Directory.CreateDirectory(PathBuilder.Dir("storage", "backups"));
|
||||||
|
Directory.CreateDirectory(PathBuilder.Dir("storage", "logs"));
|
||||||
|
|
||||||
if(IsEmpty(PathBuilder.Dir("storage", "resources")))
|
if(IsEmpty(PathBuilder.Dir("storage", "resources")))
|
||||||
{
|
{
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user