Refactored whole solution to use the MoonCore and MoonCoreUI library
This commit is contained in:
parent
2729564495
commit
64bcfe74e7
137 changed files with 402 additions and 3139 deletions
|
@ -1,6 +1,5 @@
|
|||
using Moonlight.Core.Actions.Dummy.Layouts;
|
||||
using Moonlight.Core.Actions.Dummy.Pages;
|
||||
using Moonlight.Core.Helpers;
|
||||
using Moonlight.Features.ServiceManagement.Models.Abstractions;
|
||||
|
||||
namespace Moonlight.Core.Actions.Dummy;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using System.ComponentModel;
|
||||
using Moonlight.Core.Helpers;
|
||||
using MoonCore.Helpers;
|
||||
using Moonlight.Features.Advertisement.Configuration;
|
||||
using Moonlight.Features.StoreSystem.Configuration;
|
||||
using Moonlight.Features.Theming.Configuration;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using MoonCore.Services;
|
||||
using Moonlight.Core.Configuration;
|
||||
using Moonlight.Core.Database.Entities;
|
||||
using Moonlight.Core.Services;
|
||||
using Moonlight.Features.Community.Entities;
|
||||
|
@ -12,7 +14,7 @@ namespace Moonlight.Core.Database;
|
|||
|
||||
public class DataContext : DbContext
|
||||
{
|
||||
private readonly ConfigService ConfigService;
|
||||
private readonly ConfigService<ConfigV1> ConfigService;
|
||||
|
||||
public DbSet<User> Users { get; set; }
|
||||
|
||||
|
@ -50,7 +52,7 @@ public class DataContext : DbContext
|
|||
public DbSet<ServerDockerImage> ServerDockerImages { get; set; }
|
||||
public DbSet<ServerImageVariable> ServerImageVariables { get; set; }
|
||||
|
||||
public DataContext(ConfigService configService)
|
||||
public DataContext(ConfigService<ConfigV1> configService)
|
||||
{
|
||||
ConfigService = configService;
|
||||
}
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
namespace Moonlight.Core.Exceptions;
|
||||
|
||||
public class DisplayException : Exception
|
||||
{
|
||||
public DisplayException()
|
||||
{
|
||||
}
|
||||
|
||||
public DisplayException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public DisplayException(string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
namespace Moonlight.Core.Extensions.Attributes;
|
||||
|
||||
public class SelectorAttribute : Attribute
|
||||
{
|
||||
public string SelectorProp { get; set; } = "";
|
||||
public string DisplayProp { get; set; } = "";
|
||||
public bool UseDropdown { get; set; } = false;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
using System.Text;
|
||||
|
||||
namespace Moonlight.Core.Extensions;
|
||||
|
||||
public static class ConfigurationBuilderExtensions
|
||||
{
|
||||
public static IConfigurationBuilder AddJsonString(this IConfigurationBuilder configurationBuilder, string json)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(json);
|
||||
var stream = new MemoryStream(bytes);
|
||||
return configurationBuilder.AddJsonStream(stream);
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Moonlight.Core.Helpers;
|
||||
|
||||
public static class ComponentHelper
|
||||
{
|
||||
public static RenderFragment FromType(Type type, Action<Dictionary<string, object>>? buildAttributes = null) => builder =>
|
||||
{
|
||||
builder.OpenComponent(0, type);
|
||||
|
||||
if (buildAttributes != null)
|
||||
{
|
||||
Dictionary<string, object> parameters = new();
|
||||
buildAttributes.Invoke(parameters);
|
||||
builder.AddMultipleAttributes(1, parameters);
|
||||
}
|
||||
|
||||
builder.CloseComponent();
|
||||
};
|
||||
|
||||
public static RenderFragment FromType<T>(Action<Dictionary<string, object>>? buildAttributes = null) where T : ComponentBase =>
|
||||
FromType(typeof(T), buildAttributes);
|
||||
}
|
|
@ -1,280 +0,0 @@
|
|||
using System.Text;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Moonlight.Core.Helpers;
|
||||
|
||||
public static class Formatter
|
||||
{
|
||||
public static string GenerateString(int length)
|
||||
{
|
||||
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
var stringBuilder = new StringBuilder();
|
||||
var random = new Random();
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
stringBuilder.Append(chars[random.Next(chars.Length)]);
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
|
||||
public static string IntToStringWithLeadingZeros(int number, int n)
|
||||
{
|
||||
string result = number.ToString();
|
||||
int length = result.Length;
|
||||
|
||||
for (int i = length; i < n; i++)
|
||||
{
|
||||
result = "0" + result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string CapitalizeFirstCharacter(string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
char firstChar = char.ToUpper(input[0]);
|
||||
string restOfString = input.Substring(1);
|
||||
|
||||
return firstChar + restOfString;
|
||||
}
|
||||
|
||||
public static string CutInHalf(string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
return input;
|
||||
|
||||
int length = input.Length;
|
||||
int halfLength = length / 2;
|
||||
|
||||
return input.Substring(0, halfLength);
|
||||
}
|
||||
|
||||
public static bool EndsInOneOf(string suffix, IEnumerable<string> strings)
|
||||
{
|
||||
foreach (string str in strings)
|
||||
{
|
||||
if (suffix.EndsWith(str))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool ContainsOneOf(string textToSearch, IEnumerable<string> strings, out string foundText)
|
||||
{
|
||||
foreach (string str in strings)
|
||||
{
|
||||
if (textToSearch.Contains(str))
|
||||
{
|
||||
foundText = str;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
foundText = "";
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool ContainsOneOf(string textToSearch, IEnumerable<string> strings)
|
||||
{
|
||||
return ContainsOneOf(textToSearch, strings, out _);
|
||||
}
|
||||
|
||||
public static string FormatSize(long bytes)
|
||||
{
|
||||
var i = Math.Abs(bytes) / 1024D;
|
||||
if (i < 1)
|
||||
{
|
||||
return bytes + " B";
|
||||
}
|
||||
else if (i / 1024D < 1)
|
||||
{
|
||||
return i.Round(2) + " KB";
|
||||
}
|
||||
else if (i / (1024D * 1024D) < 1)
|
||||
{
|
||||
return (i / 1024D).Round(2) + " MB";
|
||||
}
|
||||
else
|
||||
{
|
||||
return (i / (1024D * 1024D)).Round(2) + " GB";
|
||||
}
|
||||
}
|
||||
|
||||
private static double Round(this double d, int decimals)
|
||||
{
|
||||
return Math.Round(d, decimals);
|
||||
}
|
||||
|
||||
public static string ReplaceEnd(string input, string substringToReplace, string newSubstring)
|
||||
{
|
||||
int lastIndexOfSubstring = input.LastIndexOf(substringToReplace);
|
||||
if (lastIndexOfSubstring >= 0)
|
||||
{
|
||||
input = input.Remove(lastIndexOfSubstring, substringToReplace.Length)
|
||||
.Insert(lastIndexOfSubstring, newSubstring);
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
public static string ConvertCamelCaseToSpaces(string input)
|
||||
{
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
foreach (char c in input)
|
||||
{
|
||||
if (char.IsUpper(c))
|
||||
{
|
||||
output.Append(' ');
|
||||
}
|
||||
|
||||
output.Append(c);
|
||||
}
|
||||
|
||||
return output.ToString().Trim();
|
||||
}
|
||||
|
||||
public static string FormatUptime(double uptime)
|
||||
{
|
||||
TimeSpan t = TimeSpan.FromMilliseconds(uptime);
|
||||
|
||||
return FormatUptime(t);
|
||||
}
|
||||
|
||||
public static string FormatUptime(TimeSpan t)
|
||||
{
|
||||
if (t.Days > 0)
|
||||
{
|
||||
return $"{t.Days}d {t.Hours}h {t.Minutes}m {t.Seconds}s";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"{t.Hours}h {t.Minutes}m {t.Seconds}s";
|
||||
}
|
||||
}
|
||||
|
||||
public static string FormatDate(DateTime e)
|
||||
{
|
||||
string i2s(int i)
|
||||
{
|
||||
if (i.ToString().Length < 2)
|
||||
return "0" + i;
|
||||
return i.ToString();
|
||||
}
|
||||
|
||||
return $"{i2s(e.Day)}.{i2s(e.Month)}.{e.Year} {i2s(e.Hour)}:{i2s(e.Minute)}";
|
||||
}
|
||||
|
||||
public static string FormatDateOnly(DateTime e)
|
||||
{
|
||||
string i2s(int i)
|
||||
{
|
||||
if (i.ToString().Length < 2)
|
||||
return "0" + i;
|
||||
return i.ToString();
|
||||
}
|
||||
|
||||
return $"{i2s(e.Day)}.{i2s(e.Month)}.{e.Year}";
|
||||
}
|
||||
|
||||
public static string FormatSize(double bytes)
|
||||
{
|
||||
var i = Math.Abs(bytes) / 1024D;
|
||||
if (i < 1)
|
||||
{
|
||||
return bytes + " B";
|
||||
}
|
||||
else if (i / 1024D < 1)
|
||||
{
|
||||
return i.Round(2) + " KB";
|
||||
}
|
||||
else if (i / (1024D * 1024D) < 1)
|
||||
{
|
||||
return (i / 1024D).Round(2) + " MB";
|
||||
}
|
||||
else
|
||||
{
|
||||
return (i / (1024D * 1024D)).Round(2) + " GB";
|
||||
}
|
||||
}
|
||||
|
||||
public static RenderFragment FormatLineBreaks(string content)
|
||||
{
|
||||
return builder =>
|
||||
{
|
||||
int i = 0;
|
||||
var arr = content.Split("\n");
|
||||
|
||||
foreach (var line in arr)
|
||||
{
|
||||
builder.AddContent(i, line);
|
||||
if (i++ != arr.Length - 1)
|
||||
{
|
||||
builder.AddMarkupContent(i, "<br/>");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static string FormatAgoFromDateTime(DateTime dt)
|
||||
{
|
||||
TimeSpan timeSince = DateTime.UtcNow.Subtract(dt);
|
||||
|
||||
if (timeSince.TotalMilliseconds < 1)
|
||||
return "just now";
|
||||
|
||||
if (timeSince.TotalMinutes < 1)
|
||||
return "less than a minute ago";
|
||||
|
||||
if (timeSince.TotalMinutes < 2)
|
||||
return "1 minute ago";
|
||||
|
||||
if (timeSince.TotalMinutes < 60)
|
||||
return Math.Round(timeSince.TotalMinutes) + " minutes ago";
|
||||
|
||||
if (timeSince.TotalHours < 2)
|
||||
return "1 hour ago";
|
||||
|
||||
if (timeSince.TotalHours < 24)
|
||||
return Math.Round(timeSince.TotalHours) + " hours ago";
|
||||
|
||||
if (timeSince.TotalDays < 2)
|
||||
return "1 day ago";
|
||||
|
||||
return Math.Round(timeSince.TotalDays) + " days ago";
|
||||
}
|
||||
|
||||
// This will replace every placeholder with the respective value if specified in the model
|
||||
// For example:
|
||||
// A instance of the user model has been passed in the 'models' parameter of the function.
|
||||
// So the placeholder {{User.Email}} will be replaced by the value of the Email property of the model
|
||||
public static string ProcessTemplating(string text, params object[] models)
|
||||
{
|
||||
foreach (var model in models)
|
||||
{
|
||||
foreach (var property in model.GetType().GetProperties())
|
||||
{
|
||||
var value = property.GetValue(model);
|
||||
|
||||
if(value == null)
|
||||
continue;
|
||||
|
||||
var placeholder = "{{" + $"{model.GetType().Name}.{property.Name}" + "}}";
|
||||
|
||||
text = text.Replace(placeholder, value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
}
|
|
@ -1,172 +0,0 @@
|
|||
using System.Security.Cryptography;
|
||||
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
|
||||
|
||||
namespace Moonlight.Core.Helpers;
|
||||
|
||||
// Src: https://codereview.stackexchange.com/questions/176697/net-core-mvc-future-proof-hashing-of-passwords
|
||||
public static class HashHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// The default number of Iterations
|
||||
/// </summary>
|
||||
private const int DefaultIterations = 10000;
|
||||
|
||||
/// <summary>
|
||||
/// Provides Information about a specific Hash Version
|
||||
/// </summary>
|
||||
private class HashVersion
|
||||
{
|
||||
public short Version { get; set; }
|
||||
public int SaltSize { get; set; }
|
||||
public int HashSize { get; set; }
|
||||
public KeyDerivationPrf KeyDerivation { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Holds all possible Hash Versions
|
||||
/// </summary>
|
||||
private static readonly Dictionary<short, HashVersion> _versions = new Dictionary<short, HashVersion>
|
||||
{
|
||||
{
|
||||
1, new HashVersion
|
||||
{
|
||||
Version = 1,
|
||||
KeyDerivation = KeyDerivationPrf.HMACSHA512,
|
||||
HashSize = 256 / 8,
|
||||
SaltSize = 128 / 8
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The default Hash Version, which should be used, if a new Hash is Created
|
||||
/// </summary>
|
||||
private static HashVersion DefaultVersion => _versions[1];
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a given hash uses the latest version
|
||||
/// </summary>
|
||||
/// <param name="data">The hash</param>
|
||||
/// <returns>Is the hash of the latest version?</returns>
|
||||
public static bool IsLatestHashVersion(byte[] data)
|
||||
{
|
||||
var version = BitConverter.ToInt16(data, 0);
|
||||
return version == DefaultVersion.Version;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a given hash uses the latest version
|
||||
/// </summary>
|
||||
/// <param name="data">The hash</param>
|
||||
/// <returns>Is the hash of the latest version?</returns>
|
||||
public static bool IsLatestHashVersion(string data)
|
||||
{
|
||||
var dataBytes = Convert.FromBase64String(data);
|
||||
return IsLatestHashVersion(dataBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a random byte array
|
||||
/// </summary>
|
||||
/// <param name="length">The length of the byte array</param>
|
||||
/// <returns>The random byte array</returns>
|
||||
public static byte[] GetRandomBytes(int length)
|
||||
{
|
||||
var data = new byte[length];
|
||||
using (var randomNumberGenerator = RandomNumberGenerator.Create())
|
||||
{
|
||||
randomNumberGenerator.GetBytes(data);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Hash of a clear text
|
||||
/// </summary>
|
||||
/// <param name="clearText">the clear text</param>
|
||||
/// <param name="iterations">the number of iteration the hash alogrythm should run</param>
|
||||
/// <returns>the Hash</returns>
|
||||
public static byte[] Hash(string clearText, int iterations = DefaultIterations)
|
||||
{
|
||||
//get current version
|
||||
var currentVersion = DefaultVersion;
|
||||
|
||||
//get the byte arrays of the hash and meta information
|
||||
var saltBytes = GetRandomBytes(currentVersion.SaltSize);
|
||||
var versionBytes = BitConverter.GetBytes(currentVersion.Version);
|
||||
var iterationBytes = BitConverter.GetBytes(iterations);
|
||||
var hashBytes = KeyDerivation.Pbkdf2(clearText, saltBytes, currentVersion.KeyDerivation, iterations,
|
||||
currentVersion.HashSize);
|
||||
|
||||
//calculate the indexes for the combined hash
|
||||
var indexVersion = 0;
|
||||
var indexIteration = indexVersion + 2;
|
||||
var indexSalt = indexIteration + 4;
|
||||
var indexHash = indexSalt + currentVersion.SaltSize;
|
||||
|
||||
//combine all data to one result hash
|
||||
var resultBytes = new byte[2 + 4 + currentVersion.SaltSize + currentVersion.HashSize];
|
||||
Array.Copy(versionBytes, 0, resultBytes, indexVersion, 2);
|
||||
Array.Copy(iterationBytes, 0, resultBytes, indexIteration, 4);
|
||||
Array.Copy(saltBytes, 0, resultBytes, indexSalt, currentVersion.SaltSize);
|
||||
Array.Copy(hashBytes, 0, resultBytes, indexHash, currentVersion.HashSize);
|
||||
return resultBytes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Hash of a clear text and convert it to a Base64 String representation
|
||||
/// </summary>
|
||||
/// <param name="clearText">the clear text</param>
|
||||
/// <param name="iterations">the number of iteration the hash alogrythm should run</param>
|
||||
/// <returns>the Hash</returns>
|
||||
public static string HashToString(string clearText, int iterations = DefaultIterations)
|
||||
{
|
||||
var data = Hash(clearText, iterations);
|
||||
return Convert.ToBase64String(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies a given clear Text against a hash
|
||||
/// </summary>
|
||||
/// <param name="clearText">The clear text</param>
|
||||
/// <param name="data">The hash</param>
|
||||
/// <returns>Is the hash equal to the clear text?</returns>
|
||||
public static bool Verify(string clearText, byte[] data)
|
||||
{
|
||||
//Get the current version and number of iterations
|
||||
var currentVersion = _versions[BitConverter.ToInt16(data, 0)];
|
||||
var iteration = BitConverter.ToInt32(data, 2);
|
||||
|
||||
//Create the byte arrays for the salt and hash
|
||||
var saltBytes = new byte[currentVersion.SaltSize];
|
||||
var hashBytes = new byte[currentVersion.HashSize];
|
||||
|
||||
//Calculate the indexes of the salt and the hash
|
||||
var indexSalt = 2 + 4; // Int16 (Version) and Int32 (Iteration)
|
||||
var indexHash = indexSalt + currentVersion.SaltSize;
|
||||
|
||||
//Fill the byte arrays with salt and hash
|
||||
Array.Copy(data, indexSalt, saltBytes, 0, currentVersion.SaltSize);
|
||||
Array.Copy(data, indexHash, hashBytes, 0, currentVersion.HashSize);
|
||||
|
||||
//Hash the current clearText with the parameters given via the data
|
||||
var verificationHashBytes = KeyDerivation.Pbkdf2(clearText, saltBytes, currentVersion.KeyDerivation, iteration,
|
||||
currentVersion.HashSize);
|
||||
|
||||
//Check if generated hashes are equal
|
||||
return hashBytes.SequenceEqual(verificationHashBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies a given clear Text against a hash
|
||||
/// </summary>
|
||||
/// <param name="clearText">The clear text</param>
|
||||
/// <param name="data">The hash</param>
|
||||
/// <returns>Is the hash equal to the clear text?</returns>
|
||||
public static bool Verify(string clearText, string data)
|
||||
{
|
||||
var dataBytes = Convert.FromBase64String(data);
|
||||
return Verify(clearText, dataBytes);
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Moonlight.Core.Helpers;
|
||||
|
||||
public class HttpApiClient<TException> : IDisposable where TException : Exception
|
||||
{
|
||||
private readonly HttpClient Client;
|
||||
private readonly string BaseUrl;
|
||||
|
||||
public HttpApiClient(string baseUrl, string token)
|
||||
{
|
||||
Client = new();
|
||||
Client.DefaultRequestHeaders.Add("Authorization", token);
|
||||
|
||||
BaseUrl = baseUrl.EndsWith("/") ? baseUrl : baseUrl + "/";
|
||||
}
|
||||
|
||||
public async Task<string> Send(HttpMethod method, string path, string? body = null,
|
||||
string contentType = "text/plain")
|
||||
{
|
||||
var request = new HttpRequestMessage();
|
||||
|
||||
request.RequestUri = new Uri(BaseUrl + path);
|
||||
request.Method = method;
|
||||
|
||||
if (body != null)
|
||||
request.Content = new StringContent(body, Encoding.UTF8, new MediaTypeHeaderValue(contentType));
|
||||
|
||||
var response = await Client.SendAsync(request);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
await HandleRequestError(response, path);
|
||||
return "";
|
||||
}
|
||||
|
||||
return await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
|
||||
private async Task HandleRequestError(HttpResponseMessage response, string path)
|
||||
{
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
var message = $"[{path}] ({response.StatusCode}): {content}";
|
||||
var exception = Activator.CreateInstance(typeof(TException), message) as Exception;
|
||||
|
||||
throw exception!;
|
||||
}
|
||||
|
||||
#region GET
|
||||
|
||||
public async Task<string> GetAsString(string path) =>
|
||||
await Send(HttpMethod.Get, path);
|
||||
|
||||
public async Task<T> Get<T>(string path) =>
|
||||
JsonConvert.DeserializeObject<T>(await Send(HttpMethod.Get, path))!;
|
||||
|
||||
#endregion
|
||||
|
||||
#region POST
|
||||
|
||||
public async Task<string> PostAsString(string path, string body, string contentType = "text/plain") =>
|
||||
await Send(HttpMethod.Post, path, body, contentType);
|
||||
|
||||
public async Task<T> Post<T>(string path, object body) =>
|
||||
JsonConvert.DeserializeObject<T>(await Send(HttpMethod.Post, path, JsonConvert.SerializeObject(body),
|
||||
"application/json"))!;
|
||||
|
||||
public async Task Post(string path, object? body = null) => await Send(HttpMethod.Post, path,
|
||||
body == null ? "" : JsonConvert.SerializeObject(body));
|
||||
|
||||
#endregion
|
||||
|
||||
#region PUT
|
||||
|
||||
public async Task<string> PutAsString(string path, string body, string contentType = "text/plain") =>
|
||||
await Send(HttpMethod.Put, path, body, contentType);
|
||||
|
||||
public async Task<T> Put<T>(string path, object body) =>
|
||||
JsonConvert.DeserializeObject<T>(await Send(HttpMethod.Put, path, JsonConvert.SerializeObject(body),
|
||||
"application/json"))!;
|
||||
|
||||
public async Task Put(string path, object? body = null) => await Send(HttpMethod.Put, path,
|
||||
body == null ? "" : JsonConvert.SerializeObject(body));
|
||||
|
||||
#endregion
|
||||
|
||||
#region PATCH
|
||||
|
||||
public async Task<string> PatchAsString(string path, string body, string contentType = "text/plain") =>
|
||||
await Send(HttpMethod.Patch, path, body, contentType);
|
||||
|
||||
public async Task<T> Patch<T>(string path, object body) =>
|
||||
JsonConvert.DeserializeObject<T>(await Send(HttpMethod.Patch, path, JsonConvert.SerializeObject(body),
|
||||
"application/json"))!;
|
||||
|
||||
public async Task Patch(string path, object? body = null) => await Send(HttpMethod.Patch, path,
|
||||
body == null ? "" : JsonConvert.SerializeObject(body));
|
||||
|
||||
#endregion
|
||||
|
||||
#region DELETE
|
||||
|
||||
public async Task<string> DeleteAsString(string path) =>
|
||||
await Send(HttpMethod.Delete, path);
|
||||
|
||||
public async Task<T> Delete<T>(string path) =>
|
||||
JsonConvert.DeserializeObject<T>(await Send(HttpMethod.Delete, path))!;
|
||||
|
||||
#endregion
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Client.Dispose();
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
namespace Moonlight.Core.Helpers.LogMigrator;
|
||||
|
||||
public class LogMigrateProvider : ILoggerProvider
|
||||
{
|
||||
public void Dispose() {}
|
||||
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
{
|
||||
return new MigrateLogger();
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
namespace Moonlight.Core.Helpers.LogMigrator;
|
||||
|
||||
public class MigrateLogger : ILogger
|
||||
{
|
||||
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
|
||||
{
|
||||
switch (logLevel)
|
||||
{
|
||||
case LogLevel.Critical:
|
||||
Logger.Fatal(formatter(state, exception));
|
||||
|
||||
if(exception != null)
|
||||
Logger.Fatal(exception);
|
||||
|
||||
break;
|
||||
case LogLevel.Warning:
|
||||
Logger.Warn(formatter(state, exception));
|
||||
|
||||
if(exception != null)
|
||||
Logger.Warn(exception);
|
||||
|
||||
break;
|
||||
case LogLevel.Debug:
|
||||
Logger.Debug(formatter(state, exception));
|
||||
|
||||
if(exception != null)
|
||||
Logger.Debug(exception);
|
||||
|
||||
break;
|
||||
case LogLevel.Error:
|
||||
Logger.Error(formatter(state, exception));
|
||||
|
||||
if(exception != null)
|
||||
Logger.Error(exception);
|
||||
|
||||
break;
|
||||
case LogLevel.Information:
|
||||
Logger.Info(formatter(state, exception));
|
||||
|
||||
if(exception != null)
|
||||
Logger.Info(exception);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using Serilog;
|
||||
|
||||
namespace Moonlight.Core.Helpers;
|
||||
|
||||
public class Logger
|
||||
{
|
||||
#region String logger
|
||||
public static void Verbose(string message, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Verbose("{Message}", message);
|
||||
}
|
||||
|
||||
public static void Info(string message, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Information("{Message}", message);
|
||||
}
|
||||
|
||||
public static void Debug(string message, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Debug("{Message}", message);
|
||||
}
|
||||
|
||||
public static void Error(string message, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Error("{Message}", message);
|
||||
}
|
||||
|
||||
public static void Warn(string message, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Warning("{Message}", message);
|
||||
}
|
||||
|
||||
public static void Fatal(string message, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Fatal("{Message}", message);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Exception method calls
|
||||
|
||||
public static void Verbose(Exception exception, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Verbose(exception, "");
|
||||
}
|
||||
|
||||
public static void Info(Exception exception, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Information(exception, "");
|
||||
}
|
||||
|
||||
public static void Debug(Exception exception, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Debug(exception, "");
|
||||
}
|
||||
|
||||
public static void Error(Exception exception, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Error(exception, "");
|
||||
}
|
||||
|
||||
public static void Warn(Exception exception, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Warning(exception, "");
|
||||
}
|
||||
|
||||
public static void Fatal(Exception exception, string channel = "default")
|
||||
{
|
||||
Log.ForContext("SourceContext", GetNameOfCallingClass())
|
||||
.Fatal(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;
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
namespace Moonlight.Core.Helpers;
|
||||
|
||||
public static class PathBuilder
|
||||
{
|
||||
public static string Dir(params string[] parts)
|
||||
{
|
||||
var res = "";
|
||||
|
||||
foreach (var part in parts)
|
||||
{
|
||||
res += part + Path.DirectorySeparatorChar;
|
||||
}
|
||||
|
||||
return res.Replace(
|
||||
$"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}",
|
||||
$"{Path.DirectorySeparatorChar}"
|
||||
);
|
||||
}
|
||||
|
||||
public static string File(params string[] parts)
|
||||
{
|
||||
var res = "";
|
||||
|
||||
foreach (var part in parts)
|
||||
{
|
||||
res += part + (part == parts.Last() ? "" : Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
return res.Replace(
|
||||
$"{Path.DirectorySeparatorChar}{Path.DirectorySeparatorChar}",
|
||||
$"{Path.DirectorySeparatorChar}"
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
using System.Reflection;
|
||||
|
||||
namespace Moonlight.Core.Helpers;
|
||||
|
||||
public class PropBinder<T>
|
||||
{
|
||||
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 T Class
|
||||
{
|
||||
get => (T)PropertyInfo.GetValue(DataObject)!;
|
||||
set => PropertyInfo.SetValue(DataObject, value);
|
||||
}
|
||||
|
||||
public double DoubleValue
|
||||
{
|
||||
get => (double)PropertyInfo.GetValue(DataObject)!;
|
||||
set => PropertyInfo.SetValue(DataObject, value);
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Moonlight.Core.Exceptions;
|
||||
|
||||
namespace Moonlight.Core.Helpers;
|
||||
|
||||
public class ValidatorHelper
|
||||
{
|
||||
public static Task Validate(object objectToValidate)
|
||||
{
|
||||
var context = new ValidationContext(objectToValidate, null, null);
|
||||
var results = new List<ValidationResult>();
|
||||
|
||||
var isValid = Validator.TryValidateObject(objectToValidate, context, results, true);
|
||||
|
||||
if (!isValid)
|
||||
{
|
||||
var errorMsg = "Unknown form error";
|
||||
|
||||
if (results.Any())
|
||||
errorMsg = results.First().ErrorMessage ?? errorMsg;
|
||||
|
||||
throw new DisplayException(errorMsg);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public static async Task ValidateRange(IEnumerable<object> objectToValidate)
|
||||
{
|
||||
foreach (var o in objectToValidate)
|
||||
await Validate(o);
|
||||
}
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Moonlight.Core.Helpers;
|
||||
|
||||
public class WsPacketConnection
|
||||
{
|
||||
private readonly Dictionary<string, Type> Packets = new();
|
||||
private readonly WebSocket WebSocket;
|
||||
|
||||
public WsPacketConnection(WebSocket webSocket)
|
||||
{
|
||||
WebSocket = webSocket;
|
||||
}
|
||||
|
||||
public Task RegisterPacket<T>(string id)
|
||||
{
|
||||
lock (Packets)
|
||||
Packets.Add(id, typeof(T));
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task Send(object packet)
|
||||
{
|
||||
string? packetId = null;
|
||||
|
||||
// Search packet registration
|
||||
lock (Packets)
|
||||
{
|
||||
if (Packets.Any(x => x.Value == packet.GetType()))
|
||||
packetId = Packets.First(x => x.Value == packet.GetType()).Key;
|
||||
|
||||
if (packetId == null)
|
||||
throw new ArgumentException($"A packet with the type {packet.GetType().FullName} is not registered");
|
||||
}
|
||||
|
||||
// Build raw packet
|
||||
var rawPacket = new RawPacket()
|
||||
{
|
||||
Id = packetId,
|
||||
Data = packet
|
||||
};
|
||||
|
||||
// Serialize, encode and build buffer
|
||||
var json = JsonConvert.SerializeObject(rawPacket);
|
||||
var buffer = Encoding.UTF8.GetBytes(json);
|
||||
|
||||
await WebSocket.SendAsync(buffer, WebSocketMessageType.Text, WebSocketMessageFlags.None,
|
||||
CancellationToken.None);
|
||||
}
|
||||
|
||||
public async Task<object?> Receive()
|
||||
{
|
||||
// Build buffer and read
|
||||
var buffer = new byte[1024];
|
||||
await WebSocket.ReceiveAsync(buffer, CancellationToken.None);
|
||||
|
||||
// Decode and deserialize
|
||||
var json = Encoding.UTF8.GetString(buffer);
|
||||
var rawPacket = JsonConvert.DeserializeObject<RawPacket>(json)!;
|
||||
|
||||
object? packetType = null;
|
||||
|
||||
// Search packet registration
|
||||
lock (Packets)
|
||||
{
|
||||
if (Packets.ContainsKey(rawPacket.Id))
|
||||
packetType = Packets[rawPacket.Id];
|
||||
|
||||
if (packetType == null)
|
||||
throw new ArgumentException($"A packet with the type {rawPacket.Id} is not registered");
|
||||
}
|
||||
|
||||
var typedPacketType = typeof(RawPacket<>).MakeGenericType((packetType as Type)!);
|
||||
var typedPacket = JsonConvert.DeserializeObject(json, typedPacketType);
|
||||
|
||||
return typedPacketType.GetProperty("Data")!.GetValue(typedPacket);
|
||||
}
|
||||
|
||||
public async Task<T?> Receive<T>() where T : class
|
||||
{
|
||||
var o = await Receive();
|
||||
|
||||
if (o == null)
|
||||
return default;
|
||||
|
||||
return (T)o;
|
||||
}
|
||||
|
||||
public async Task Close()
|
||||
{
|
||||
if(WebSocket.State == WebSocketState.Open)
|
||||
await WebSocket.CloseOutputAsync(WebSocketCloseStatus.Empty, null, CancellationToken.None);
|
||||
}
|
||||
|
||||
public async Task WaitForClose()
|
||||
{
|
||||
var source = new TaskCompletionSource();
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (WebSocket.State == WebSocketState.Open)
|
||||
await Task.Delay(10);
|
||||
|
||||
source.SetResult();
|
||||
});
|
||||
|
||||
await source.Task;
|
||||
}
|
||||
|
||||
public class RawPacket
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public object Data { get; set; }
|
||||
}
|
||||
|
||||
public class RawPacket<T>
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public T Data { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moonlight.Core.Helpers;
|
||||
using MoonCore.Helpers;
|
||||
using Moonlight.Features.Theming.Services;
|
||||
|
||||
namespace Moonlight.Core.Http.Controllers.Api;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using MoonCore.Abstractions;
|
||||
using Moonlight.Core.Database.Entities;
|
||||
using Moonlight.Core.Models.Enums;
|
||||
using Moonlight.Core.Repositories;
|
||||
|
||||
using Moonlight.Core.Services;
|
||||
using Moonlight.Core.Services.Utils;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moonlight.Core.Helpers;
|
||||
using MoonCore.Helpers;
|
||||
using Moonlight.Core.Models.Enums;
|
||||
using Moonlight.Core.Services;
|
||||
using Moonlight.Core.Services.Utils;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moonlight.Core.Helpers;
|
||||
using MoonCore.Helpers;
|
||||
using Moonlight.Core.Services;
|
||||
|
||||
namespace Moonlight.Core.Http.Controllers.Api;
|
||||
|
|
|
@ -1,38 +1,41 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using MoonCore.Abstractions;
|
||||
using MoonCore.Attributes;
|
||||
using Moonlight.Core.Database;
|
||||
|
||||
namespace Moonlight.Core.Repositories;
|
||||
|
||||
public class Repository<TEntity> where TEntity : class
|
||||
[Scoped]
|
||||
public class GenericRepository<TEntity> : Repository<TEntity> where TEntity : class
|
||||
{
|
||||
private readonly DataContext DataContext;
|
||||
private readonly DbSet<TEntity> DbSet;
|
||||
|
||||
public Repository(DataContext dbContext)
|
||||
public GenericRepository(DataContext dbContext)
|
||||
{
|
||||
DataContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
|
||||
DbSet = DataContext.Set<TEntity>();
|
||||
}
|
||||
|
||||
public DbSet<TEntity> Get()
|
||||
public override DbSet<TEntity> Get()
|
||||
{
|
||||
return DbSet;
|
||||
}
|
||||
|
||||
public TEntity Add(TEntity entity)
|
||||
public override TEntity Add(TEntity entity)
|
||||
{
|
||||
var x = DbSet.Add(entity);
|
||||
DataContext.SaveChanges();
|
||||
return x.Entity;
|
||||
}
|
||||
|
||||
public void Update(TEntity entity)
|
||||
public override void Update(TEntity entity)
|
||||
{
|
||||
DbSet.Update(entity);
|
||||
DataContext.SaveChanges();
|
||||
}
|
||||
|
||||
public void Delete(TEntity entity)
|
||||
public override void Delete(TEntity entity)
|
||||
{
|
||||
DbSet.Remove(entity);
|
||||
DataContext.SaveChanges();
|
|
@ -2,20 +2,26 @@
|
|||
using Moonlight.Core.Event;
|
||||
using Moonlight.Core.Event.Args;
|
||||
using Moonlight.Features.ServiceManagement.Entities;
|
||||
using BackgroundService = MoonCore.Abstractions.BackgroundService;
|
||||
|
||||
namespace Moonlight.Core.Services.Background;
|
||||
|
||||
public class AutoMailSendService // This service is responsible for sending mails automatically
|
||||
public class AutoMailSendService : BackgroundService // This service is responsible for sending mails automatically
|
||||
{
|
||||
private readonly MailService MailService;
|
||||
|
||||
public AutoMailSendService(MailService mailService)
|
||||
{
|
||||
MailService = mailService;
|
||||
}
|
||||
|
||||
public override Task Run()
|
||||
{
|
||||
Events.OnUserRegistered += OnUserRegistered;
|
||||
Events.OnServiceOrdered += OnServiceOrdered;
|
||||
Events.OnTransactionCreated += OnTransactionCreated;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async void OnTransactionCreated(object? sender, TransactionCreatedEventArgs eventArgs)
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
using Moonlight.Core.Helpers;
|
||||
using MoonCore.Attributes;
|
||||
using MoonCore.Helpers;
|
||||
|
||||
namespace Moonlight.Core.Services;
|
||||
|
||||
[Singleton]
|
||||
public class BucketService
|
||||
{
|
||||
private readonly string BasePath;
|
||||
|
|
|
@ -1,102 +0,0 @@
|
|||
using Moonlight.Core.Configuration;
|
||||
using Moonlight.Core.Helpers;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Moonlight.Core.Services;
|
||||
|
||||
public class ConfigService
|
||||
{
|
||||
private readonly string Path = PathBuilder.File("storage", "config.json");
|
||||
private ConfigV1 Data;
|
||||
|
||||
public ConfigService()
|
||||
{
|
||||
Reload();
|
||||
}
|
||||
|
||||
public void Reload()
|
||||
{
|
||||
if(!File.Exists(Path))
|
||||
File.WriteAllText(Path, "{}");
|
||||
|
||||
var text = File.ReadAllText(Path);
|
||||
Data = JsonConvert.DeserializeObject<ConfigV1>(text) ?? new();
|
||||
|
||||
ApplyEnvironmentVariables("Moonlight", Data);
|
||||
|
||||
Save();
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
var text = JsonConvert.SerializeObject(Data, Formatting.Indented);
|
||||
File.WriteAllText(Path, text);
|
||||
}
|
||||
|
||||
public ConfigV1 Get()
|
||||
{
|
||||
return Data;
|
||||
}
|
||||
|
||||
public string GetDiagnoseJson()
|
||||
{
|
||||
var text = File.ReadAllText(Path);
|
||||
var data = JsonConvert.DeserializeObject<ConfigV1>(text) ?? new();
|
||||
|
||||
// Security token
|
||||
data.Security.Token = "";
|
||||
|
||||
// Database
|
||||
if (string.IsNullOrEmpty(data.Database.Password))
|
||||
data.Database.Password = "WAS EMPTY";
|
||||
else
|
||||
data.Database.Password = "WAS NOT EMPTY";
|
||||
|
||||
// Mailserver
|
||||
if (string.IsNullOrEmpty(data.MailServer.Password))
|
||||
data.MailServer.Password = "WAS EMPTY";
|
||||
else
|
||||
data.MailServer.Password = "WAS NOT EMPTY";
|
||||
|
||||
return JsonConvert.SerializeObject(data, Formatting.Indented);
|
||||
}
|
||||
|
||||
private void ApplyEnvironmentVariables(string prefix, object objectToLookAt)
|
||||
{
|
||||
foreach (var property in objectToLookAt.GetType().GetProperties())
|
||||
{
|
||||
var envName = $"{prefix}_{property.Name}";
|
||||
|
||||
if (property.PropertyType.Assembly == GetType().Assembly)
|
||||
{
|
||||
ApplyEnvironmentVariables(envName, property.GetValue(objectToLookAt)!);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!Environment.GetEnvironmentVariables().Contains(envName))
|
||||
continue;
|
||||
|
||||
var envValue = Environment.GetEnvironmentVariable(envName)!;
|
||||
|
||||
if (property.PropertyType == typeof(string))
|
||||
{
|
||||
property.SetValue(objectToLookAt, envValue);
|
||||
}
|
||||
else if (property.PropertyType == typeof(int))
|
||||
{
|
||||
if(!int.TryParse(envValue, out int envIntValue))
|
||||
continue;
|
||||
|
||||
property.SetValue(objectToLookAt, envIntValue);
|
||||
}
|
||||
else if (property.PropertyType == typeof(bool))
|
||||
{
|
||||
if(!bool.TryParse(envValue, out bool envBoolValue))
|
||||
continue;
|
||||
|
||||
property.SetValue(objectToLookAt, envBoolValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using MoonCore.Abstractions;
|
||||
using MoonCore.Attributes;
|
||||
using MoonCore.Exceptions;
|
||||
using MoonCore.Helpers;
|
||||
using Moonlight.Core.Database.Entities;
|
||||
using Moonlight.Core.Exceptions;
|
||||
using Moonlight.Core.Helpers;
|
||||
using Moonlight.Core.Models.Abstractions;
|
||||
using Moonlight.Core.Models.Enums;
|
||||
using Moonlight.Core.Repositories;
|
||||
using Moonlight.Core.Services.Utils;
|
||||
using Moonlight.Features.StoreSystem.Entities;
|
||||
using OtpNet;
|
||||
|
@ -13,6 +14,7 @@ namespace Moonlight.Core.Services;
|
|||
|
||||
// This service allows you to reauthenticate, login and force login
|
||||
// It does also contain the permission system accessor for the current user
|
||||
[Scoped]
|
||||
public class IdentityService
|
||||
{
|
||||
private readonly Repository<User> UserRepository;
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
using Microsoft.JSInterop;
|
||||
|
||||
namespace Moonlight.Core.Services.Interop;
|
||||
|
||||
public class AlertService
|
||||
{
|
||||
private readonly IJSRuntime JsRuntime;
|
||||
|
||||
public AlertService(IJSRuntime jsRuntime)
|
||||
{
|
||||
JsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
public async Task Info(string title, string message)
|
||||
{
|
||||
await JsRuntime.InvokeVoidAsync("moonlight.alerts.info", title, message);
|
||||
}
|
||||
|
||||
public async Task Success(string title, string message)
|
||||
{
|
||||
await JsRuntime.InvokeVoidAsync("moonlight.alerts.success", title, message);
|
||||
}
|
||||
|
||||
public async Task Warning(string title, string message)
|
||||
{
|
||||
await JsRuntime.InvokeVoidAsync("moonlight.alerts.warning", title, message);
|
||||
}
|
||||
|
||||
public async Task Error(string title, string message)
|
||||
{
|
||||
await JsRuntime.InvokeVoidAsync("moonlight.alerts.error", title, message);
|
||||
}
|
||||
|
||||
public async Task<string> Text(string title, string message)
|
||||
{
|
||||
return await JsRuntime.InvokeAsync<string>("moonlight.alerts.text", title, message);
|
||||
}
|
||||
|
||||
public async Task<bool> YesNo(string title, string yes, string no)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await JsRuntime.InvokeAsync<bool>("moonlight.alerts.yesno", title, yes, no);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
using Microsoft.JSInterop;
|
||||
|
||||
namespace Moonlight.Core.Services.Interop;
|
||||
|
||||
public class ClipboardService
|
||||
{
|
||||
private readonly IJSRuntime JsRuntime;
|
||||
|
||||
public ClipboardService(IJSRuntime jsRuntime)
|
||||
{
|
||||
JsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
public async Task Copy(string content)
|
||||
{
|
||||
await JsRuntime.InvokeVoidAsync("moonlight.clipboard.copy", content);
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
using Microsoft.JSInterop;
|
||||
|
||||
namespace Moonlight.Core.Services.Interop;
|
||||
|
||||
public class CookieService
|
||||
{
|
||||
private readonly IJSRuntime JsRuntime;
|
||||
private string Expires = "";
|
||||
|
||||
public CookieService(IJSRuntime jsRuntime)
|
||||
{
|
||||
JsRuntime = jsRuntime;
|
||||
ExpireDays = 300;
|
||||
}
|
||||
|
||||
public async Task SetValue(string key, string value, int? days = null)
|
||||
{
|
||||
var curExp = (days != null) ? (days > 0 ? DateToUTC(days.Value) : "") : Expires;
|
||||
await SetCookie($"{key}={value}; expires={curExp}; path=/");
|
||||
}
|
||||
|
||||
public async Task<string> GetValue(string key, string def = "")
|
||||
{
|
||||
var cookieString = await GetCookie();
|
||||
|
||||
var cookieParts = cookieString.Split(";");
|
||||
|
||||
foreach (var cookiePart in cookieParts)
|
||||
{
|
||||
if(string.IsNullOrEmpty(cookiePart))
|
||||
continue;
|
||||
|
||||
var cookieKeyValue = cookiePart.Split("=")
|
||||
.Select(x => x.Trim()) // There may be spaces e.g. with the "AspNetCore.Culture" key
|
||||
.ToArray();
|
||||
|
||||
if (cookieKeyValue.Length == 2)
|
||||
{
|
||||
if (cookieKeyValue[0] == key)
|
||||
return cookieKeyValue[1];
|
||||
}
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
private async Task SetCookie(string value)
|
||||
{
|
||||
await JsRuntime.InvokeVoidAsync("eval", $"document.cookie = \"{value}\"");
|
||||
}
|
||||
|
||||
private async Task<string> GetCookie()
|
||||
{
|
||||
return await JsRuntime.InvokeAsync<string>("eval", $"document.cookie");
|
||||
}
|
||||
|
||||
private int ExpireDays
|
||||
{
|
||||
set => Expires = DateToUTC(value);
|
||||
}
|
||||
|
||||
private static string DateToUTC(int days) => DateTime.Now.AddDays(days).ToUniversalTime().ToString("R");
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
using System.Text;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace Moonlight.Core.Services.Interop;
|
||||
|
||||
public class FileDownloadService
|
||||
{
|
||||
private readonly IJSRuntime JsRuntime;
|
||||
|
||||
public FileDownloadService(IJSRuntime jsRuntime)
|
||||
{
|
||||
JsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
public async Task DownloadStream(string fileName, Stream stream)
|
||||
{
|
||||
using var streamRef = new DotNetStreamReference(stream);
|
||||
|
||||
await JsRuntime.InvokeVoidAsync("moonlight.utils.download", fileName, streamRef);
|
||||
}
|
||||
|
||||
public async Task DownloadBytes(string fileName, byte[] bytes)
|
||||
{
|
||||
var ms = new MemoryStream(bytes);
|
||||
|
||||
await DownloadStream(fileName, ms);
|
||||
|
||||
ms.Close();
|
||||
await ms.DisposeAsync();
|
||||
}
|
||||
|
||||
public async Task DownloadString(string fileName, string content) =>
|
||||
await DownloadBytes(fileName, Encoding.UTF8.GetBytes(content));
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
using Microsoft.JSInterop;
|
||||
|
||||
namespace Moonlight.Core.Services.Interop;
|
||||
|
||||
public class ModalService
|
||||
{
|
||||
private readonly IJSRuntime JsRuntime;
|
||||
|
||||
public ModalService(IJSRuntime jsRuntime)
|
||||
{
|
||||
JsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
public async Task Show(string id, bool focus = true) // Focus can be specified to fix issues with other components
|
||||
{
|
||||
try
|
||||
{
|
||||
await JsRuntime.InvokeVoidAsync("moonlight.modals.show", id, focus);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Hide(string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
await JsRuntime.InvokeVoidAsync("moonlight.modals.hide", id);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Ignored
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
using Microsoft.JSInterop;
|
||||
|
||||
namespace Moonlight.Core.Services.Interop;
|
||||
|
||||
public class ToastService
|
||||
{
|
||||
private readonly IJSRuntime JsRuntime;
|
||||
|
||||
public ToastService(IJSRuntime jsRuntime)
|
||||
{
|
||||
JsRuntime = jsRuntime;
|
||||
}
|
||||
|
||||
public async Task Success(string title, string message, int timeout = 5000)
|
||||
{
|
||||
await JsRuntime.InvokeVoidAsync("moonlight.toasts.success", title, message, timeout);
|
||||
}
|
||||
|
||||
public async Task Info(string title, string message, int timeout = 5000)
|
||||
{
|
||||
await JsRuntime.InvokeVoidAsync("moonlight.toasts.info", title, message, timeout);
|
||||
}
|
||||
|
||||
public async Task Danger(string title, string message, int timeout = 5000)
|
||||
{
|
||||
await JsRuntime.InvokeVoidAsync("moonlight.toasts.danger", title, message, timeout);
|
||||
}
|
||||
|
||||
public async Task Warning(string title, string message, int timeout = 5000)
|
||||
{
|
||||
await JsRuntime.InvokeVoidAsync("moonlight.toasts.warning", title, message, timeout);
|
||||
}
|
||||
|
||||
// Overloads
|
||||
|
||||
public async Task Success(string message, int timeout = 5000)
|
||||
{
|
||||
await Success("", message, timeout);
|
||||
}
|
||||
|
||||
public async Task Info(string message, int timeout = 5000)
|
||||
{
|
||||
await Info("", message, timeout);
|
||||
}
|
||||
|
||||
public async Task Danger(string message, int timeout = 5000)
|
||||
{
|
||||
await Danger("", message, timeout);
|
||||
}
|
||||
|
||||
public async Task Warning(string message, int timeout = 5000)
|
||||
{
|
||||
await Warning("", message, timeout);
|
||||
}
|
||||
}
|
|
@ -1,16 +1,20 @@
|
|||
using MailKit.Net.Smtp;
|
||||
using MimeKit;
|
||||
using MoonCore.Attributes;
|
||||
using MoonCore.Helpers;
|
||||
using MoonCore.Services;
|
||||
using Moonlight.Core.Configuration;
|
||||
using Moonlight.Core.Database.Entities;
|
||||
using Moonlight.Core.Helpers;
|
||||
|
||||
namespace Moonlight.Core.Services;
|
||||
|
||||
[Singleton]
|
||||
public class MailService
|
||||
{
|
||||
private readonly ConfigService ConfigService;
|
||||
private readonly ConfigService<ConfigV1> ConfigService;
|
||||
private readonly string BasePath;
|
||||
|
||||
public MailService(ConfigService configService)
|
||||
public MailService(ConfigService<ConfigV1> configService)
|
||||
{
|
||||
ConfigService = configService;
|
||||
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
using System.IO.Compression;
|
||||
using MoonCore.Attributes;
|
||||
using MoonCore.Helpers;
|
||||
using MoonCore.Services;
|
||||
using Moonlight.Core.Configuration;
|
||||
using Moonlight.Core.Event;
|
||||
using Moonlight.Core.Extensions;
|
||||
using Moonlight.Core.Helpers;
|
||||
using Moonlight.Features.Theming.Services;
|
||||
|
||||
namespace Moonlight.Core.Services;
|
||||
|
||||
[Singleton]
|
||||
public class MoonlightService // This service can be used to perform strictly panel specific actions
|
||||
{
|
||||
private readonly ConfigService ConfigService;
|
||||
private readonly ConfigService<ConfigV1> ConfigService;
|
||||
private readonly IServiceProvider ServiceProvider;
|
||||
|
||||
public WebApplication Application { get; set; } // Do NOT modify using a plugin
|
||||
public string LogPath { get; set; } // Do NOT modify using a plugin
|
||||
public ThemeService Theme => ServiceProvider.GetRequiredService<ThemeService>();
|
||||
|
||||
public MoonlightService(ConfigService configService, IServiceProvider serviceProvider)
|
||||
public MoonlightService(ConfigService<ConfigV1> configService, IServiceProvider serviceProvider)
|
||||
{
|
||||
ConfigService = configService;
|
||||
ServiceProvider = serviceProvider;
|
||||
|
@ -53,9 +57,11 @@ public class MoonlightService // This service can be used to perform strictly pa
|
|||
|
||||
// TODO: Add node settings here
|
||||
|
||||
// TODO: Reimplement as extension here
|
||||
|
||||
// Add config
|
||||
var config = ConfigService.GetDiagnoseJson();
|
||||
await zip.AddFromText("config.json", config);
|
||||
//var config = ConfigService.GetDiagnoseJson();
|
||||
//await zip.AddFromText("config.json", config);
|
||||
|
||||
// Make a list of plugins
|
||||
var pluginService = scope.ServiceProvider.GetRequiredService<PluginService>();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
using System.Reflection;
|
||||
using Moonlight.Core.Helpers;
|
||||
using MoonCore.Helpers;
|
||||
using Moonlight.Core.Plugins;
|
||||
using Moonlight.Core.Plugins.Contexts;
|
||||
using Moonlight.Features.ServiceManagement.Models.Abstractions;
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
using Moonlight.Core.Models.Abstractions;
|
||||
using MoonCore.Attributes;
|
||||
using Moonlight.Core.Models.Abstractions;
|
||||
|
||||
namespace Moonlight.Core.Services;
|
||||
|
||||
[Singleton]
|
||||
public class SessionService
|
||||
{
|
||||
private readonly List<Session> AllSessions = new();
|
||||
|
|
|
@ -1,28 +1,32 @@
|
|||
using Moonlight.Core.Database.Entities;
|
||||
using MoonCore.Abstractions;
|
||||
using MoonCore.Attributes;
|
||||
using MoonCore.Exceptions;
|
||||
using MoonCore.Helpers;
|
||||
using MoonCore.Services;
|
||||
using Moonlight.Core.Configuration;
|
||||
using Moonlight.Core.Database.Entities;
|
||||
using Moonlight.Core.Event;
|
||||
using Moonlight.Core.Exceptions;
|
||||
using Moonlight.Core.Extensions;
|
||||
using Moonlight.Core.Helpers;
|
||||
using Moonlight.Core.Models.Abstractions;
|
||||
using Moonlight.Core.Models.Enums;
|
||||
using Moonlight.Core.Models.Templates;
|
||||
using Moonlight.Core.Repositories;
|
||||
using Moonlight.Core.Services.Utils;
|
||||
using OtpNet;
|
||||
|
||||
namespace Moonlight.Core.Services.Users;
|
||||
|
||||
[Scoped]
|
||||
public class UserAuthService
|
||||
{
|
||||
private readonly Repository<User> UserRepository;
|
||||
private readonly JwtService JwtService;
|
||||
private readonly ConfigService ConfigService;
|
||||
private readonly ConfigService<ConfigV1> ConfigService;
|
||||
private readonly MailService MailService;
|
||||
|
||||
public UserAuthService(
|
||||
Repository<User> userRepository,
|
||||
JwtService jwtService,
|
||||
ConfigService configService,
|
||||
ConfigService<ConfigV1> configService,
|
||||
MailService mailService)
|
||||
{
|
||||
UserRepository = userRepository;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using MoonCore.Abstractions;
|
||||
using MoonCore.Attributes;
|
||||
using Moonlight.Core.Database.Entities;
|
||||
using Moonlight.Core.Repositories;
|
||||
|
||||
using Moonlight.Features.Community.Entities;
|
||||
using Moonlight.Features.Community.Services;
|
||||
using Moonlight.Features.ServiceManagement.Entities;
|
||||
|
@ -10,6 +12,7 @@ using Moonlight.Features.Ticketing.Entities;
|
|||
|
||||
namespace Moonlight.Core.Services.Users;
|
||||
|
||||
[Scoped]
|
||||
public class UserDeleteService
|
||||
{
|
||||
private readonly Repository<Service> ServiceRepository;
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
using Moonlight.Core.Database.Entities;
|
||||
using Moonlight.Core.Repositories;
|
||||
using MoonCore.Abstractions;
|
||||
using MoonCore.Attributes;
|
||||
using Moonlight.Core.Database.Entities;
|
||||
|
||||
|
||||
namespace Moonlight.Core.Services.Users;
|
||||
|
||||
[Scoped]
|
||||
public class UserDetailsService
|
||||
{
|
||||
private readonly BucketService BucketService;
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
using Moonlight.Core.Database.Entities;
|
||||
using Moonlight.Core.Exceptions;
|
||||
using Moonlight.Core.Repositories;
|
||||
using MoonCore.Abstractions;
|
||||
using MoonCore.Attributes;
|
||||
using MoonCore.Exceptions;
|
||||
using Moonlight.Core.Database.Entities;
|
||||
|
||||
namespace Moonlight.Core.Services.Users;
|
||||
|
||||
[Scoped]
|
||||
public class UserService
|
||||
{
|
||||
private readonly Repository<User> UserRepository;
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
using Moonlight.Core.Helpers;
|
||||
using MoonCore.Attributes;
|
||||
using MoonCore.Helpers;
|
||||
using MoonCore.Services;
|
||||
using Moonlight.Core.Configuration;
|
||||
|
||||
namespace Moonlight.Core.Services.Utils;
|
||||
|
||||
[Scoped]
|
||||
public class ConnectionService
|
||||
{
|
||||
private readonly IHttpContextAccessor ContextAccessor;
|
||||
private readonly ConfigService ConfigService;
|
||||
private readonly ConfigService<ConfigV1> ConfigService;
|
||||
|
||||
public ConnectionService(IHttpContextAccessor contextAccessor, ConfigService configService)
|
||||
public ConnectionService(IHttpContextAccessor contextAccessor, ConfigService<ConfigV1> configService)
|
||||
{
|
||||
ContextAccessor = contextAccessor;
|
||||
ConfigService = configService;
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
using JWT.Algorithms;
|
||||
using JWT.Builder;
|
||||
using Moonlight.Core.Helpers;
|
||||
using MoonCore.Attributes;
|
||||
using MoonCore.Helpers;
|
||||
using MoonCore.Services;
|
||||
using Moonlight.Core.Configuration;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Moonlight.Core.Services.Utils;
|
||||
|
||||
[Singleton]
|
||||
public class JwtService
|
||||
{
|
||||
private readonly ConfigService ConfigService;
|
||||
private readonly ConfigService<ConfigV1> ConfigService;
|
||||
private readonly TimeSpan DefaultDuration = TimeSpan.FromDays(365 * 10);
|
||||
|
||||
public JwtService(ConfigService configService)
|
||||
public JwtService(ConfigService<ConfigV1> configService)
|
||||
{
|
||||
ConfigService = configService;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
@using Moonlight.Core.Models.Forms
|
||||
@using Moonlight.Core.Exceptions
|
||||
@using Moonlight.Core.Models.Enums
|
||||
@using Moonlight.Core.Models.Forms.Auth
|
||||
@using Moonlight.Core.Services
|
||||
@using Moonlight.Core.Services.Users
|
||||
@using MoonCore.Exceptions
|
||||
|
||||
@inject IdentityService IdentityService
|
||||
@inject UserService UserService
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
@using Moonlight.Core.Models.Forms
|
||||
@using Moonlight.Core.Models.Forms.Auth
|
||||
@using Moonlight.Core.Services
|
||||
@using Moonlight.Core.Services.Interop
|
||||
@using MoonCoreUI.Services
|
||||
|
||||
@inject IdentityService IdentityService
|
||||
@inject CookieService CookieService
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
@page "/register"
|
||||
@* Virtual route to trick blazor *@
|
||||
@using Moonlight.Core.Models.Forms
|
||||
@using Moonlight.Core.Exceptions
|
||||
@using Moonlight.Core.Models.Forms.Auth
|
||||
@using Moonlight.Core.Services
|
||||
@using Moonlight.Core.Services.Interop
|
||||
@using Moonlight.Core.Services.Users
|
||||
@using MoonCore.Exceptions
|
||||
@using MoonCoreUI.Services
|
||||
|
||||
@inject IdentityService IdentityService
|
||||
@inject UserService UserService
|
||||
|
|
|
@ -1,242 +0,0 @@
|
|||
@using BlazorTable
|
||||
@using System.Linq.Expressions
|
||||
@using Mappy.Net
|
||||
@using Moonlight.Core.Repositories
|
||||
@using Moonlight.Core.Services.Interop
|
||||
|
||||
@typeparam TItem where TItem : class
|
||||
@typeparam TCreateForm
|
||||
@typeparam TUpdateForm
|
||||
|
||||
@inject Repository<TItem> ItemRepository
|
||||
@inject ToastService ToastService
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">@(Title)</h3>
|
||||
<div class="card-toolbar">
|
||||
@Toolbar
|
||||
<button @onclick="StartCreate" class="btn btn-icon btn-success ms-3">
|
||||
<i class="bx bx-sm bx-plus"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<LazyLoader @ref="LazyLoader" Load="LoadItems">
|
||||
<Table TableItem="TItem"
|
||||
Items="Items"
|
||||
PageSize="50"
|
||||
TableClass="table table-row-bordered table-row-gray-100 align-middle gs-0 gy-3 fs-6"
|
||||
TableHeadClass="fw-bold text-muted">
|
||||
@View
|
||||
<Column TableItem="TItem" Field="IdExpression" Title="" Sortable="false" Filterable="false">
|
||||
<Template>
|
||||
<div class="text-end">
|
||||
<div class="btn-group">
|
||||
<button @onclick="() => StartUpdate(context)" class="btn btn-icon btn-warning">
|
||||
<i class="bx bx-sm bx-slider"></i>
|
||||
</button>
|
||||
<button @onclick="() => StartDelete(context)" class="btn btn-icon btn-danger">
|
||||
<i class="bx bx-sm bx-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Template>
|
||||
</Column>
|
||||
</Table>
|
||||
</LazyLoader>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SmartModal @ref="CreateModal" CssClasses="modal-dialog-centered modal-lg">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Create</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<SmartForm Model="CreateForm" OnValidSubmit="FinishCreate">
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<AutoForm Columns="@(CreateForm.GetType().GetProperties().Length > 1 ? 6 : 12)" Model="CreateForm"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="submit" class="btn btn-primary">Save changes</button>
|
||||
</div>
|
||||
</SmartForm>
|
||||
</SmartModal>
|
||||
|
||||
<SmartModal @ref="UpdateModal" CssClasses="modal-dialog-centered modal-lg">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Update</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<SmartForm Model="UpdateForm" OnValidSubmit="FinishUpdate">
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<AutoForm Columns="@(UpdateForm.GetType().GetProperties().Length > 1 ? 6 : 12)" Model="UpdateForm"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="submit" class="btn btn-primary">Save changes</button>
|
||||
</div>
|
||||
</SmartForm>
|
||||
</SmartModal>
|
||||
|
||||
<SmartModal @ref="DeleteModal" CssClasses="modal-dialog-centered">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Do you want to delete this item?</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="text-gray-400 fs-5 fw-semibold">
|
||||
This action cannot be undone. The data will be deleted and cannot be restored
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer p-3">
|
||||
<div class="btn-group w-100">
|
||||
<WButton OnClick="FinishDelete" Text="Delete" CssClasses="btn btn-danger w-50 me-3"/>
|
||||
<button class="btn btn-secondary w-50" data-bs-dismiss="modal">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</SmartModal>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public string Title { get; set; } = "";
|
||||
|
||||
[Parameter]
|
||||
public Func<Repository<TItem>, TItem[]> Load { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment View { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment Toolbar { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<TItem, Task>? ValidateAdd { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<TItem, Task>? ValidateUpdate { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<TItem, Task>? ValidateDelete { get; set; }
|
||||
|
||||
private TItem[] Items;
|
||||
private TCreateForm CreateForm;
|
||||
private TUpdateForm UpdateForm;
|
||||
private TItem ItemToUpdate;
|
||||
private TItem ItemToDelete;
|
||||
|
||||
private SmartModal CreateModal;
|
||||
private SmartModal UpdateModal;
|
||||
private SmartModal DeleteModal;
|
||||
|
||||
private Expression<Func<TItem, object>> IdExpression;
|
||||
private LazyLoader LazyLoader;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
if (Load == null)
|
||||
throw new ArgumentNullException(nameof(Load));
|
||||
|
||||
CreateForm = Activator.CreateInstance<TCreateForm>()!;
|
||||
UpdateForm = Activator.CreateInstance<TUpdateForm>()!;
|
||||
|
||||
IdExpression = CreateExpression();
|
||||
}
|
||||
|
||||
public async Task Reload() => await LazyLoader.Reload();
|
||||
|
||||
private Task LoadItems(LazyLoader _)
|
||||
{
|
||||
Items = Load.Invoke(ItemRepository);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task StartUpdate(TItem item)
|
||||
{
|
||||
UpdateForm = Mapper.Map<TUpdateForm>(item);
|
||||
ItemToUpdate = item;
|
||||
await UpdateModal.Show();
|
||||
}
|
||||
|
||||
private async Task FinishUpdate()
|
||||
{
|
||||
var item = Mapper.Map(ItemToUpdate, UpdateForm!);
|
||||
|
||||
if (ValidateUpdate != null) // Optional additional validation
|
||||
await ValidateUpdate.Invoke(item);
|
||||
|
||||
ItemRepository.Update(item);
|
||||
|
||||
// Reset
|
||||
await UpdateModal.Hide();
|
||||
await LazyLoader.Reload();
|
||||
await ToastService.Success("Successfully updated item");
|
||||
}
|
||||
|
||||
private async Task StartCreate()
|
||||
{
|
||||
CreateForm = Activator.CreateInstance<TCreateForm>()!;
|
||||
await CreateModal.Show();
|
||||
}
|
||||
|
||||
private async Task FinishCreate()
|
||||
{
|
||||
var item = Mapper.Map<TItem>(CreateForm!);
|
||||
|
||||
if (ValidateAdd != null) // Optional additional validation
|
||||
await ValidateAdd.Invoke(item);
|
||||
|
||||
ItemRepository.Add(item);
|
||||
|
||||
// Reset
|
||||
await CreateModal.Hide();
|
||||
await LazyLoader.Reload();
|
||||
await ToastService.Success("Successfully added item");
|
||||
}
|
||||
|
||||
private async Task StartDelete(TItem item)
|
||||
{
|
||||
ItemToDelete = item;
|
||||
await DeleteModal.Show();
|
||||
}
|
||||
|
||||
private async Task FinishDelete()
|
||||
{
|
||||
if (ValidateDelete != null) // Optional additional validation
|
||||
await ValidateDelete.Invoke(ItemToDelete);
|
||||
|
||||
ItemRepository.Delete(ItemToDelete);
|
||||
|
||||
// Reset
|
||||
await DeleteModal.Hide();
|
||||
await LazyLoader.Reload();
|
||||
await ToastService.Success("Successfully deleted item");
|
||||
}
|
||||
|
||||
private Expression<Func<TItem, object>> CreateExpression()
|
||||
{
|
||||
// Parameter expression for the input object
|
||||
var inputParam = Expression.Parameter(typeof(TItem), "input");
|
||||
|
||||
// Convert the input object to the actual model type (MyModel in this example)
|
||||
var castedInput = Expression.Convert(inputParam, typeof(TItem));
|
||||
|
||||
// Create a property access expression using the property name
|
||||
var propertyAccess = Expression.Property(castedInput, "Id");
|
||||
|
||||
// Convert the property value to an object
|
||||
var castedResult = Expression.Convert(propertyAccess, typeof(object));
|
||||
|
||||
// Create a lambda expression
|
||||
var lambda = Expression.Lambda<Func<TItem, object>>(castedResult, inputParam);
|
||||
|
||||
return lambda;
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
@using Moonlight.Core.Helpers
|
||||
@typeparam TForm
|
||||
|
||||
@foreach (var prop in typeof(TForm).GetProperties())
|
||||
{
|
||||
<div class="col-md-@(Columns) col-12">
|
||||
@{
|
||||
var typeToCreate = typeof(AutoProperty<>).MakeGenericType(prop.PropertyType);
|
||||
var rf = ComponentHelper.FromType(typeToCreate, parameters =>
|
||||
{
|
||||
parameters.Add("Data", Model);
|
||||
parameters.Add("Property", prop);
|
||||
});
|
||||
}
|
||||
|
||||
@rf
|
||||
</div>
|
||||
}
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public TForm Model { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public int Columns { get; set; } = 6;
|
||||
}
|
|
@ -1,255 +0,0 @@
|
|||
@using BlazorTable
|
||||
@using System.Linq.Expressions
|
||||
@using Mappy.Net
|
||||
@using Moonlight.Core.Repositories
|
||||
@using Moonlight.Core.Services.Interop
|
||||
|
||||
@typeparam TItem where TItem : class
|
||||
@typeparam TRootItem where TRootItem : class
|
||||
@typeparam TCreateForm
|
||||
@typeparam TUpdateForm
|
||||
|
||||
@inject ToastService ToastService
|
||||
@inject Repository<TRootItem> RootRepository
|
||||
@inject Repository<TItem> ItemRepository
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">@(Title)</h3>
|
||||
<div class="card-toolbar">
|
||||
@Toolbar
|
||||
<button @onclick="StartCreate" class="btn btn-icon btn-success ms-3">
|
||||
<i class="bx bx-sm bx-plus"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<LazyLoader @ref="LazyLoader" Load="LoadItems">
|
||||
<Table TableItem="TItem"
|
||||
Items="Items"
|
||||
PageSize="50"
|
||||
TableClass="table table-row-bordered table-row-gray-100 align-middle gs-0 gy-3 fs-6"
|
||||
TableHeadClass="fw-bold text-muted">
|
||||
@View
|
||||
<Column TableItem="TItem" Field="IdExpression" Title="" Sortable="false" Filterable="false">
|
||||
<Template>
|
||||
<div class="text-end">
|
||||
<div class="btn-group">
|
||||
<button @onclick="() => StartUpdate(context)" class="btn btn-icon btn-warning">
|
||||
<i class="bx bx-sm bx-slider"></i>
|
||||
</button>
|
||||
<button @onclick="() => StartDelete(context)" class="btn btn-icon btn-danger">
|
||||
<i class="bx bx-sm bx-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Template>
|
||||
</Column>
|
||||
</Table>
|
||||
</LazyLoader>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SmartModal @ref="CreateModal" CssClasses="modal-dialog-centered modal-lg">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Create</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<SmartForm Model="CreateForm" OnValidSubmit="FinishCreate">
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<AutoForm Columns="@(CreateForm.GetType().GetProperties().Length > 1 ? 6 : 12)" Model="CreateForm"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="submit" class="btn btn-primary">Save changes</button>
|
||||
</div>
|
||||
</SmartForm>
|
||||
</SmartModal>
|
||||
|
||||
<SmartModal @ref="UpdateModal" CssClasses="modal-dialog-centered modal-lg">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Update</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<SmartForm Model="UpdateForm" OnValidSubmit="FinishUpdate">
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<AutoForm Columns="@(UpdateForm.GetType().GetProperties().Length > 1 ? 6 : 12)" Model="UpdateForm"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||
<button type="submit" class="btn btn-primary">Save changes</button>
|
||||
</div>
|
||||
</SmartForm>
|
||||
</SmartModal>
|
||||
|
||||
<SmartModal @ref="DeleteModal" CssClasses="modal-dialog-centered">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Do you want to delete this item?</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="text-gray-400 fs-5 fw-semibold">
|
||||
This action cannot be undone. The data will be deleted and cannot be restored
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer p-3">
|
||||
<div class="btn-group w-100">
|
||||
<WButton OnClick="FinishDelete" Text="Delete" CssClasses="btn btn-danger w-50 me-3"/>
|
||||
<button class="btn btn-secondary w-50" data-bs-dismiss="modal">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</SmartModal>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public string Title { get; set; } = "";
|
||||
|
||||
[Parameter]
|
||||
public TRootItem RootItem { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<TRootItem, IList<TItem>> Field { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment View { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment Toolbar { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<TItem, Task>? ValidateAdd { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<TItem, Task>? ValidateUpdate { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<TItem, Task>? ValidateDelete { get; set; }
|
||||
|
||||
private TItem[] Items;
|
||||
private TCreateForm CreateForm;
|
||||
private TUpdateForm UpdateForm;
|
||||
private TItem ItemToUpdate;
|
||||
private TItem ItemToDelete;
|
||||
|
||||
private SmartModal CreateModal;
|
||||
private SmartModal UpdateModal;
|
||||
private SmartModal DeleteModal;
|
||||
|
||||
private Expression<Func<TItem, object>> IdExpression;
|
||||
private LazyLoader LazyLoader;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
if (Field == null)
|
||||
throw new ArgumentNullException(nameof(Field));
|
||||
|
||||
CreateForm = Activator.CreateInstance<TCreateForm>()!;
|
||||
UpdateForm = Activator.CreateInstance<TUpdateForm>()!;
|
||||
|
||||
IdExpression = CreateExpression();
|
||||
}
|
||||
|
||||
public async Task Reload() => await LazyLoader.Reload();
|
||||
|
||||
private Task LoadItems(LazyLoader _)
|
||||
{
|
||||
Items = Field.Invoke(RootItem).ToArray();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task StartUpdate(TItem item)
|
||||
{
|
||||
UpdateForm = Mapper.Map<TUpdateForm>(item);
|
||||
ItemToUpdate = item;
|
||||
await UpdateModal.Show();
|
||||
}
|
||||
|
||||
private async Task FinishUpdate()
|
||||
{
|
||||
var item = Mapper.Map(ItemToUpdate, UpdateForm!);
|
||||
|
||||
if (ValidateUpdate != null) // Optional additional validation
|
||||
await ValidateUpdate.Invoke(item);
|
||||
|
||||
ItemRepository.Update(item);
|
||||
|
||||
// Reset
|
||||
await UpdateModal.Hide();
|
||||
await LazyLoader.Reload();
|
||||
await ToastService.Success("Successfully updated item");
|
||||
}
|
||||
|
||||
private async Task StartCreate()
|
||||
{
|
||||
CreateForm = Activator.CreateInstance<TCreateForm>()!;
|
||||
await CreateModal.Show();
|
||||
}
|
||||
|
||||
private async Task FinishCreate()
|
||||
{
|
||||
var item = Mapper.Map<TItem>(CreateForm!);
|
||||
|
||||
if (ValidateAdd != null) // Optional additional validation
|
||||
await ValidateAdd.Invoke(item);
|
||||
|
||||
Field.Invoke(RootItem).Add(item);
|
||||
RootRepository.Update(RootItem);
|
||||
|
||||
// Reset
|
||||
await CreateModal.Hide();
|
||||
await LazyLoader.Reload();
|
||||
await ToastService.Success("Successfully added item");
|
||||
}
|
||||
|
||||
private async Task StartDelete(TItem item)
|
||||
{
|
||||
ItemToDelete = item;
|
||||
await DeleteModal.Show();
|
||||
}
|
||||
|
||||
private async Task FinishDelete()
|
||||
{
|
||||
if (ValidateDelete != null) // Optional additional validation
|
||||
await ValidateDelete.Invoke(ItemToDelete);
|
||||
|
||||
Field.Invoke(RootItem).Remove(ItemToDelete);
|
||||
RootRepository.Update(RootItem);
|
||||
|
||||
try
|
||||
{
|
||||
ItemRepository.Delete(ItemToDelete);
|
||||
}
|
||||
catch (Exception) { /* ignored, as we dont want such an operation to fail the request */ }
|
||||
|
||||
// Reset
|
||||
await DeleteModal.Hide();
|
||||
await LazyLoader.Reload();
|
||||
await ToastService.Success("Successfully deleted item");
|
||||
}
|
||||
|
||||
private Expression<Func<TItem, object>> CreateExpression()
|
||||
{
|
||||
// Parameter expression for the input object
|
||||
var inputParam = Expression.Parameter(typeof(TItem), "input");
|
||||
|
||||
// Convert the input object to the actual model type (MyModel in this example)
|
||||
var castedInput = Expression.Convert(inputParam, typeof(TItem));
|
||||
|
||||
// Create a property access expression using the property name
|
||||
var propertyAccess = Expression.Property(castedInput, "Id");
|
||||
|
||||
// Convert the property value to an object
|
||||
var castedResult = Expression.Convert(propertyAccess, typeof(object));
|
||||
|
||||
// Create a lambda expression
|
||||
var lambda = Expression.Lambda<Func<TItem, object>>(castedResult, inputParam);
|
||||
|
||||
return lambda;
|
||||
}
|
||||
}
|
|
@ -1,157 +0,0 @@
|
|||
@using System.Reflection
|
||||
@using System.ComponentModel
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Moonlight.Core.Extensions.Attributes
|
||||
@using Moonlight.Core.Helpers
|
||||
@using Moonlight.Core.Repositories
|
||||
|
||||
@typeparam TProp
|
||||
@inject IServiceProvider ServiceProvider
|
||||
|
||||
<label class="form-label">
|
||||
@(Formatter.ConvertCamelCaseToSpaces(Property.Name))
|
||||
</label>
|
||||
|
||||
@* Description using attribute *@
|
||||
|
||||
@{
|
||||
var attrs = Property.GetCustomAttributes(true);
|
||||
|
||||
var descAttr = attrs
|
||||
.FirstOrDefault(x => x.GetType() == typeof(DescriptionAttribute));
|
||||
}
|
||||
|
||||
@if (descAttr != null)
|
||||
{
|
||||
var attribute = descAttr as DescriptionAttribute;
|
||||
|
||||
<div class="form-text fs-5 mb-2 mt-0">
|
||||
@(attribute!.Description)
|
||||
</div>
|
||||
}
|
||||
|
||||
@* Actual value binding *@
|
||||
|
||||
<div class="input-group mb-5">
|
||||
@if (Property.PropertyType == typeof(string))
|
||||
{
|
||||
<div class="w-100">
|
||||
<InputText id="@Property.Name" @bind-Value="Binder.StringValue" class="form-control"/>
|
||||
</div>
|
||||
}
|
||||
else if (Property.PropertyType == typeof(int))
|
||||
{
|
||||
<InputNumber id="@Property.Name" @bind-Value="Binder.IntValue" class="form-control"/>
|
||||
}
|
||||
else if (Property.PropertyType == typeof(double))
|
||||
{
|
||||
<InputNumber id="@Property.Name" @bind-Value="Binder.DoubleValue" class="form-control"/>
|
||||
}
|
||||
else if (Property.PropertyType == typeof(long))
|
||||
{
|
||||
<InputNumber id="@Property.Name" @bind-Value="Binder.LongValue" class="form-control"/>
|
||||
}
|
||||
else if (Property.PropertyType == typeof(bool))
|
||||
{
|
||||
<div class="form-check">
|
||||
<InputCheckbox id="@Property.Name" @bind-Value="Binder.BoolValue" class="form-check-input"/>
|
||||
</div>
|
||||
}
|
||||
else if (Property.PropertyType == typeof(DateTime))
|
||||
{
|
||||
<InputDate id="@Property.Name" @bind-Value="Binder.DateTimeValue" class="form-control"/>
|
||||
}
|
||||
else if (Property.PropertyType == typeof(decimal))
|
||||
{
|
||||
<InputNumber id="@Property.Name" step="0.01" @bind-Value="Binder.DoubleValue" class="form-control"/>
|
||||
}
|
||||
else if (Property.PropertyType.IsEnum)
|
||||
{
|
||||
<select @bind="Binder.Class" class="form-select">
|
||||
@foreach (var status in (TProp[])Enum.GetValues(typeof(TProp)))
|
||||
{
|
||||
if (Binder.Class.ToString() == status.ToString())
|
||||
{
|
||||
<option value="@(status)" selected="">@(status)</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@(status)">@(status)</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
}
|
||||
else if (Property.PropertyType.IsClass)
|
||||
{
|
||||
var attribute = Property.GetCustomAttributes(true)
|
||||
.FirstOrDefault(x => x.GetType() == typeof(SelectorAttribute)) as SelectorAttribute;
|
||||
|
||||
if (attribute != null)
|
||||
{
|
||||
if (attribute.UseDropdown)
|
||||
{
|
||||
var displayFunc = new Func<TProp, string>(x =>
|
||||
{
|
||||
var prop = typeof(TProp).GetProperties().First(x => x.Name == attribute.DisplayProp);
|
||||
return prop.GetValue(x) as string ?? "N/A";
|
||||
});
|
||||
|
||||
var searchFunc = new Func<TProp, string>(x =>
|
||||
{
|
||||
var prop = typeof(TProp).GetProperties().First(x => x.Name == attribute.SelectorProp);
|
||||
return prop.GetValue(x) as string ?? "N/A";
|
||||
});
|
||||
|
||||
<SmartDropdown @bind-Value="Binder.Class" DisplayFunc="displayFunc" SearchProp="searchFunc" Items="Items"/>
|
||||
}
|
||||
else
|
||||
{
|
||||
var displayFunc = new Func<TProp, string>(x =>
|
||||
{
|
||||
var prop = typeof(TProp).GetProperties().First(x => x.Name == attribute.DisplayProp);
|
||||
return prop.GetValue(x) as string ?? "N/A";
|
||||
});
|
||||
|
||||
<SmartSelect @bind-Value="Binder.Class" DisplayField="displayFunc" Items="Items" CanBeNull="true"/>
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public object Data { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public PropertyInfo Property { get; set; }
|
||||
|
||||
private PropBinder<TProp> Binder;
|
||||
private TProp[] Items = Array.Empty<TProp>();
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Binder = new(Property, Data);
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
Binder = new(Property, Data);
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
if (Property.GetCustomAttributes(true).Any(x => x.GetType() == typeof(SelectorAttribute)))
|
||||
{
|
||||
var typeToGetByDi = typeof(Repository<>).MakeGenericType(typeof(TProp));
|
||||
var repo = ServiceProvider.GetRequiredService(typeToGetByDi);
|
||||
var dbSet = repo.GetType().GetMethods().First(x => x.Name == "Get").Invoke(repo, null) as IEnumerable<TProp>;
|
||||
Items = dbSet!.ToArray();
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
@if (ShowConfirm)
|
||||
{
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-success me-2 rounded-end" @onclick="Do">Confirm</button>
|
||||
<button class="btn btn-danger rounded-start" @onclick="() => SetConfirm(false)">Cancel</button>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Working)
|
||||
{
|
||||
<button class="btn @(CssClasses) disabled" disabled="">
|
||||
<span class="spinner-border spinner-border-sm align-middle me-2"></span>
|
||||
@WorkingText
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button class="btn @(CssClasses)" @onclick="() => SetConfirm(true)">
|
||||
@Text
|
||||
@ChildContent
|
||||
</button>
|
||||
}
|
||||
}
|
||||
|
||||
@code
|
||||
{
|
||||
private bool Working { get; set; } = false;
|
||||
private bool ShowConfirm = false;
|
||||
|
||||
[Parameter]
|
||||
public string CssClasses { get; set; } = "btn-primary";
|
||||
|
||||
[Parameter]
|
||||
public string Text { get; set; } = "";
|
||||
|
||||
[Parameter]
|
||||
public string WorkingText { get; set; } = "";
|
||||
|
||||
[Parameter]
|
||||
public Func<Task>? OnClick { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment ChildContent { get; set; }
|
||||
|
||||
private async Task SetConfirm(bool b)
|
||||
{
|
||||
ShowConfirm = b;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private async Task Do()
|
||||
{
|
||||
Working = true;
|
||||
ShowConfirm = false;
|
||||
StateHasChanged();
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
if (OnClick != null)
|
||||
await OnClick.Invoke();
|
||||
|
||||
Working = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
@using Moonlight.Core.Helpers
|
||||
@{
|
||||
var typeToCreate = typeof(AutoForm<>).MakeGenericType(Model.GetType());
|
||||
var rf = ComponentHelper.FromType(typeToCreate, parameter =>
|
||||
{
|
||||
parameter.Add("Model", Model);
|
||||
parameter.Add("Columns", Columns);
|
||||
});
|
||||
}
|
||||
|
||||
@rf
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public object Model { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public int Columns { get; set; } = 6;
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Moonlight.Core.Helpers
|
||||
@using Moonlight.Core.Services.Interop
|
||||
|
||||
@inject ToastService ToastService
|
||||
|
||||
<InputFile OnChange="OnFileChanged" type="file" id="fileUpload" hidden=""/>
|
||||
<label for="fileUpload" class="">
|
||||
@if (SelectedFile == null)
|
||||
{
|
||||
@ChildContent
|
||||
}
|
||||
</label>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public IBrowserFile? SelectedFile { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public int MaxFileSize { get; set; } = 1024 * 1024 * 5;
|
||||
|
||||
[Parameter]
|
||||
public Func<IBrowserFile, Task>? OnFileSelected { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment ChildContent { get; set; }
|
||||
|
||||
private async Task OnFileChanged(InputFileChangeEventArgs arg)
|
||||
{
|
||||
if (arg.FileCount > 0)
|
||||
{
|
||||
if (arg.File.Size < MaxFileSize)
|
||||
{
|
||||
SelectedFile = arg.File;
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
if(OnFileSelected != null)
|
||||
await OnFileSelected.Invoke(SelectedFile);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await ToastService.Danger($"The uploaded file should not be bigger than {Formatter.FormatSize(MaxFileSize)}");
|
||||
}
|
||||
|
||||
SelectedFile = null;
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
public async Task RemoveSelection()
|
||||
{
|
||||
SelectedFile = null;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
|
@ -1,141 +0,0 @@
|
|||
@using Microsoft.AspNetCore.Components.Forms
|
||||
|
||||
@typeparam T
|
||||
@inherits InputBase<T>
|
||||
|
||||
<div class="dropdown w-100">
|
||||
<div class="input-group">
|
||||
@if (CurrentValue == null)
|
||||
{
|
||||
<input class="form-control" type="text" @bind-value="SearchTerm" @bind-value:event="oninput" placeholder="@(Placeholder)">
|
||||
}
|
||||
else
|
||||
{
|
||||
<input class="form-control" type="text" value="@(DisplayFunc(CurrentValue))">
|
||||
<button class="btn btn-sm btn-primary" @onclick="() => SelectItem(default(T)!)">
|
||||
<i class="bx bx-sm bx-x"></i>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
|
||||
@{
|
||||
var anyItems = FilteredItems.Any();
|
||||
}
|
||||
|
||||
<div class="dropdown-menu w-100 @(anyItems ? "show" : "")" style="max-height: 200px; overflow-y: auto;">
|
||||
@if (anyItems)
|
||||
{
|
||||
foreach (var item in FilteredItems)
|
||||
{
|
||||
<button class="dropdown-item py-2" type="button" @onclick="() => SelectItem(item)">@DisplayFunc(item)</button>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
|
||||
[Parameter]
|
||||
public IEnumerable<T> Items { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<T, string> DisplayFunc { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<T, string> SearchProp { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<Task>? OnSelected { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Placeholder { get; set; } = "Search...";
|
||||
|
||||
private string SearchTerm
|
||||
{
|
||||
get => searchTerm;
|
||||
set
|
||||
{
|
||||
searchTerm = value;
|
||||
FilteredItems = Items.OrderByDescending(x => Matches(SearchProp(x), searchTerm)).Take(30).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
private string searchTerm = "";
|
||||
|
||||
private List<T> FilteredItems = new();
|
||||
|
||||
private async void SelectItem(T item)
|
||||
{
|
||||
CurrentValue = item;
|
||||
SearchTerm = "";
|
||||
FilteredItems.Clear();
|
||||
|
||||
if (OnSelected != null)
|
||||
await OnSelected.Invoke();
|
||||
}
|
||||
|
||||
protected override bool TryParseValueFromString(string? value, out T result, out string? validationErrorMessage)
|
||||
{
|
||||
// Check if the value is null or empty
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
result = default(T)!;
|
||||
validationErrorMessage = "Value cannot be null or empty";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to find an item that matches the search term
|
||||
var item = FilteredItems.OrderByDescending(x => Matches(SearchProp(x), value)).FirstOrDefault();
|
||||
if (item != null)
|
||||
{
|
||||
result = item;
|
||||
validationErrorMessage = null;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = default(T)!;
|
||||
validationErrorMessage = $"No item found for search term '{value}'";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private float Matches(string input, string search)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(search))
|
||||
return 0;
|
||||
|
||||
var cleanedSearch = search
|
||||
.ToLower()
|
||||
.Replace(" ", "")
|
||||
.Replace("-", "");
|
||||
|
||||
var cleanedInput = input
|
||||
.ToLower()
|
||||
.Replace(" ", "")
|
||||
.Replace("-", "");
|
||||
|
||||
if (cleanedInput == cleanedSearch)
|
||||
return 10000;
|
||||
|
||||
float matches = 0;
|
||||
|
||||
int i = 0;
|
||||
foreach (var c in cleanedInput)
|
||||
{
|
||||
if (cleanedSearch.Length > i)
|
||||
{
|
||||
if (c == cleanedSearch[i])
|
||||
matches++;
|
||||
else
|
||||
matches--;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
matches = matches / searchTerm.Length;
|
||||
|
||||
return matches;
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
@typeparam TField where TField : struct
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@inherits InputBase<TField>
|
||||
|
||||
<select @bind="CurrentValue" class="form-select">
|
||||
@foreach (var status in (TField[])Enum.GetValues(typeof(TField)))
|
||||
{
|
||||
if (CurrentValue.ToString() == status.ToString())
|
||||
{
|
||||
<option value="@(status)" selected="">@(status)</option>
|
||||
}
|
||||
else
|
||||
{
|
||||
<option value="@(status)">@(status)</option>
|
||||
}
|
||||
}
|
||||
</select>
|
||||
|
||||
@code
|
||||
{
|
||||
protected override bool TryParseValueFromString(string? value, out TField result, out string? validationErrorMessage)
|
||||
{
|
||||
result = Enum.Parse<TField>(value);
|
||||
validationErrorMessage = "";
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Moonlight.Core.Helpers
|
||||
@using Moonlight.Core.Services.Interop
|
||||
|
||||
@inject ToastService ToastService
|
||||
|
||||
<InputFile OnChange="OnFileChanged" type="file" id="fileUpload" hidden=""/>
|
||||
<label for="fileUpload" class="">
|
||||
@if (SelectedFile != null)
|
||||
{
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control disabled" value="@(SelectedFile.Name)" disabled="">
|
||||
<button class="btn btn-danger" type="button" @onclick="RemoveSelection">
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="btn btn-primary me-3 btn-icon">
|
||||
<i class="bx bx-upload"></i>
|
||||
</div>
|
||||
}
|
||||
</label>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public IBrowserFile? SelectedFile { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public int MaxFileSize { get; set; } = 1024 * 1024 * 5;
|
||||
|
||||
private async Task OnFileChanged(InputFileChangeEventArgs arg)
|
||||
{
|
||||
if (arg.FileCount > 0)
|
||||
{
|
||||
if (arg.File.Size < MaxFileSize)
|
||||
{
|
||||
SelectedFile = arg.File;
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
return;
|
||||
}
|
||||
|
||||
await ToastService.Danger($"The uploaded file should not be bigger than {Formatter.FormatSize(MaxFileSize)}");
|
||||
}
|
||||
|
||||
SelectedFile = null;
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
public async Task RemoveSelection()
|
||||
{
|
||||
SelectedFile = null;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
|
@ -1,132 +0,0 @@
|
|||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Moonlight.Core.Exceptions
|
||||
|
||||
<div class="form @CssClass">
|
||||
<EditForm @ref="EditForm" Model="Model" OnValidSubmit="ValidSubmit" OnInvalidSubmit="InvalidSubmit">
|
||||
<DataAnnotationsValidator></DataAnnotationsValidator>
|
||||
@if (Working)
|
||||
{
|
||||
<div class="d-flex flex-center flex-column">
|
||||
<span class="fs-1 spinner-border spinner-border-lg align-middle me-2"></span>
|
||||
<span class="mt-3 fs-5">Proccessing</span>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ErrorMessages.Any())
|
||||
{
|
||||
<div class="alert alert-danger bg-danger text-white p-10 mb-3 rounded-0">
|
||||
@foreach (var msg in ErrorMessages)
|
||||
{
|
||||
@(msg)
|
||||
<br/>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@(ChildContent)
|
||||
}
|
||||
</EditForm>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public object Model { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<Task>? OnValidSubmit { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<Task>? OnInvalidSubmit { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<Task>? OnSubmit { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment ChildContent { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string CssClass { get; set; }
|
||||
|
||||
private EditForm EditForm;
|
||||
|
||||
private List<string> ErrorMessages = new();
|
||||
|
||||
private bool Working = false;
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
//EditForm.EditContext!.SetFieldCssClassProvider(new FieldCssHelper());
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ValidSubmit(EditContext context)
|
||||
{
|
||||
ErrorMessages.Clear();
|
||||
Working = true;
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
await InvokeAsync(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (OnValidSubmit != null)
|
||||
await OnValidSubmit.Invoke();
|
||||
|
||||
if (OnSubmit != null)
|
||||
await OnSubmit.Invoke();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is DisplayException displayException)
|
||||
{
|
||||
ErrorMessages.Add(displayException.Message);
|
||||
}
|
||||
else
|
||||
throw;
|
||||
}
|
||||
});
|
||||
|
||||
Working = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
});
|
||||
}
|
||||
|
||||
private async Task InvalidSubmit(EditContext context)
|
||||
{
|
||||
ErrorMessages.Clear();
|
||||
context.Validate();
|
||||
|
||||
foreach (var message in context.GetValidationMessages())
|
||||
{
|
||||
ErrorMessages.Add(message);
|
||||
}
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
try
|
||||
{
|
||||
if (OnInvalidSubmit != null)
|
||||
await OnInvalidSubmit.Invoke();
|
||||
|
||||
if (OnSubmit != null)
|
||||
await OnSubmit.Invoke();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is DisplayException displayException)
|
||||
{
|
||||
ErrorMessages.Add(displayException.Message);
|
||||
}
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
@typeparam TField
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@inherits InputBase<TField>
|
||||
|
||||
<select class="form-select" @bind="Binding">
|
||||
@if (CanBeNull)
|
||||
{
|
||||
<option value="-1">---</option>
|
||||
}
|
||||
@foreach (var item in Items)
|
||||
{
|
||||
<option value="@(item!.GetHashCode())">@(DisplayField(item))</option>
|
||||
}
|
||||
</select>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public IEnumerable<TField> Items { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<TField, string> DisplayField { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<Task>? OnChange { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool CanBeNull { get; set; } = false;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override string? FormatValueAsString(TField? value)
|
||||
{
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
return DisplayField.Invoke(value);
|
||||
}
|
||||
|
||||
protected override bool TryParseValueFromString(string? value, out TField result, out string? validationErrorMessage)
|
||||
{
|
||||
validationErrorMessage = "";
|
||||
result = default(TField)!;
|
||||
return false;
|
||||
}
|
||||
|
||||
private int Binding
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Value == null)
|
||||
return -1;
|
||||
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
set
|
||||
{
|
||||
var i = Items.FirstOrDefault(x => x!.GetHashCode() == value);
|
||||
|
||||
if(i == null && !CanBeNull)
|
||||
return;
|
||||
|
||||
Value = i;
|
||||
ValueChanged.InvokeAsync(i);
|
||||
OnChange?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
@inject IJSRuntime JsRuntime
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Ganss.Xss
|
||||
@using Moonlight.Core.Helpers
|
||||
@using MoonCore.Helpers
|
||||
@using Moonlight.Core.Services
|
||||
@inherits InputBase<string>
|
||||
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
@if (!Working)
|
||||
{
|
||||
<button class="btn @(CssClasses)" @onclick="Do">
|
||||
@Text
|
||||
@ChildContent
|
||||
</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button class="btn @(CssClasses) disabled" disabled="">
|
||||
<span class="spinner-border spinner-border-sm align-middle me-2"></span>
|
||||
@WorkingText
|
||||
</button>
|
||||
}
|
||||
|
||||
@code
|
||||
{
|
||||
private bool Working { get; set; } = false;
|
||||
|
||||
[Parameter]
|
||||
public string CssClasses { get; set; } = "btn-primary";
|
||||
|
||||
[Parameter]
|
||||
public string Text { get; set; } = "";
|
||||
|
||||
[Parameter]
|
||||
public string WorkingText { get; set; } = "";
|
||||
|
||||
[Parameter]
|
||||
public Func<Task>? OnClick { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment ChildContent { get; set; }
|
||||
|
||||
private async Task Do()
|
||||
{
|
||||
Working = true;
|
||||
StateHasChanged();
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
if (OnClick != null)
|
||||
await OnClick.Invoke();
|
||||
|
||||
Working = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
@if (Loaded)
|
||||
{
|
||||
@ChildContent
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ShowAsCard)
|
||||
{
|
||||
<div class="d-flex flex-column flex-center">
|
||||
<div class="card card-body">
|
||||
<div class="d-flex justify-content-center py-4">
|
||||
<span class="fs-1 spinner-border spinner-border-lg align-middle me-2"></span>
|
||||
<span class="ms-3 fs-5 align-middle">@(Text)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="d-flex justify-content-center py-4">
|
||||
<span class="fs-1 spinner-border spinner-border-lg align-middle me-2"></span>
|
||||
<span class="ms-3 fs-5 align-middle">@(Text)</span>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public RenderFragment ChildContent { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool ShowAsCard { get; set; } = false;
|
||||
|
||||
[Parameter]
|
||||
public Func<LazyLoader, Task> Load { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string Text { get; set; } = "";
|
||||
|
||||
private bool Loaded = false;
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
await Load.Invoke(this);
|
||||
Loaded = true;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SetText(string text)
|
||||
{
|
||||
Text = text;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
public async Task Reload()
|
||||
{
|
||||
Loaded = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
await Load.Invoke(this);
|
||||
Loaded = true;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
@using Moonlight.Core.Services
|
||||
@using Moonlight.Core.Services.Interop
|
||||
@using Moonlight.Core.UI.Layouts
|
||||
@using Moonlight.Core.Configuration
|
||||
@using MoonCore.Services
|
||||
@using MoonCoreUI.Services
|
||||
|
||||
@inject IdentityService IdentityService
|
||||
@inject ConfigService ConfigService
|
||||
@inject ConfigService<ConfigV1> ConfigService
|
||||
@inject CookieService CookieService
|
||||
|
||||
<div class="app-header">
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
@using Moonlight.Core.Services.Interop
|
||||
@inject ModalService ModalService
|
||||
|
||||
<div class="modal fade" id="modal@(Id)" tabindex="-1">
|
||||
<div class="modal-dialog @(CssClasses)">
|
||||
<div class="modal-content">
|
||||
@if (ShouldShow)
|
||||
{
|
||||
@ChildContent
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public string CssClasses { get; set; } = "";
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment ChildContent { get; set; }
|
||||
|
||||
private int Id;
|
||||
private bool ShouldShow = false;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Id = GetHashCode();
|
||||
}
|
||||
|
||||
public async Task Show(bool focus = true) // Focus can be specified to fix issues with other components
|
||||
{
|
||||
ShouldShow = true;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
await ModalService.Show("modal" + Id, focus);
|
||||
}
|
||||
|
||||
public async Task Hide()
|
||||
{
|
||||
await ModalService.Hide("modal" + Id);
|
||||
|
||||
ShouldShow = false;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
@using System.Diagnostics
|
||||
@using Moonlight.Core.Exceptions
|
||||
@using Moonlight.Core.Helpers
|
||||
@using MoonCore.Exceptions
|
||||
@using MoonCore.Helpers
|
||||
@using Moonlight.Core.Models.Enums
|
||||
@using Moonlight.Core.Services
|
||||
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
@using Moonlight.Core.Models.Abstractions
|
||||
@using Moonlight.Core.Models.Enums
|
||||
@using Moonlight.Core.Services
|
||||
@using Moonlight.Core.Services.Interop
|
||||
@using Moonlight.Core.Services.Utils
|
||||
@using Moonlight.Core.Event
|
||||
@using Moonlight.Core.UI.Components.Auth
|
||||
@using Moonlight.Features.Advertisement.Services
|
||||
@using Moonlight.Features.Advertisement.UI.Components
|
||||
@using MoonCoreUI.Services
|
||||
@using MoonCore.Services
|
||||
@using Moonlight.Core.Configuration
|
||||
|
||||
@inherits LayoutComponentBase
|
||||
@implements IDisposable
|
||||
|
||||
@inject CookieService CookieService
|
||||
@inject ConfigService ConfigService
|
||||
@inject ConfigService<ConfigV1> ConfigService
|
||||
@inject IdentityService IdentityService
|
||||
@inject SessionService SessionService
|
||||
@inject NavigationManager Navigation
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
@page "/account"
|
||||
@using Moonlight.Core.Models.Forms
|
||||
@using Moonlight.Core.Helpers
|
||||
@using Moonlight.Core.Models.Forms.Auth
|
||||
@using Moonlight.Core.Services
|
||||
@using Moonlight.Core.Services.Interop
|
||||
@using Moonlight.Core.Services.Users
|
||||
@using MoonCoreUI.Services
|
||||
@using MoonCore.Helpers
|
||||
|
||||
@inject IdentityService IdentityService
|
||||
@inject UserService UserService
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
@page "/account/payments"
|
||||
@using BlazorTable
|
||||
@using Moonlight.Core.Exceptions
|
||||
@using Moonlight.Core.Helpers
|
||||
@using Moonlight.Core.Models.Abstractions
|
||||
@using MoonCore.Exceptions
|
||||
@using MoonCore.Helpers
|
||||
@using MoonCore.Services
|
||||
@using MoonCoreUI.Services
|
||||
@using Moonlight.Core.Configuration
|
||||
@using Moonlight.Core.Services
|
||||
@using Moonlight.Core.Services.Interop
|
||||
@using Moonlight.Features.StoreSystem.Entities
|
||||
@using Moonlight.Features.StoreSystem.Models.Abstractions
|
||||
@using Moonlight.Features.StoreSystem.Services
|
||||
|
||||
@inject IdentityService IdentityService
|
||||
@inject ConfigService ConfigService
|
||||
@inject ConfigService<ConfigV1> ConfigService
|
||||
@inject StoreService StoreService
|
||||
@inject ToastService ToastService
|
||||
@inject NavigationManager Navigation
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
@using QRCoder
|
||||
@using Moonlight.Core.Models.Forms.Auth
|
||||
@using Moonlight.Core.Services
|
||||
@using Moonlight.Core.Services.Interop
|
||||
@using Moonlight.Core.Services.Users
|
||||
@using Moonlight.Core.Exceptions
|
||||
@using Moonlight.Core.Models.Enums
|
||||
@using MoonCore.Exceptions
|
||||
@using MoonCoreUI.Services
|
||||
|
||||
@inject IdentityService IdentityService
|
||||
@inject UserService UserService
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@using Moonlight.Core.Extensions.Attributes
|
||||
@using Moonlight.Core.Models.Enums
|
||||
@using Moonlight.Core.Services
|
||||
@using Moonlight.Core.Services.Interop
|
||||
@using MoonCoreUI.Services
|
||||
|
||||
@attribute [RequirePermission(Permission.AdminRoot)]
|
||||
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
@page "/admin/sys/settings"
|
||||
@using System.Reflection
|
||||
@using MoonCore.Services
|
||||
@using MoonCoreUI.Helpers
|
||||
@using MoonCoreUI.Services
|
||||
@using Moonlight.Core.Configuration
|
||||
@using Moonlight.Core.Extensions.Attributes
|
||||
@using Moonlight.Core.Helpers
|
||||
@using Moonlight.Core.Models.Enums
|
||||
@using Moonlight.Core.Services
|
||||
@using Moonlight.Core.Services.Interop
|
||||
|
||||
@attribute [RequirePermission(Permission.AdminRoot)]
|
||||
|
||||
@inject ConfigService ConfigService
|
||||
@inject ConfigService<ConfigV1> ConfigService
|
||||
@inject ToastService ToastService
|
||||
|
||||
<AdminSysNavigation Index="1" />
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
@page "/admin/users"
|
||||
@using BlazorTable
|
||||
@using MoonCore.Abstractions
|
||||
@using MoonCore.Helpers
|
||||
@using Moonlight.Core.Database.Entities
|
||||
@using Moonlight.Core.Extensions.Attributes
|
||||
@using Moonlight.Core.Helpers
|
||||
@using Moonlight.Core.Models.Enums
|
||||
@using Moonlight.Core.Repositories
|
||||
|
||||
@attribute [RequirePermission(Permission.AdminUsers)]
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
@page "/admin/users/sessions"
|
||||
@using BlazorTable
|
||||
@using MoonCore.Helpers
|
||||
@using Moonlight.Core.Extensions.Attributes
|
||||
@using Moonlight.Core.Helpers
|
||||
@using Moonlight.Core.Models.Abstractions
|
||||
@using Moonlight.Core.Models.Enums
|
||||
@using Moonlight.Core.Services
|
||||
|
|
|
@ -2,14 +2,16 @@
|
|||
@using Microsoft.EntityFrameworkCore
|
||||
@using Mappy.Net
|
||||
@using BlazorTable
|
||||
@using MoonCore.Abstractions
|
||||
@using MoonCore.Helpers
|
||||
@using MoonCore.Services
|
||||
@using MoonCoreUI.Services
|
||||
@using Moonlight.Core.Configuration
|
||||
@using Moonlight.Core.Database.Entities
|
||||
@using Moonlight.Core.Extensions.Attributes
|
||||
@using Moonlight.Core.Helpers
|
||||
@using Moonlight.Core.Models.Enums
|
||||
@using Moonlight.Core.Models.Forms.Admin.Users
|
||||
@using Moonlight.Core.Repositories
|
||||
@using Moonlight.Core.Services
|
||||
@using Moonlight.Core.Services.Interop
|
||||
@using Moonlight.Core.Services.Users
|
||||
@using Moonlight.Features.ServiceManagement.Entities
|
||||
@using Moonlight.Features.StoreSystem.Entities
|
||||
|
@ -21,7 +23,7 @@
|
|||
@inject SessionService SessionService
|
||||
@inject UserService UserService
|
||||
@inject ToastService ToastService
|
||||
@inject ConfigService ConfigService
|
||||
@inject ConfigService<ConfigV1> ConfigService
|
||||
@inject NavigationManager Navigation
|
||||
|
||||
<LazyLoader Load="Load" ShowAsCard="true">
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
using Microsoft.JSInterop;
|
||||
using Moonlight.Core.Helpers;
|
||||
using MoonCore.Attributes;
|
||||
using MoonCore.Helpers;
|
||||
|
||||
namespace Moonlight.Features.Advertisement.Services;
|
||||
|
||||
[Scoped]
|
||||
public class AdBlockService
|
||||
{
|
||||
private readonly IJSRuntime JsRuntime;
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
using System.Text.RegularExpressions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MoonCore.Abstractions;
|
||||
using MoonCore.Attributes;
|
||||
using MoonCore.Exceptions;
|
||||
using Moonlight.Core.Database.Entities;
|
||||
using Moonlight.Core.Event;
|
||||
using Moonlight.Core.Exceptions;
|
||||
using Moonlight.Core.Extensions;
|
||||
using Moonlight.Core.Repositories;
|
||||
using Moonlight.Features.Community.Entities;
|
||||
using Moonlight.Features.Community.Entities.Enums;
|
||||
|
||||
namespace Moonlight.Features.Community.Services;
|
||||
|
||||
[Scoped]
|
||||
public class PostService
|
||||
{
|
||||
private readonly Repository<Post> PostRepository;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
@using Moonlight.Core.Services
|
||||
@using Moonlight.Core.Services.Interop
|
||||
@using Moonlight.Features.Community.Entities.Enums
|
||||
@using Moonlight.Features.Community.Models.Forms
|
||||
@using Moonlight.Features.Community.Services
|
||||
@using MoonCoreUI.Services
|
||||
|
||||
@inject PostService PostService
|
||||
@inject IdentityService IdentityService
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
@using Ganss.Xss
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Moonlight.Core.Helpers
|
||||
@using MoonCore.Abstractions
|
||||
@using MoonCore.Helpers
|
||||
@using MoonCoreUI.Services
|
||||
@using Moonlight.Core.Models.Enums
|
||||
@using Moonlight.Core.Repositories
|
||||
|
||||
@using Moonlight.Core.Services
|
||||
@using Moonlight.Core.Services.Interop
|
||||
@using Moonlight.Features.Community.Entities
|
||||
@using Moonlight.Features.Community.Services
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
@page "/admin/community/filter"
|
||||
|
||||
@using BlazorTable
|
||||
@using MoonCore.Abstractions
|
||||
@using Moonlight.Core.Extensions.Attributes
|
||||
@using Moonlight.Core.Models.Enums
|
||||
@using Moonlight.Core.Repositories
|
||||
|
||||
@using Moonlight.Features.Community.Entities
|
||||
@using Moonlight.Features.Community.Models.Forms.Admin
|
||||
@using Moonlight.Features.Community.UI.Components
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
@page "/community/events"
|
||||
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using MoonCore.Abstractions
|
||||
@using Moonlight.Core.Models.Enums
|
||||
@using Moonlight.Core.Repositories
|
||||
|
||||
@using Moonlight.Core.Services
|
||||
@using Moonlight.Features.Community.Entities
|
||||
@using Moonlight.Features.Community.Entities.Enums
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
@page "/community"
|
||||
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using MoonCore.Abstractions
|
||||
@using Moonlight.Core.Models.Enums
|
||||
@using Moonlight.Core.Repositories
|
||||
|
||||
@using Moonlight.Core.Services
|
||||
@using Moonlight.Features.Community.Entities
|
||||
@using Moonlight.Features.Community.Entities.Enums
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
@page "/community/projects"
|
||||
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Moonlight.Core.Repositories
|
||||
@using MoonCore.Abstractions
|
||||
|
||||
@using Moonlight.Features.Community.Entities
|
||||
@using Moonlight.Features.Community.Entities.Enums
|
||||
@using Moonlight.Features.Community.UI.Components
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Moonlight.Core.Exceptions;
|
||||
using Moonlight.Core.Helpers;
|
||||
using Moonlight.Core.Repositories;
|
||||
using MoonCore.Abstractions;
|
||||
using MoonCore.Exceptions;
|
||||
using MoonCore.Helpers;
|
||||
using Moonlight.Features.Servers.Entities;
|
||||
using Moonlight.Features.Servers.Models.Enums;
|
||||
using Moonlight.Features.Servers.Services;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using Moonlight.Core.Helpers;
|
||||
|
||||
using Moonlight.Features.Servers.UI.Layouts;
|
||||
using Moonlight.Features.ServiceManagement.Models.Abstractions;
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System.Net.WebSockets;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Moonlight.Core.Helpers;
|
||||
using MoonCore.Helpers;
|
||||
|
||||
using Moonlight.Features.Servers.Entities;
|
||||
using Moonlight.Features.Servers.Extensions.Attributes;
|
||||
using Moonlight.Features.Servers.Models.Packets;
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Moonlight.Core.Helpers;
|
||||
using Moonlight.Core.Repositories;
|
||||
using MoonCore.Abstractions;
|
||||
using MoonCore.Helpers;
|
||||
|
||||
|
||||
using Moonlight.Features.Servers.Entities;
|
||||
using Moonlight.Features.Servers.Extensions;
|
||||
using Moonlight.Features.Servers.Extensions.Attributes;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Moonlight.Core.Repositories;
|
||||
using MoonCore.Abstractions;
|
||||
|
||||
using Moonlight.Features.Servers.Entities;
|
||||
using Moonlight.Features.Servers.Extensions.Attributes;
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
using MoonCore.Attributes;
|
||||
using Moonlight.Features.Servers.Helpers;
|
||||
using Moonlight.Features.Servers.Models.Abstractions;
|
||||
|
||||
namespace Moonlight.Features.Servers.Services;
|
||||
|
||||
[Singleton]
|
||||
public class NodeService
|
||||
{
|
||||
public readonly MetaCache<NodeMeta> Meta = new();
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Moonlight.Core.Helpers;
|
||||
using Moonlight.Core.Repositories;
|
||||
using MoonCore.Abstractions;
|
||||
using MoonCore.Attributes;
|
||||
using MoonCore.Helpers;
|
||||
|
||||
|
||||
using Moonlight.Features.Servers.Entities;
|
||||
using Moonlight.Features.Servers.Exceptions;
|
||||
using Moonlight.Features.Servers.Helpers;
|
||||
|
@ -9,6 +12,7 @@ using Moonlight.Features.Servers.Models.Enums;
|
|||
|
||||
namespace Moonlight.Features.Servers.Services;
|
||||
|
||||
[Singleton]
|
||||
public class ServerService
|
||||
{
|
||||
public readonly MetaCache<ServerMeta> Meta = new();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
@using Newtonsoft.Json
|
||||
@using Mappy.Net
|
||||
@using Moonlight.Core.Helpers
|
||||
@using MoonCore.Helpers
|
||||
|
||||
@using Moonlight.Features.Servers.Models
|
||||
@using Moonlight.Features.Servers.Models.Forms.Admin.Images
|
||||
@using Moonlight.Features.Servers.Models.Forms.Admin.Images.Parsing
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
@using XtermBlazor
|
||||
@using Moonlight.Core.Services.Interop
|
||||
|
||||
@using MoonCoreUI.Services
|
||||
|
||||
@inject ClipboardService ClipboardService
|
||||
@inject ToastService ToastService
|
||||
|
|
|
@ -2,12 +2,15 @@
|
|||
@using Moonlight.Features.ServiceManagement.Models.Abstractions
|
||||
@using Moonlight.Features.Servers.Entities
|
||||
@using Moonlight.Features.Servers.Services
|
||||
@using Moonlight.Core.Repositories
|
||||
@using Moonlight.Core.Services.Interop
|
||||
|
||||
|
||||
@using Moonlight.Features.Servers.Models.Abstractions
|
||||
@using Moonlight.Features.Servers.Models.Enums
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Moonlight.Core.Helpers
|
||||
@using MoonCore.Abstractions
|
||||
@using MoonCore.Helpers
|
||||
@using MoonCoreUI.Services
|
||||
|
||||
@using Moonlight.Features.Servers.UI.Components
|
||||
|
||||
@inject Repository<Server> ServerRepository
|
||||
|
|
|
@ -2,9 +2,12 @@
|
|||
|
||||
@using BlazorTable
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Moonlight.Core.Exceptions
|
||||
@using Moonlight.Core.Repositories
|
||||
@using Moonlight.Core.Services.Interop
|
||||
@using MoonCore.Abstractions
|
||||
@using MoonCore.Exceptions
|
||||
@using MoonCoreUI.Services
|
||||
|
||||
|
||||
|
||||
@using Moonlight.Features.Servers.Entities
|
||||
@using Moonlight.Features.Servers.UI.Components
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
@page "/admin/servers/images/new"
|
||||
|
||||
@using Moonlight.Features.Servers.Models.Forms.Admin.Images
|
||||
@using Moonlight.Core.Services.Interop
|
||||
@using Moonlight.Core.Repositories
|
||||
|
||||
|
||||
@using Moonlight.Features.Servers.Entities
|
||||
@using Mappy.Net
|
||||
@using MoonCore.Abstractions
|
||||
@using MoonCoreUI.Services
|
||||
|
||||
@inject NavigationManager Navigation
|
||||
@inject ToastService ToastService
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
@page "/admin/servers/images/{Id:int}/configparsing"
|
||||
|
||||
@using Moonlight.Features.Servers.Entities
|
||||
@using Moonlight.Core.Repositories
|
||||
@using Moonlight.Core.Services.Interop
|
||||
|
||||
|
||||
@using Moonlight.Features.Servers.UI.Components
|
||||
@using Moonlight.Core.Exceptions
|
||||
|
||||
@using MoonCore.Exceptions
|
||||
@using MoonCore.Abstractions
|
||||
@using MoonCoreUI.Services
|
||||
|
||||
@inject Repository<ServerImage> ImageRepository
|
||||
@inject ToastService ToastService
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
@page "/admin/servers/images/{Id:int}/details"
|
||||
|
||||
@using Moonlight.Features.Servers.Entities
|
||||
@using Moonlight.Core.Repositories
|
||||
|
||||
@using Moonlight.Features.Servers.Models.Forms.Admin.Images
|
||||
@using Mappy.Net
|
||||
@using Moonlight.Core.Services.Interop
|
||||
@using MoonCore.Abstractions
|
||||
@using MoonCoreUI.Services
|
||||
|
||||
@using Moonlight.Features.Servers.UI.Components
|
||||
|
||||
@inject Repository<ServerImage> ImageRepository
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
@page "/admin/servers/images/{Id:int}/dockerimages"
|
||||
|
||||
@using Moonlight.Features.Servers.Entities
|
||||
@using Moonlight.Core.Repositories
|
||||
|
||||
@using Moonlight.Features.Servers.Models.Forms.Admin.Images
|
||||
@using Moonlight.Features.Servers.UI.Components
|
||||
@using BlazorTable
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Moonlight.Core.Services.Interop
|
||||
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using MoonCore.Abstractions
|
||||
@using MoonCoreUI.Services
|
||||
|
||||
@inject Repository<ServerImage> ImageRepository
|
||||
@inject ToastService ToastService
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
@page "/admin/servers/images/{Id:int}"
|
||||
|
||||
@using Moonlight.Features.Servers.Entities
|
||||
@using Moonlight.Core.Repositories
|
||||
|
||||
@using Moonlight.Features.Servers.Models.Forms.Admin.Images
|
||||
@using Mappy.Net
|
||||
@using Moonlight.Core.Services.Interop
|
||||
@using MoonCore.Abstractions
|
||||
@using MoonCoreUI.Services
|
||||
|
||||
@using Moonlight.Features.Servers.UI.Components
|
||||
|
||||
@inject Repository<ServerImage> ImageRepository
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
@page "/admin/servers/images/{Id:int}/installation"
|
||||
|
||||
@using Moonlight.Features.Servers.Entities
|
||||
@using Moonlight.Core.Repositories
|
||||
|
||||
@using Moonlight.Features.Servers.Models.Forms.Admin.Images
|
||||
@using Mappy.Net
|
||||
@using Moonlight.Core.Services.Interop
|
||||
@using MoonCore.Abstractions
|
||||
@using MoonCoreUI.Services
|
||||
|
||||
@using Moonlight.Features.Servers.UI.Components
|
||||
|
||||
@inject Repository<ServerImage> ImageRepository
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
@page "/admin/servers/images/{Id:int}/variables"
|
||||
|
||||
@using Moonlight.Features.Servers.Entities
|
||||
@using Moonlight.Core.Repositories
|
||||
|
||||
@using Moonlight.Features.Servers.UI.Components
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using BlazorTable
|
||||
@using MoonCore.Abstractions
|
||||
@using Moonlight.Features.Servers.Models.Forms.Admin.Images
|
||||
|
||||
@inject Repository<ServerImage> ImageRepository
|
||||
|
|
|
@ -2,14 +2,17 @@
|
|||
|
||||
@using Moonlight.Core.Extensions.Attributes
|
||||
@using Moonlight.Core.Models.Enums
|
||||
@using Moonlight.Core.Repositories
|
||||
|
||||
@using Moonlight.Features.Servers.UI.Components
|
||||
@using Moonlight.Features.Servers.Entities
|
||||
@using Moonlight.Features.Servers.Models.Forms.Admin
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using Moonlight.Core.Exceptions
|
||||
@using Moonlight.Core.Helpers
|
||||
|
||||
|
||||
@using BlazorTable
|
||||
@using MoonCore.Abstractions
|
||||
@using MoonCore.Exceptions
|
||||
@using MoonCore.Helpers
|
||||
|
||||
@attribute [RequirePermission(Permission.AdminServers)]
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using MoonCoreUI.Helpers;
|
||||
using Moonlight.Core.Database.Entities;
|
||||
using Moonlight.Core.Helpers;
|
||||
|
||||
using Moonlight.Features.ServiceManagement.Entities;
|
||||
using Moonlight.Features.StoreSystem.Entities;
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue