mirror of
https://github.com/techgarage-ir/MTWireGuard.git
synced 2025-08-28 05:53:50 +02:00
Added Serilog to manage logs
Handle required fields while starting
This commit is contained in:
parent
6fbfaa1007
commit
b128154c60
17 changed files with 749 additions and 154 deletions
|
@ -1,15 +1,12 @@
|
|||
using Hangfire;
|
||||
using Hangfire.Storage.SQLite;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using MTWireGuard.Application.Mapper;
|
||||
using MTWireGuard.Application.Repositories;
|
||||
using MTWireGuard.Application.Services;
|
||||
using Serilog;
|
||||
using Serilog.Ui.SqliteDataProvider;
|
||||
using Serilog.Ui.Web;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace MTWireGuard.Application
|
||||
{
|
||||
|
@ -17,16 +14,15 @@ namespace MTWireGuard.Application
|
|||
{
|
||||
public static void AddApplicationServices(this IServiceCollection services)
|
||||
{
|
||||
// Add Serilog
|
||||
services.AddLogging(loggingBuilder =>
|
||||
{
|
||||
loggingBuilder.AddSerilog(Helper.LoggerConfiguration(), dispose: true);
|
||||
});
|
||||
|
||||
// Add DBContext
|
||||
services.AddDbContext<DBContext>();
|
||||
|
||||
// Add HangFire
|
||||
services.AddHangfire(config =>
|
||||
{
|
||||
config.UseSQLiteStorage(Path.Join(AppDomain.CurrentDomain.BaseDirectory, "MikrotikWireguard.db"));
|
||||
});
|
||||
services.AddHangfireServer();
|
||||
|
||||
// Auto Mapper Configurations
|
||||
services.AddSingleton<PeerMapping>();
|
||||
services.AddSingleton<ServerMapping>();
|
||||
|
@ -79,6 +75,9 @@ namespace MTWireGuard.Application
|
|||
o.Conventions.AllowAnonymousToPage("/Login");
|
||||
});
|
||||
|
||||
// Add HttpContextAccessor
|
||||
services.AddHttpContextAccessor();
|
||||
|
||||
// Add Session
|
||||
services.AddDistributedMemoryCache();
|
||||
|
||||
|
@ -91,6 +90,12 @@ namespace MTWireGuard.Application
|
|||
|
||||
// Add CORS
|
||||
services.AddCors();
|
||||
|
||||
// Add SerilogUI
|
||||
services.AddSerilogUi(options =>
|
||||
{
|
||||
options.UseSqliteServer($"Data Source={Helper.GetLogPath("logs.db")}", "Logs");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
30
Application/ClientIdEnricher.cs
Normal file
30
Application/ClientIdEnricher.cs
Normal file
|
@ -0,0 +1,30 @@
|
|||
using Serilog.Configuration;
|
||||
using Serilog;
|
||||
using Serilog.Core;
|
||||
using Serilog.Events;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MTWireGuard.Application
|
||||
{
|
||||
public class ClientIdEnricher(string clientId) : ILogEventEnricher
|
||||
{
|
||||
private readonly string _clientId = clientId;
|
||||
|
||||
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
|
||||
{
|
||||
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("ClientId", _clientId));
|
||||
}
|
||||
}
|
||||
|
||||
public static class LoggingExtensions
|
||||
{
|
||||
public static LoggerConfiguration WithClientId(this LoggerEnrichmentConfiguration enrichmentConfiguration, string clientId)
|
||||
{
|
||||
return enrichmentConfiguration.With(new ClientIdEnricher(clientId));
|
||||
}
|
||||
}
|
||||
}
|
59
Application/ExceptionHandler.cs
Normal file
59
Application/ExceptionHandler.cs
Normal file
|
@ -0,0 +1,59 @@
|
|||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MTWireGuard.Application
|
||||
{
|
||||
public class ExceptionHandler(Serilog.ILogger logger, IHttpContextAccessor contextAccessor) : IExceptionHandler
|
||||
{
|
||||
private readonly Serilog.ILogger logger = logger;
|
||||
private readonly IHttpContextAccessor contextAccessor = contextAccessor;
|
||||
|
||||
public ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
string exceptionType = exception.GetType().Name,
|
||||
message = exception.Message,
|
||||
stackTrace = exception.StackTrace,
|
||||
//details = JsonConvert.SerializeObject(exception)!;
|
||||
details = exception.Source;
|
||||
|
||||
ExceptionHandlerContext.Message = message;
|
||||
ExceptionHandlerContext.StackTrace = stackTrace;
|
||||
ExceptionHandlerContext.Details = details;
|
||||
|
||||
if (SetupValidator.IsValid)
|
||||
{
|
||||
logger.Error(exception, "Unhandled error");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error("Error in configuration: {Title}, {Description}", SetupValidator.Title, SetupValidator.Description);
|
||||
}
|
||||
|
||||
contextAccessor.HttpContext.Response.Redirect("/Error", true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Fatal(ex, "Error In Exception Handler");
|
||||
}
|
||||
return ValueTask.FromResult(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExceptionHandlerContext
|
||||
{
|
||||
public static string Message { get; internal set; }
|
||||
public static string StackTrace { get; internal set; }
|
||||
public static string Details { get; internal set; }
|
||||
}
|
||||
}
|
|
@ -1,19 +1,23 @@
|
|||
using AutoMapper;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MTWireGuard.Application.MinimalAPI;
|
||||
using MTWireGuard.Application.Models;
|
||||
using MTWireGuard.Application.Repositories;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using Serilog.Exceptions;
|
||||
using Serilog.Exceptions.Core;
|
||||
using Serilog.Filters;
|
||||
using System.IO.Compression;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace MTWireGuard.Application
|
||||
{
|
||||
|
@ -33,6 +37,11 @@ namespace MTWireGuard.Application
|
|||
return $"/tool fetch mode=http url=\"{apiURL}\" http-method=post check-certificate=no http-data=([/interface/wireguard/peers/print show-ids proplist=rx,tx as-value]);";
|
||||
}
|
||||
|
||||
public static string UserExpirationScript(string userID)
|
||||
{
|
||||
return $"/interface/wireguard/peers/disable {userID}";
|
||||
}
|
||||
|
||||
public static int ParseEntityID(string entityID)
|
||||
{
|
||||
return Convert.ToInt32(entityID[1..], 16);
|
||||
|
@ -70,6 +79,11 @@ namespace MTWireGuard.Application
|
|||
SizeSuffixes[mag]);
|
||||
}
|
||||
|
||||
public static long GigabyteToByte(int gigabyte)
|
||||
{
|
||||
return Convert.ToInt64(gigabyte * (1024L * 1024 * 1024));
|
||||
}
|
||||
|
||||
#region API Section
|
||||
public static List<UsageObject> ParseTrafficUsage(string input)
|
||||
{
|
||||
|
@ -118,17 +132,20 @@ namespace MTWireGuard.Application
|
|||
}
|
||||
#endregion
|
||||
|
||||
public static async void HandleUserTraffics(List<DataUsage> updates, DBContext dbContext, IMikrotikRepository API)
|
||||
public static async void HandleUserTraffics(List<DataUsage> updates, DBContext dbContext, IMikrotikRepository API, ILogger logger)
|
||||
{
|
||||
var dataUsages = await dbContext.DataUsages.ToListAsync();
|
||||
var existingItems = dataUsages.OrderBy(x => x.CreationTime).ToList();
|
||||
var lastKnownTraffics = dbContext.LastKnownTraffic.ToList();
|
||||
var users = await dbContext.Users.ToListAsync();
|
||||
|
||||
foreach (var item in updates)
|
||||
{
|
||||
var tempUser = users.Find(x => x.Id == item.UserID);
|
||||
if (tempUser == null) continue;
|
||||
using var transaction = await dbContext.Database.BeginTransactionAsync();
|
||||
|
||||
using var transactionDbContext = new DBContext(); // Create a new DbContext for each transaction
|
||||
using var transaction = await transactionDbContext.Database.BeginTransactionAsync();
|
||||
try
|
||||
{
|
||||
LastKnownTraffic lastKnown = lastKnownTraffics.Find(x => x.UserID == item.UserID);
|
||||
|
@ -137,7 +154,7 @@ namespace MTWireGuard.Application
|
|||
var old = existingItems.FindLast(oldItem => oldItem.UserID == item.UserID);
|
||||
if (old == null)
|
||||
{
|
||||
await dbContext.DataUsages.AddAsync(item);
|
||||
await transactionDbContext.DataUsages.AddAsync(item);
|
||||
tempUser.RX = item.RX + lastKnown.RX;
|
||||
tempUser.TX = item.TX + lastKnown.TX;
|
||||
}
|
||||
|
@ -146,41 +163,58 @@ namespace MTWireGuard.Application
|
|||
if ((old.RX <= item.RX || old.TX <= item.TX) &&
|
||||
(old.RX != item.RX && old.TX != item.TX)) // Normal Data (and not duplicate)
|
||||
{
|
||||
await dbContext.DataUsages.AddAsync(item);
|
||||
await transactionDbContext.DataUsages.AddAsync(item);
|
||||
}
|
||||
else if (old.RX > item.RX || old.TX > item.TX) // Server Reset
|
||||
{
|
||||
lastKnown.RX = old.RX;
|
||||
lastKnown.TX = old.TX;
|
||||
lastKnown.CreationTime = DateTime.Now;
|
||||
dbContext.LastKnownTraffic.Update(lastKnown);
|
||||
transactionDbContext.LastKnownTraffic.Update(lastKnown);
|
||||
item.ResetNotes = $"System reset detected at: {DateTime.Now}";
|
||||
await dbContext.DataUsages.AddAsync(item);
|
||||
await transactionDbContext.DataUsages.AddAsync(item);
|
||||
}
|
||||
if (item.RX > old.RX) tempUser.RX = item.RX + lastKnown.RX;
|
||||
if (item.TX > old.TX) tempUser.TX = item.TX + lastKnown.TX;
|
||||
}
|
||||
if (tempUser.TrafficLimit > 0 && tempUser.RX + tempUser.TX >= tempUser.TrafficLimit)
|
||||
if (tempUser.TrafficLimit > 0 && tempUser.RX + tempUser.TX >= GigabyteToByte(tempUser.TrafficLimit))
|
||||
{
|
||||
// Disable User
|
||||
logger.Information($"User #{tempUser.Id} reached {tempUser.RX + tempUser.TX} of {GigabyteToByte(tempUser.TrafficLimit)} bandwidth.");
|
||||
var disable = await API.DisableUser(item.UserID);
|
||||
if (disable.Code != "200")
|
||||
{
|
||||
Console.WriteLine("Failed disabling user");
|
||||
logger.Error("Failed disabling user", new
|
||||
{
|
||||
userId = item.UserID,
|
||||
disable.Code,
|
||||
disable.Title,
|
||||
disable.Description
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Information("Disabled user due to bandwidth limit", new
|
||||
{
|
||||
item.UserID,
|
||||
TrafficUsed = Helper.ConvertByteSize(tempUser.RX + tempUser.TX),
|
||||
tempUser.TrafficLimit
|
||||
});
|
||||
}
|
||||
}
|
||||
dbContext.Users.Update(tempUser);
|
||||
await dbContext.SaveChangesAsync();
|
||||
transaction.Commit();
|
||||
transactionDbContext.Users.Update(tempUser);
|
||||
await transactionDbContext.SaveChangesAsync();
|
||||
await transaction.CommitAsync();
|
||||
}
|
||||
catch (DbUpdateException ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
transaction.Rollback();
|
||||
logger.Error(ex.Message);
|
||||
await transaction.RollbackAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
logger.Error(ex.Message);
|
||||
await transaction.RollbackAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -190,6 +224,63 @@ namespace MTWireGuard.Application
|
|||
return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return full path of requested file in app's home directory
|
||||
/// </summary>
|
||||
/// <param name="filename">requested file name</param>
|
||||
/// <returns></returns>
|
||||
public static string GetHomePath(string filename)
|
||||
{
|
||||
return RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "/home/app" : Path.Join(AppDomain.CurrentDomain.BaseDirectory, filename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return full path of requested file in log files directory
|
||||
/// </summary>
|
||||
/// <param name="filename">requested file name</param>
|
||||
/// <returns></returns>
|
||||
public static string GetLogPath(string filename)
|
||||
{
|
||||
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Path.Join(AppDomain.CurrentDomain.BaseDirectory, "log", filename) : Path.Join("/var/log/mtwireguard", filename);
|
||||
}
|
||||
|
||||
public static string GetIDFile() => GetHomePath("identifier.id");
|
||||
public static string GetIDContent() => File.ReadAllText(GetIDFile());
|
||||
|
||||
public static Serilog.Core.Logger LoggerConfiguration()
|
||||
{
|
||||
return new LoggerConfiguration()
|
||||
.Enrich.WithExceptionDetails(new DestructuringOptionsBuilder()
|
||||
.WithDefaultDestructurers()
|
||||
.WithRootName("Message").WithRootName("Exception").WithRootName("Exception"))
|
||||
.Enrich.WithProperty("App.Version", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "0.0.0.0")
|
||||
.Enrich.WithMachineName()
|
||||
.Enrich.WithEnvironmentUserName()
|
||||
.Enrich.WithClientId(GetIDContent())
|
||||
.WriteTo.Logger(lc => lc
|
||||
.Filter.ByIncludingOnly(AspNetCoreRequestLogging())
|
||||
.WriteTo.File(
|
||||
GetLogPath("access.log"),
|
||||
rollingInterval: RollingInterval.Day,
|
||||
retainedFileCountLimit: 31
|
||||
))
|
||||
.WriteTo.Logger(lc => lc
|
||||
.Filter.ByIncludingOnly(LogEvent => LogEvent.Exception != null)
|
||||
.WriteTo.Seq("https://mtwglogger.techgarage.ir/"))
|
||||
.WriteTo.Logger(lc => lc
|
||||
.Filter.ByExcluding(AspNetCoreRequestLogging())
|
||||
.WriteTo.SQLite(GetLogPath("logs.db")))
|
||||
.CreateLogger();
|
||||
}
|
||||
|
||||
private static Func<LogEvent, bool> AspNetCoreRequestLogging()
|
||||
{
|
||||
return e =>
|
||||
Matching.FromSource("Microsoft.AspNetCore.Hosting.Diagnostics").Invoke(e) ||
|
||||
Matching.FromSource("Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware").Invoke(e) ||
|
||||
Matching.FromSource("Microsoft.AspNetCore.Routing.EndpointMiddleware").Invoke(e);
|
||||
}
|
||||
|
||||
public static TimeSpan ConvertToTimeSpan(string input)
|
||||
{
|
||||
int weeks = 0;
|
||||
|
|
|
@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using MikrotikAPI;
|
||||
using MTWireGuard.Application.Models;
|
||||
|
@ -115,11 +116,11 @@ namespace MTWireGuard.Application.MinimalAPI
|
|||
/// </summary>
|
||||
public static async Task<Results<Accepted, ProblemHttpResult>> TrafficUsageUpdate(
|
||||
[FromServices] IMapper mapper,
|
||||
[FromServices] Serilog.ILogger logger,
|
||||
[FromServices] DBContext dbContext,
|
||||
[FromServices] IMikrotikRepository mikrotikRepository,
|
||||
HttpContext context)
|
||||
{
|
||||
|
||||
StreamReader reader = new(context.Request.Body);
|
||||
string body = await reader.ReadToEndAsync();
|
||||
|
||||
|
@ -128,7 +129,7 @@ namespace MTWireGuard.Application.MinimalAPI
|
|||
|
||||
if (updates == null || updates.Count < 1) return TypedResults.Problem("Empty data");
|
||||
|
||||
Helper.HandleUserTraffics(updates, dbContext, mikrotikRepository);
|
||||
Helper.HandleUserTraffics(updates, dbContext, mikrotikRepository, logger);
|
||||
|
||||
return TypedResults.Accepted("Done");
|
||||
}
|
||||
|
|
18
Application/SerilogUiAuthorizeFilter.cs
Normal file
18
Application/SerilogUiAuthorizeFilter.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Serilog.Ui.Web.Authorization;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MTWireGuard.Application
|
||||
{
|
||||
public class SerilogUiAuthorizeFilter : IUiAuthorizationFilter
|
||||
{
|
||||
public bool Authorize(HttpContext httpContext)
|
||||
{
|
||||
return httpContext.User.Identity is { IsAuthenticated: true };
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,86 +1,77 @@
|
|||
using AutoMapper;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MTWireGuard.Application.Models;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MTWireGuard.Application.Repositories;
|
||||
using MTWireGuard.Application.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Serilog;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||
|
||||
namespace MTWireGuard.Application
|
||||
{
|
||||
public class SetupValidator(IServiceProvider serviceProvider)
|
||||
{
|
||||
private IMikrotikRepository api;
|
||||
private ILogger logger;
|
||||
|
||||
public async Task Validate()
|
||||
public static bool IsValid { get; private set; }
|
||||
public static string Title { get; private set; }
|
||||
public static string Description { get; private set; }
|
||||
|
||||
public async Task<bool> Validate()
|
||||
{
|
||||
var envVariables = ValidateEnvironmentVariables();
|
||||
if (envVariables)
|
||||
InitializeServices();
|
||||
|
||||
if (ValidateEnvironmentVariables())
|
||||
{
|
||||
Console.BackgroundColor = ConsoleColor.Black;
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($"[-] Environment variables are not set!");
|
||||
Console.WriteLine($"[!] Please set \"MT_IP\", \"MT_USER\", \"MT_PASS\", \"MT_PUBLIC_IP\" variables in container environment.");
|
||||
Console.ResetColor();
|
||||
Shutdown();
|
||||
LogAndDisplayError("Environment variables are not set!", "Please set \"MT_IP\", \"MT_USER\", \"MT_PASS\", \"MT_PUBLIC_IP\" variables in container environment.");
|
||||
IsValid = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
serviceProvider.GetService<DBContext>().Database.EnsureCreated();
|
||||
api = serviceProvider.GetService<IMikrotikRepository>();
|
||||
if (!File.Exists(Helper.GetIDFile()))
|
||||
{
|
||||
using var fs = File.OpenWrite(Helper.GetIDFile());
|
||||
var id = Guid.NewGuid().ToString();
|
||||
id = id[(id.LastIndexOf('-') + 1)..];
|
||||
byte[] identifier = new UTF8Encoding(true).GetBytes(id);
|
||||
fs.Write(identifier, 0, identifier.Length);
|
||||
}
|
||||
|
||||
var (apiConnection, apiConnectionMessage) = await ValidateAPIConnection();
|
||||
if (!apiConnection)
|
||||
{
|
||||
Console.BackgroundColor = ConsoleColor.Black;
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($"[-] Error connecting to the router api!");
|
||||
Console.WriteLine($"[!] {apiConnectionMessage}");
|
||||
Console.ResetColor();
|
||||
Shutdown();
|
||||
var MT_IP = Environment.GetEnvironmentVariable("MT_IP");
|
||||
var ping = new Ping();
|
||||
var reply = ping.Send(MT_IP, 60 * 1000);
|
||||
if (reply.Status == IPStatus.Success)
|
||||
{
|
||||
LogAndDisplayError("Error connecting to the router api!", apiConnectionMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogAndDisplayError("Error connecting to the router api!", $"Can't find Mikrotik API server at address: {MT_IP}\r\nping status: {reply.Status}");
|
||||
}
|
||||
IsValid = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
var ip = GetIPAddress();
|
||||
if (string.IsNullOrEmpty(ip))
|
||||
{
|
||||
Console.BackgroundColor = ConsoleColor.Black;
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($"[-] Error getting container IP address!");
|
||||
Console.ResetColor();
|
||||
Shutdown();
|
||||
LogAndDisplayError("Error getting container IP address!", "Invalid container IP address.");
|
||||
IsValid = false;
|
||||
return false;
|
||||
}
|
||||
var scripts = await api.GetScripts();
|
||||
var schedulers = await api.GetSchedulers();
|
||||
var trafficScript = scripts.Find(x => x.Name == "SendTrafficUsage");
|
||||
var trafficScheduler = schedulers.Find(x => x.Name == "TrafficUsage");
|
||||
|
||||
if (trafficScript == null)
|
||||
if (!await api.TryConnectAsync())
|
||||
{
|
||||
var create = await api.CreateScript(new()
|
||||
{
|
||||
Name = "SendTrafficUsage",
|
||||
Policies = ["write", "read", "test"],
|
||||
DontRequiredPermissions = false,
|
||||
Source = Helper.PeersTrafficUsageScript($"http://{ip}/api/usage")
|
||||
});
|
||||
var result = create.Code;
|
||||
}
|
||||
if (trafficScheduler == null)
|
||||
{
|
||||
var create = await api.CreateScheduler(new()
|
||||
{
|
||||
Name = "TrafficUsage",
|
||||
Interval = new TimeSpan(0, 5, 0),
|
||||
OnEvent = "SendTrafficUsage",
|
||||
Policies = ["write", "read", "test"]
|
||||
});
|
||||
var result = create.Code;
|
||||
LogAndDisplayError("Error connecting to the router api!", "Connecting to API failed.");
|
||||
IsValid = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
await EnsureTrafficScripts(ip);
|
||||
IsValid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool ValidateEnvironmentVariables()
|
||||
|
@ -105,7 +96,7 @@ namespace MTWireGuard.Application
|
|||
}
|
||||
}
|
||||
|
||||
private static string GetIPAddress()
|
||||
private string GetIPAddress()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -115,11 +106,69 @@ namespace MTWireGuard.Application
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
logger.Error(ex, "Error getting container IP address.");
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
private void LogAndDisplayError(string title, string description)
|
||||
{
|
||||
Title = title;
|
||||
Description = description;
|
||||
Console.BackgroundColor = ConsoleColor.Black;
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine($"[-] {Title}");
|
||||
Console.WriteLine($"[!] {Description}");
|
||||
Console.ResetColor();
|
||||
logger.Error("Error in container configuration", new { Error = Title, Description });
|
||||
}
|
||||
|
||||
private void InitializeServices()
|
||||
{
|
||||
serviceProvider.GetService<DBContext>().Database.EnsureCreated();
|
||||
api = serviceProvider.GetService<IMikrotikRepository>();
|
||||
logger = serviceProvider.GetService<ILogger>();
|
||||
}
|
||||
|
||||
private async Task EnsureTrafficScripts(string ip)
|
||||
{
|
||||
var scripts = await api.GetScripts();
|
||||
var schedulers = await api.GetSchedulers();
|
||||
|
||||
//if (scripts.Find(x => x.Name == "SendTrafficUsage") == null)
|
||||
//{
|
||||
// var create = await api.CreateScript(new()
|
||||
// {
|
||||
// Name = "SendTrafficUsage",
|
||||
// Policies = ["write", "read", "test", "ftp"],
|
||||
// DontRequiredPermissions = false,
|
||||
// Source = Helper.PeersTrafficUsageScript($"http://{ip}/api/usage")
|
||||
// });
|
||||
// var result = create.Code;
|
||||
// logger.Information("Created TrafficUsage Script", new
|
||||
// {
|
||||
// result = create
|
||||
// });
|
||||
//}
|
||||
if (schedulers.Find(x => x.Name == "TrafficUsage") == null)
|
||||
{
|
||||
var create = await api.CreateScheduler(new()
|
||||
{
|
||||
Name = "TrafficUsage",
|
||||
Interval = new TimeSpan(0, 5, 0),
|
||||
//OnEvent = "SendTrafficUsage",
|
||||
OnEvent = Helper.PeersTrafficUsageScript($"http://{ip}/api/usage"),
|
||||
Policies = ["write", "read", "test", "ftp"],
|
||||
Comment = "update wireguard peers traffic usage"
|
||||
});
|
||||
var result = create.Code;
|
||||
logger.Information("Created TrafficUsage Scheduler", new
|
||||
{
|
||||
result = create
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void Shutdown()
|
||||
{
|
||||
Environment.Exit(0);
|
||||
|
|
|
@ -14,6 +14,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MTWireGuard.Application", "
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MikrotikAPI", "MikrotikAPI\MikrotikAPI.csproj", "{357EE40B-AA30-482C-94CF-34854BE24D61}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serilog.Ui.SqliteProvider", "Serilog.Ui.SqliteProvider\Serilog.Ui.SqliteProvider.csproj", "{4D0ED34E-E84B-4861-82DC-C2149DCD6E14}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -32,6 +34,10 @@ Global
|
|||
{357EE40B-AA30-482C-94CF-34854BE24D61}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{357EE40B-AA30-482C-94CF-34854BE24D61}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{357EE40B-AA30-482C-94CF-34854BE24D61}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4D0ED34E-E84B-4861-82DC-C2149DCD6E14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4D0ED34E-E84B-4861-82DC-C2149DCD6E14}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4D0ED34E-E84B-4861-82DC-C2149DCD6E14}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4D0ED34E-E84B-4861-82DC-C2149DCD6E14}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Serilog.Ui.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serilog.Ui.SqliteDataProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Sqlite data provider specific extension methods for <see cref="SerilogUiOptionsBuilder"/>.
|
||||
/// </summary>
|
||||
public static class SerilogUiOptionBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures the SerilogUi to connect to a Sqlite database.
|
||||
/// </summary>
|
||||
/// <param name="optionsBuilder"> The options builder. </param>
|
||||
/// <param name="connectionString"> The connection string. </param>
|
||||
/// <param name="tableName"> Name of the table. </param>
|
||||
/// <exception cref="ArgumentNullException"> throw if connectionString is null </exception>
|
||||
/// <exception cref="ArgumentNullException"> throw is tableName is null </exception>
|
||||
public static void UseSqliteServer(
|
||||
this SerilogUiOptionsBuilder optionsBuilder,
|
||||
string connectionString,
|
||||
string tableName
|
||||
)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(connectionString))
|
||||
throw new ArgumentNullException(nameof(connectionString));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(tableName))
|
||||
throw new ArgumentNullException(nameof(tableName));
|
||||
|
||||
var relationProvider = new RelationalDbOptions
|
||||
{
|
||||
ConnectionString = connectionString,
|
||||
TableName = tableName
|
||||
};
|
||||
|
||||
((ISerilogUiOptionsBuilder)optionsBuilder).Services
|
||||
.AddScoped<IDataProvider, SqliteDataProvider>(p => ActivatorUtilities.CreateInstance<SqliteDataProvider>(p, relationProvider));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
16
Serilog.Ui.SqliteProvider/Serilog.Ui.SqliteProvider.csproj
Normal file
16
Serilog.Ui.SqliteProvider/Serilog.Ui.SqliteProvider.csproj
Normal file
|
@ -0,0 +1,16 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Dapper" Version="2.1.35" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="8.0.5" />
|
||||
<PackageReference Include="Serilog.UI" Version="2.6.0" />
|
||||
<PackageReference Include="SQLite" Version="3.13.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
134
Serilog.Ui.SqliteProvider/SqliteDataProvider.cs
Normal file
134
Serilog.Ui.SqliteProvider/SqliteDataProvider.cs
Normal file
|
@ -0,0 +1,134 @@
|
|||
using Dapper;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Serilog.Ui.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serilog.Ui.SqliteDataProvider
|
||||
{
|
||||
public class SqliteDataProvider(RelationalDbOptions options) : IDataProvider
|
||||
{
|
||||
private readonly RelationalDbOptions _options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
|
||||
public async Task<(IEnumerable<LogModel>, int)> FetchDataAsync(
|
||||
int page,
|
||||
int count,
|
||||
string level = null,
|
||||
string searchCriteria = null,
|
||||
DateTime? startDate = null,
|
||||
DateTime? endDate = null
|
||||
)
|
||||
{
|
||||
var logsTask = GetLogs(page - 1, count, level, searchCriteria, startDate, endDate);
|
||||
var logCountTask = CountLogs(level, searchCriteria, startDate, endDate);
|
||||
|
||||
await Task.WhenAll(logsTask, logCountTask);
|
||||
|
||||
return (await logsTask, await logCountTask);
|
||||
}
|
||||
|
||||
public string Name => _options.ToDataProviderName("SQLite");
|
||||
|
||||
private Task<IEnumerable<LogModel>> GetLogs(
|
||||
int page,
|
||||
int count,
|
||||
string level,
|
||||
string searchCriteria,
|
||||
DateTime? startDate,
|
||||
DateTime? endDate)
|
||||
{
|
||||
var queryBuilder = new StringBuilder();
|
||||
queryBuilder.Append("SELECT Id, RenderedMessage AS Message, Level, Timestamp, Exception, Properties FROM ");
|
||||
queryBuilder.Append(_options.TableName);
|
||||
queryBuilder.Append(" ");
|
||||
|
||||
GenerateWhereClause(queryBuilder, level, searchCriteria, startDate, endDate);
|
||||
|
||||
queryBuilder.Append("ORDER BY Id DESC LIMIT @Offset, @Count");
|
||||
|
||||
using (var connection = new SqliteConnection(_options.ConnectionString))
|
||||
{
|
||||
var param = new
|
||||
{
|
||||
Offset = page * count,
|
||||
Count = count,
|
||||
Level = level,
|
||||
Search = searchCriteria != null ? $"%{searchCriteria}%" : null,
|
||||
StartDate = startDate,
|
||||
EndDate = endDate
|
||||
};
|
||||
var logs = connection.Query<LogModel>(queryBuilder.ToString(), param);
|
||||
var index = 1;
|
||||
foreach (var log in logs)
|
||||
log.RowNo = (page * count) + index++;
|
||||
|
||||
return Task.FromResult(logs);
|
||||
}
|
||||
}
|
||||
|
||||
private Task<int> CountLogs(
|
||||
string level,
|
||||
string searchCriteria,
|
||||
DateTime? startDate = null,
|
||||
DateTime? endDate = null)
|
||||
{
|
||||
var queryBuilder = new StringBuilder();
|
||||
queryBuilder.Append("SELECT COUNT(Id) FROM ");
|
||||
queryBuilder.Append(_options.TableName);
|
||||
queryBuilder.Append(" ");
|
||||
|
||||
GenerateWhereClause(queryBuilder, level, searchCriteria, startDate, endDate);
|
||||
|
||||
using var connection = new SqliteConnection(_options.ConnectionString);
|
||||
return Task.FromResult(connection.QueryFirstOrDefault<int>(queryBuilder.ToString(),
|
||||
new
|
||||
{
|
||||
Level = level,
|
||||
Search = searchCriteria != null ? "%" + searchCriteria + "%" : null,
|
||||
StartDate = startDate,
|
||||
EndDate = endDate
|
||||
}));
|
||||
}
|
||||
|
||||
private void GenerateWhereClause(
|
||||
StringBuilder queryBuilder,
|
||||
string level,
|
||||
string searchCriteria,
|
||||
DateTime? startDate = null,
|
||||
DateTime? endDate = null)
|
||||
{
|
||||
var whereIncluded = false;
|
||||
|
||||
if (!string.IsNullOrEmpty(level))
|
||||
{
|
||||
queryBuilder.Append("WHERE Level = @Level ");
|
||||
whereIncluded = true;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(searchCriteria))
|
||||
{
|
||||
queryBuilder.Append(whereIncluded
|
||||
? "AND (RenderedMessage LIKE @Search OR Exception LIKE @Search) "
|
||||
: "WHERE (RenderedMessage LIKE @Search OR Exception LIKE @Search) ");
|
||||
whereIncluded = true;
|
||||
}
|
||||
|
||||
if (startDate != null)
|
||||
{
|
||||
queryBuilder.Append(whereIncluded
|
||||
? "AND Timestamp >= @StartDate "
|
||||
: "WHERE Timestamp >= @StartDate ");
|
||||
whereIncluded = true;
|
||||
}
|
||||
|
||||
if (endDate != null)
|
||||
{
|
||||
queryBuilder.Append(whereIncluded
|
||||
? "AND Timestamp <= @EndDate "
|
||||
: "WHERE Timestamp <= @EndDate ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
|
@ -11,18 +11,23 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="12.0.1" />
|
||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.1">
|
||||
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.6">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="NuGet.Protocol" Version="6.8.0" />
|
||||
<PackageReference Include="Razor.Templating.Core" Version="1.9.0" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="8.0.1" />
|
||||
<PackageReference Include="NuGet.Protocol" Version="6.10.1" />
|
||||
<PackageReference Include="Razor.Templating.Core" Version="2.0.0" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
|
||||
<PackageReference Include="Serilog.Enrichers.Environment" Version="3.0.1" />
|
||||
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Seq" Version="8.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.SQLite" Version="6.0.0" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="8.0.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
@page
|
||||
@model ErrorModel
|
||||
@inject IHttpContextAccessor contextAccessor
|
||||
@{
|
||||
Layout = null;
|
||||
var httpContext = contextAccessor.HttpContext;
|
||||
string title = Application.ExceptionHandlerContext.Message,
|
||||
message = Application.ExceptionHandlerContext.StackTrace,
|
||||
details = Application.ExceptionHandlerContext.Details;
|
||||
}
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
@ -32,6 +37,16 @@
|
|||
|
||||
<!-- Theme Style Switcher-->
|
||||
<script src="assets/js/themeSwitcher.js"></script>
|
||||
|
||||
<style>
|
||||
.accordion-body {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.accordion-body p {
|
||||
max-height: 30vh;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="min-vh-100 d-flex flex-row align-items-center">
|
||||
|
@ -39,10 +54,45 @@
|
|||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="clearfix">
|
||||
<h1 class="float-start display-3 me-4">500</h1>
|
||||
<h4 class="pt-3">@ViewBag.Title</h4>
|
||||
<h6>@ViewBag.Message</h6>
|
||||
<p class="text-medium-emphasis">@Html.Raw(ViewBag.Details)</p>
|
||||
<h1 class="float-start display-3 mx-2 mt-4">500</h1>
|
||||
<h4 class="pt-3">@title</h4>
|
||||
@if (!Application.SetupValidator.IsValid)
|
||||
{
|
||||
<hr />
|
||||
<h4 class="text-danger"><i class='bx bxs-chevrons-right'></i> Invalid Setup Variables</h4>
|
||||
<strong>@Application.SetupValidator.Title</strong>
|
||||
<br />
|
||||
<p>@Application.SetupValidator.Description</p>
|
||||
}
|
||||
<div class="accordion mb-3" id="infoAccordion">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#titleAccordion" aria-expanded="true" aria-controls="titleAccordion">
|
||||
Error Message
|
||||
</button>
|
||||
</h2>
|
||||
<div id="titleAccordion" class="accordion-collapse collapse" data-bs-parent="#infoAccordion">
|
||||
<div class="accordion-body">
|
||||
<p class="text-medium-emphasis text-break">@message</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#detailsAccordion" aria-expanded="true" aria-controls="detailsAccordion">
|
||||
Error Details
|
||||
</button>
|
||||
</h2>
|
||||
<div id="detailsAccordion" class="accordion-collapse collapse" data-bs-parent="#infoAccordion">
|
||||
<div class="accordion-body">
|
||||
<p class="text-medium-emphasis text-break">@Html.Raw(details)</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="/Debug" class="btn btn-primary">
|
||||
<i class="bx bx-notepad me-1"></i> <span class="d-none d-lg-inline-block">View Logs</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.VisualStudio.Web.CodeGenerators.Mvc.Templates.Blazor;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace MTWireGuard.Pages
|
||||
{
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
[IgnoreAntiforgeryToken]
|
||||
[AllowAnonymous]
|
||||
|
||||
public class ErrorModel : PageModel
|
||||
{
|
||||
|
||||
public ErrorModel()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -3,31 +3,44 @@ using MTWireGuard.Middlewares;
|
|||
using MTWireGuard.Application;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using MTWireGuard.Application.MinimalAPI;
|
||||
using Serilog;
|
||||
using Serilog.Exceptions.Core;
|
||||
using Serilog.Exceptions;
|
||||
using System.Configuration;
|
||||
using Serilog.Ui.Web;
|
||||
using Serilog.Ui.Web.Authorization;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddApplicationServices();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (!app.Environment.IsDevelopment())
|
||||
internal class Program
|
||||
{
|
||||
app.UseExceptionHandler("/Error");
|
||||
app.UseHsts();
|
||||
}
|
||||
public static bool isValid { get; private set; }
|
||||
public static string validationMessage { get; private set; }
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
private static async Task Main(string[] args)
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
builder.Services.AddControllersWithViews();
|
||||
builder.Services.AddExceptionHandler<ExceptionHandler>();
|
||||
builder.Services.AddProblemDetails();
|
||||
builder.Services.AddApplicationServices();
|
||||
|
||||
var serviceScope = app.Services.CreateScope().ServiceProvider;
|
||||
builder.Host.UseSerilog(Helper.LoggerConfiguration());
|
||||
|
||||
// Validate Prerequisite
|
||||
var validator = new SetupValidator(serviceScope);
|
||||
await validator.Validate();
|
||||
var app = builder.Build();
|
||||
|
||||
if (!app.Environment.IsDevelopment())
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
var serviceScope = app.Services.CreateScope().ServiceProvider;
|
||||
|
||||
// Validate Prerequisite
|
||||
var validator = new SetupValidator(serviceScope);
|
||||
isValid = await validator.Validate();
|
||||
|
||||
if (!app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseStaticFiles();
|
||||
else
|
||||
app.UseHsts();
|
||||
}
|
||||
else
|
||||
app.UseStaticFiles(new StaticFileOptions()
|
||||
{
|
||||
OnPrepareResponse = context =>
|
||||
|
@ -37,29 +50,44 @@ else
|
|||
}
|
||||
});
|
||||
|
||||
app.UseDependencyCheck();
|
||||
app.UseClientReporting();
|
||||
app.UseExceptionHandling();
|
||||
//app.UseAntiForgery();
|
||||
app.UseExceptionHandler();
|
||||
app.UseClientReporting();
|
||||
//app.UseAntiForgery();
|
||||
app.UseRouting();
|
||||
|
||||
app.UseRouting();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseSession();
|
||||
|
||||
app.UseSession();
|
||||
app.MapRazorPages();
|
||||
|
||||
app.MapRazorPages();
|
||||
|
||||
app.
|
||||
app.
|
||||
MapGroup("/api/").
|
||||
MapGeneralApi();
|
||||
|
||||
app.UseCors(options =>
|
||||
{
|
||||
app.UseCors(options =>
|
||||
{
|
||||
options.AllowAnyHeader();
|
||||
options.AllowAnyMethod();
|
||||
options.AllowAnyOrigin();
|
||||
});
|
||||
});
|
||||
|
||||
app.Run();
|
||||
app.UseSerilogRequestLogging();
|
||||
|
||||
app.UseSerilogUi(options =>
|
||||
{
|
||||
options.RoutePrefix = "Debug";
|
||||
options.InjectStylesheet("/assets/lib/boxicons/css/boxicons.min.css");
|
||||
options.InjectStylesheet("/assets/css/serilogui.css");
|
||||
options.InjectJavascript("/assets/js/serilogui.js");
|
||||
options.Authorization.AuthenticationType = AuthenticationType.Jwt;
|
||||
options.Authorization.Filters =
|
||||
[
|
||||
new SerilogUiAuthorizeFilter()
|
||||
];
|
||||
});
|
||||
|
||||
app.Run();
|
||||
}
|
||||
}
|
||||
|
|
46
UI/wwwroot/assets/css/serilogui.css
Normal file
46
UI/wwwroot/assets/css/serilogui.css
Normal file
|
@ -0,0 +1,46 @@
|
|||
#sidebar {
|
||||
background: #343a40;
|
||||
}
|
||||
|
||||
#sidebar .logo {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
#sidebar ul li a {
|
||||
padding: 0;
|
||||
border-radius: .5rem;
|
||||
border-bottom: 3px solid;
|
||||
outline: 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin: .5rem;
|
||||
}
|
||||
|
||||
#sidebar ul li a span {
|
||||
margin-right: 15px;
|
||||
width: 50px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 40px;
|
||||
font-size: 1.25rem;
|
||||
margin-right: .25rem;
|
||||
}
|
||||
|
||||
#sidebar ul.components li a {
|
||||
--bs-primary: #4582ec;
|
||||
padding: 10px 0;
|
||||
color: var(--bs-primary);
|
||||
background-color: rgba(105,108,255,.16) !important;
|
||||
border-color: var(--bs-primary);
|
||||
}
|
||||
|
||||
#sidebar.active ul.components li a {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#logTable .log-level {
|
||||
border-radius: 5px;
|
||||
user-select: none;
|
||||
}
|
6
UI/wwwroot/assets/js/serilogui.js
Normal file
6
UI/wwwroot/assets/js/serilogui.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
let favicon = document.createElement('link');
|
||||
favicon.href = 'img/favicon.ico';
|
||||
favicon.rel = 'icon';
|
||||
document.getElementsByTagName('head')[0].appendChild(favicon);
|
||||
document.querySelector('#sidebar.active .logo:first-child').innerHTML = 'MW';
|
||||
document.querySelector('#sidebar.active .logo:last-child').innerHTML = 'MTWireguard';
|
Loading…
Add table
Add a link
Reference in a new issue