Added some helpers, a new logger and log migration and modified the router
This commit is contained in:
parent
69b50275cd
commit
afb3a7f3a3
252
Moonlight/App/Helpers/Formatter.cs
Normal file
252
Moonlight/App/Helpers/Formatter.cs
Normal file
|
@ -0,0 +1,252 @@
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace Moonlight.App.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", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||||
|
|
||||||
|
foreach (var line in arr)
|
||||||
|
{
|
||||||
|
builder.AddContent(i, line);
|
||||||
|
if (i++ != arr.Length - 1)
|
||||||
|
{
|
||||||
|
builder.AddMarkupContent(i, "<br/>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
172
Moonlight/App/Helpers/HashHelper.cs
Normal file
172
Moonlight/App/Helpers/HashHelper.cs
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
|
||||||
|
|
||||||
|
namespace Moonlight.App.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);
|
||||||
|
}
|
||||||
|
}
|
11
Moonlight/App/Helpers/LogMigrator/LogMigrateProvider.cs
Normal file
11
Moonlight/App/Helpers/LogMigrator/LogMigrateProvider.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Moonlight.App.Helpers.LogMigrator;
|
||||||
|
|
||||||
|
public class LogMigrateProvider : ILoggerProvider
|
||||||
|
{
|
||||||
|
public void Dispose() {}
|
||||||
|
|
||||||
|
public ILogger CreateLogger(string categoryName)
|
||||||
|
{
|
||||||
|
return new MigrateLogger();
|
||||||
|
}
|
||||||
|
}
|
53
Moonlight/App/Helpers/LogMigrator/MigrateLogger.cs
Normal file
53
Moonlight/App/Helpers/LogMigrator/MigrateLogger.cs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
namespace Moonlight.App.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
112
Moonlight/App/Helpers/Logger.cs
Normal file
112
Moonlight/App/Helpers/Logger.cs
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Reflection;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
|
namespace Moonlight.App.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;
|
||||||
|
}
|
||||||
|
}
|
34
Moonlight/App/Helpers/PathBuilder.cs
Normal file
34
Moonlight/App/Helpers/PathBuilder.cs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
namespace Moonlight.App.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}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
<Router AppAssembly="@typeof(BlazorApp).Assembly">
|
<Router AppAssembly="@typeof(BlazorApp).Assembly">
|
||||||
<Found Context="routeData">
|
<Found Context="routeData">
|
||||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
|
<CascadingValue TValue="Type" Name="TargetPageType" Value="routeData.PageType">
|
||||||
|
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
|
||||||
|
</CascadingValue>
|
||||||
</Found>
|
</Found>
|
||||||
<NotFound>
|
<NotFound>
|
||||||
<PageTitle>Not found</PageTitle>
|
<PageTitle>Not found</PageTitle>
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
<Folder Include="App\Database\Enums\" />
|
<Folder Include="App\Database\Enums\" />
|
||||||
<Folder Include="App\Database\Migrations\" />
|
<Folder Include="App\Database\Migrations\" />
|
||||||
<Folder Include="App\Exceptions\" />
|
<Folder Include="App\Exceptions\" />
|
||||||
<Folder Include="App\Helpers\" />
|
|
||||||
<Folder Include="App\Http\" />
|
<Folder Include="App\Http\" />
|
||||||
<Folder Include="App\Models\Abstractions\" />
|
<Folder Include="App\Models\Abstractions\" />
|
||||||
<Folder Include="App\Models\Enums\" />
|
<Folder Include="App\Models\Enums\" />
|
||||||
|
@ -28,4 +27,10 @@
|
||||||
<Folder Include="App\Services\" />
|
<Folder Include="App\Services\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
<PackageReference Include="Serilog" Version="3.1.0-dev-02078" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.0-dev-00923" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -1,10 +1,29 @@
|
||||||
using Microsoft.AspNetCore.Components;
|
|
||||||
using Microsoft.AspNetCore.Components.Web;
|
|
||||||
using Moonlight.App.Extensions;
|
using Moonlight.App.Extensions;
|
||||||
|
using Moonlight.App.Helpers;
|
||||||
|
using Moonlight.App.Helpers.LogMigrator;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
|
Directory.CreateDirectory(PathBuilder.Dir("storage"));
|
||||||
|
Directory.CreateDirectory(PathBuilder.Dir("storage", "logs"));
|
||||||
|
|
||||||
|
var logConfig = new LoggerConfiguration();
|
||||||
|
|
||||||
|
logConfig = logConfig.Enrich.FromLogContext()
|
||||||
|
.WriteTo.Console(
|
||||||
|
outputTemplate:
|
||||||
|
"{Timestamp:HH:mm:ss} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}");
|
||||||
|
|
||||||
|
Log.Logger = logConfig.CreateLogger();
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
builder.Services.AddRazorPages();
|
builder.Services.AddRazorPages();
|
||||||
builder.Services.AddServerSideBlazor();
|
builder.Services.AddServerSideBlazor();
|
||||||
|
builder.Services.AddHttpContextAccessor();
|
||||||
|
builder.Services.AddControllers();
|
||||||
|
|
||||||
|
builder.Logging.ClearProviders();
|
||||||
|
builder.Logging.AddProvider(new LogMigrateProvider());
|
||||||
|
|
||||||
var config =
|
var config =
|
||||||
new ConfigurationBuilder().AddJsonString(
|
new ConfigurationBuilder().AddJsonString(
|
||||||
|
@ -13,19 +32,11 @@ builder.Logging.AddConfiguration(config.Build());
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
if (!app.Environment.IsDevelopment())
|
|
||||||
{
|
|
||||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
|
||||||
app.UseHsts();
|
|
||||||
}
|
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
|
||||||
|
|
||||||
app.UseStaticFiles();
|
app.UseStaticFiles();
|
||||||
|
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
|
||||||
app.MapBlazorHub();
|
app.MapBlazorHub();
|
||||||
app.MapFallbackToPage("/_Host");
|
app.MapFallbackToPage("/_Host");
|
||||||
|
app.MapControllers();
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
|
@ -2,4 +2,5 @@
|
||||||
@using Microsoft.AspNetCore.Components.Web
|
@using Microsoft.AspNetCore.Components.Web
|
||||||
@using Microsoft.JSInterop
|
@using Microsoft.JSInterop
|
||||||
@using Moonlight
|
@using Moonlight
|
||||||
|
@using Moonlight.App.Helpers
|
||||||
@using Moonlight.Shared.Components.Partials
|
@using Moonlight.Shared.Components.Partials
|
Loading…
Reference in a new issue