Compare commits

..

No commits in common. "15241429fce0987f887901430a7ad07b45e9972b" and "9b132e1db2d870482cf0ba85d92d290ab34d2f3d" have entirely different histories.

12 changed files with 118 additions and 177 deletions

View File

@ -1,8 +0,0 @@
namespace NetworkResurrector.Agent.Extensions
{
internal static class DataTypeExtensions
{
public static string Nullify(this string value)
=> string.IsNullOrWhiteSpace(value) ? null : value;
}
}

View File

@ -1,41 +0,0 @@
using Microsoft.Extensions.Configuration;
using Serilog;
using Serilog.Configuration;
using Serilog.Sinks.MSSqlServer;
using System;
namespace NetworkResurrector.Agent.Extensions
{
internal static class LoggingExtensions
{
internal static LoggerSinkConfiguration ConfiguredDestinations(this LoggerSinkConfiguration writeTo, IConfiguration configuration)
{
var sqlServerEnabled = configuration.GetValue<bool>("Logs:SqlServer:Enabled");
var sqlServerConnection = configuration.GetValue<string>("Logs:SqlServer:Connection");
var seqEnabled = configuration.GetValue<bool>("Logs:Seq:Enabled");
var seqUrl = configuration.GetValue<string>("Logs:Seq:Url");
var seqApiKey = configuration.GetValue<string>("Logs:Seq:ApiKey");
if (sqlServerEnabled && string.IsNullOrWhiteSpace(sqlServerConnection))
throw new Exception("If SqlServer logging is enabled, the SqlServer connection must be configured.");
if (seqEnabled && string.IsNullOrWhiteSpace(seqUrl))
throw new Exception("If Seq logging is enabled, the Seq URL must be configured.");
if (sqlServerEnabled)
{
var columnOptions = new ColumnOptions();
columnOptions.Store.Remove(StandardColumn.Properties);
columnOptions.Store.Remove(StandardColumn.MessageTemplate);
columnOptions.Store.Add(StandardColumn.LogEvent);
var mssqlSinkOptions = new MSSqlServerSinkOptions() { AutoCreateSqlTable = true, TableName = "__Logs_Agent" };
writeTo.MSSqlServer(sqlServerConnection, mssqlSinkOptions, columnOptions: columnOptions);
}
if (seqEnabled)
writeTo.Seq(seqUrl, apiKey: seqApiKey.Nullify());
return writeTo;
}
}
}

View File

@ -15,7 +15,6 @@
<PackageReference Include="Serilog.AspNetCore" Version="$(SerilogPackageVersion)" /> <PackageReference Include="Serilog.AspNetCore" Version="$(SerilogPackageVersion)" />
<PackageReference Include="Serilog.Extensions.Logging" Version="$(SerilogExtensionsPackageVersion)" /> <PackageReference Include="Serilog.Extensions.Logging" Version="$(SerilogExtensionsPackageVersion)" />
<PackageReference Include="Serilog.Sinks.MSSqlServer" Version="$(SerilogSinksMSSqlServerPackageVersion)" /> <PackageReference Include="Serilog.Sinks.MSSqlServer" Version="$(SerilogSinksMSSqlServerPackageVersion)" />
<PackageReference Include="Serilog.Sinks.Seq" Version="$(SerilogSinksSeqPackageVersion)" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="$(AutoMapperExtensionsPackageVersion)" /> <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="$(AutoMapperExtensionsPackageVersion)" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="$(MediatRPackageVersion)" /> <PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="$(MediatRPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="$(MicrosoftExtensionsHostingPackageVersion)" /> <PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="$(MicrosoftExtensionsHostingPackageVersion)" />

View File

@ -5,6 +5,9 @@ using Microsoft.Extensions.Hosting;
using NetworkResurrector.Agent.Extensions; using NetworkResurrector.Agent.Extensions;
using NetworkResurrector.Agent.Extensions.Serilog; using NetworkResurrector.Agent.Extensions.Serilog;
using Serilog; using Serilog;
using Serilog.Core;
using Serilog.Events;
using Serilog.Sinks.MSSqlServer;
using System; using System;
namespace NetworkResurrector.Agent namespace NetworkResurrector.Agent
@ -17,12 +20,28 @@ namespace NetworkResurrector.Agent
builder.Host.UseSerilog((_, lc) => builder.Host.UseSerilog((_, lc) =>
{ {
var connectionString = builder.Configuration.GetConnectionString("DatabaseConnection");
var loggingLevelParam = builder.Configuration.GetValue<string>("Logging:LogLevel:Default");
var loggingLevelOk = Enum.TryParse(loggingLevelParam, out LogEventLevel loggingLevel);
if (!loggingLevelOk)
throw new Exception($"Logging level '{loggingLevelParam}' is not valid.");
var loggingLevelSwitch = new LoggingLevelSwitch(loggingLevel);
var columnOptions = new ColumnOptions();
columnOptions.Store.Remove(StandardColumn.Properties);
columnOptions.Store.Remove(StandardColumn.MessageTemplate);
columnOptions.Store.Add(StandardColumn.LogEvent);
var mssqlSinkOptions = new MSSqlServerSinkOptions() { AutoCreateSqlTable = true, TableName = "__Logs_Agent" };
lc lc
.ReadFrom.Configuration(builder.Configuration) .MinimumLevel.ControlledBy(loggingLevelSwitch)
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.Enrich.FromLogContext() .Enrich.FromLogContext()
.Enrich.With(new AgentCodeEventEnricher(builder.Configuration)) .Enrich.With(new AgentCodeEventEnricher(builder.Configuration))
.WriteTo.Console() .WriteTo.Console()
.WriteTo.ConfiguredDestinations(builder.Configuration); .WriteTo.MSSqlServer(connectionString, mssqlSinkOptions, columnOptions: columnOptions);
}); });
builder.Services.ConfigureServices(builder.Configuration); builder.Services.ConfigureServices(builder.Configuration);

View File

@ -6,23 +6,11 @@
"Host": { "Host": {
"UseWindowsService": true "UseWindowsService": true
}, },
"Serilog": { "Logging": {
"MinimumLevel": { "LogLevel": {
"Default": "Information", "Default": "Debug",
"Override": { "Microsoft": "Warning",
"Microsoft": "Information" "Microsoft.Hosting.Lifetime": "Information"
}
}
},
"Logs": {
"SqlServer": {
"Enabled": false,
"Connection": "Server=#########;Database=#########;User Id=#########;Password=#########;MultipleActiveResultSets=true"
},
"Seq": {
"Enabled": false,
"Url": "",
"ApiKey": ""
} }
}, },
"AllowedHosts": "*", "AllowedHosts": "*",
@ -30,7 +18,7 @@
"Code": "DEV_AGENT" "Code": "DEV_AGENT"
}, },
"IdentityServer": { "IdentityServer": {
"BaseAddress": "http://<host>:<port>/api/" "BaseAddress": "https://lab.code-rove.com/identity-server-api/"
}, },
"Restrictions": { "Restrictions": {
"EnforceActionOwner": true "EnforceActionOwner": true

View File

@ -31,7 +31,7 @@ namespace NetworkResurrector.Api
var exitCode = 0; var exitCode = 0;
try try
{ {
var urls = builder.Configuration.GetValue<string>("Urls"); var urls = builder.Configuration.GetValue<string>("urls");
Log.Information("Starting network resurrector API..."); Log.Information("Starting network resurrector API...");
Log.Information($"Network resurrector API listening on {urls}"); Log.Information($"Network resurrector API listening on {urls}");
Console.WriteLine("Application started. Press Ctrl+C to shut down."); Console.WriteLine("Application started. Press Ctrl+C to shut down.");

View File

@ -1,8 +0,0 @@
namespace NetworkResurrector.Server.Extensions
{
internal static class DataTypeExtensions
{
public static string Nullify(this string value)
=> string.IsNullOrWhiteSpace(value) ? null : value;
}
}

View File

@ -1,41 +0,0 @@
using Microsoft.Extensions.Configuration;
using Serilog;
using Serilog.Configuration;
using Serilog.Sinks.MSSqlServer;
using System;
namespace NetworkResurrector.Server.Extensions
{
internal static class LoggingExtensions
{
internal static LoggerSinkConfiguration ConfiguredDestinations(this LoggerSinkConfiguration writeTo, IConfiguration configuration)
{
var sqlServerEnabled = configuration.GetValue<bool>("Logs:SqlServer:Enabled");
var sqlServerConnection = configuration.GetValue<string>("Logs:SqlServer:Connection");
var seqEnabled = configuration.GetValue<bool>("Logs:Seq:Enabled");
var seqUrl = configuration.GetValue<string>("Logs:Seq:Url");
var seqApiKey = configuration.GetValue<string>("Logs:Seq:ApiKey");
if (sqlServerEnabled && string.IsNullOrWhiteSpace(sqlServerConnection))
throw new Exception("If SqlServer logging is enabled, the SqlServer connection must be configured.");
if (seqEnabled && string.IsNullOrWhiteSpace(seqUrl))
throw new Exception("If Seq logging is enabled, the Seq URL must be configured.");
if (sqlServerEnabled)
{
var columnOptions = new ColumnOptions();
columnOptions.Store.Remove(StandardColumn.Properties);
columnOptions.Store.Remove(StandardColumn.MessageTemplate);
columnOptions.Store.Add(StandardColumn.LogEvent);
var mssqlSinkOptions = new MSSqlServerSinkOptions() { AutoCreateSqlTable = true, TableName = "__Logs" };
writeTo.MSSqlServer(sqlServerConnection, mssqlSinkOptions, columnOptions: columnOptions);
}
if (seqEnabled)
writeTo.Seq(seqUrl, apiKey: seqApiKey.Nullify());
return writeTo;
}
}
}

View File

@ -15,7 +15,6 @@
<PackageReference Include="Serilog.AspNetCore" Version="$(SerilogPackageVersion)" /> <PackageReference Include="Serilog.AspNetCore" Version="$(SerilogPackageVersion)" />
<PackageReference Include="Serilog.Extensions.Logging" Version="$(SerilogExtensionsPackageVersion)" /> <PackageReference Include="Serilog.Extensions.Logging" Version="$(SerilogExtensionsPackageVersion)" />
<PackageReference Include="Serilog.Sinks.MSSqlServer" Version="$(SerilogSinksMSSqlServerPackageVersion)" /> <PackageReference Include="Serilog.Sinks.MSSqlServer" Version="$(SerilogSinksMSSqlServerPackageVersion)" />
<PackageReference Include="Serilog.Sinks.Seq" Version="$(SerilogSinksSeqPackageVersion)" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="$(AutoMapperExtensionsPackageVersion)" /> <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="$(AutoMapperExtensionsPackageVersion)" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="$(MediatRPackageVersion)" /> <PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="$(MediatRPackageVersion)" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="$(MicrosoftExtensionsHostingPackageVersion)" /> <PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="$(MicrosoftExtensionsHostingPackageVersion)" />

View File

@ -1,10 +1,14 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using NetworkResurrector.Server.Extensions;
using Serilog; using Serilog;
using Serilog.Core;
using Serilog.Events;
using Serilog.Sinks.MSSqlServer;
using System; using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
namespace NetworkResurrector.Server namespace NetworkResurrector.Server
{ {
@ -12,46 +16,76 @@ namespace NetworkResurrector.Server
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
var builder = WebApplication.CreateBuilder(args); var isConsole = Debugger.IsAttached || args.Contains("--console");
if (!isConsole)
builder.Host.UseSerilog((_, lc) =>
{ {
lc var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
.ReadFrom.Configuration(builder.Configuration) var pathToContentRoot = Path.GetDirectoryName(pathToExe);
Directory.SetCurrentDirectory(pathToContentRoot);
}
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
var connectionString = configuration.GetConnectionString("DatabaseConnection");
var loggingLevelParam = configuration.GetValue<string>("Logging:LogLevel:Default");
var loggingLevelOk = Enum.TryParse(loggingLevelParam, out LogEventLevel loggingLevel);
if (!loggingLevelOk)
throw new Exception($"Logging level '{loggingLevelParam}' is not valid.");
var loggingLevelSwitch = new LoggingLevelSwitch(loggingLevel);
var columnOptions = new ColumnOptions();
columnOptions.Store.Remove(StandardColumn.Properties);
columnOptions.Store.Remove(StandardColumn.MessageTemplate);
columnOptions.Store.Add(StandardColumn.LogEvent);
var mssqlSinkOptions = new MSSqlServerSinkOptions() { AutoCreateSqlTable = true, TableName = "__Logs" };
Log.Logger = new LoggerConfiguration()
.MinimumLevel.ControlledBy(loggingLevelSwitch)
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.Enrich.FromLogContext() .Enrich.FromLogContext()
.WriteTo.Console() .WriteTo.Console()
.WriteTo.ConfiguredDestinations(builder.Configuration); .WriteTo.MSSqlServer(connectionString, mssqlSinkOptions, columnOptions: columnOptions)
}); .CreateLogger();
builder.Services.ConfigureServices(builder.Configuration);
var useWindowsService = builder.Configuration.GetValue<bool>("Host:UseWindowsService");
if (useWindowsService)
builder.Host.UseWindowsService();
var app = builder.Build();
app.Configure();
var exitCode = 0;
try try
{ {
var urls = builder.Configuration.GetValue<string>("Urls"); var urls = configuration.GetValue<string>("urls");
Log.Information("Starting network resurrector server..."); Log.Information("Starting network resurrector API...");
Log.Information($"Server listening on {urls}"); Log.Information($"API listening on {urls}");
Console.WriteLine("Application started. Press Ctrl+C to shut down."); Console.WriteLine("Application started. Press Ctrl+C to shut down.");
app.Run(); CreateHostBuilder(args, configuration, !isConsole).Build().Run();
} }
catch (Exception ex) catch (Exception ex)
{ {
Log.Fatal(ex, "Network resurrector server host terminated unexpectedly"); Log.Fatal(ex, "Network resurrector API host terminated unexpectedly");
} }
finally finally
{ {
Log.CloseAndFlush(); Log.CloseAndFlush();
} }
}
Environment.Exit(exitCode); public static IHostBuilder CreateHostBuilder(string[] args, IConfiguration configuration, bool useWindowsService)
{
var builder = Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>()
.UseConfiguration(configuration)
.UseSerilog();
});
if (useWindowsService)
builder.UseWindowsService();
return builder;
} }
} }
} }

View File

@ -9,20 +9,29 @@ using Netmash.Extensions.Swagger;
using Netmash.Extensions.Swagger.Constants; using Netmash.Extensions.Swagger.Constants;
using Netmash.Security.Authentication.Identity; using Netmash.Security.Authentication.Identity;
using NetworkResurrector.Server.Application; using NetworkResurrector.Server.Application;
using NetworkResurrector.Server.Extensions;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Reflection; using System.Reflection;
namespace NetworkResurrector.Server.Extensions namespace NetworkResurrector.Server
{ {
public static class StartupExtensions public class Startup
{ {
public static void ConfigureServices(this IServiceCollection services, IConfiguration configuration) private readonly IConfiguration _configuration;
public Startup(IConfiguration configuration)
{
_configuration = configuration;
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{ {
services.AddControllers() services.AddControllers()
.AddNewtonsoftJson(o => o.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc); .AddNewtonsoftJson(o => o.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc);
// Add basic authentication // Add basic authentication
services.AddIdentityAuthentication(configuration.GetSection("IdentityServer")["BaseAddress"]); services.AddIdentityAuthentication(_configuration.GetSection("IdentityServer")["BaseAddress"]);
services.AddHttpContextAccessor(); services.AddHttpContextAccessor();
@ -42,11 +51,11 @@ namespace NetworkResurrector.Server.Extensions
services.AddApplicationServices(); services.AddApplicationServices();
// WakeOnLan // WakeOnLan
services.AddWakeOnLan(configuration); services.AddWakeOnLan(_configuration);
} }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public static void Configure(this IApplicationBuilder app) public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{ {
// global cors policy // global cors policy
app.UseCors(x => x app.UseCors(x => x
@ -54,6 +63,11 @@ namespace NetworkResurrector.Server.Extensions
.AllowAnyMethod() .AllowAnyMethod()
.AllowAnyHeader()); .AllowAnyHeader());
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting(); app.UseRouting();
app.UseAuthentication(); app.UseAuthentication();
app.UseAuthorization(); app.UseAuthorization();
@ -64,7 +78,7 @@ namespace NetworkResurrector.Server.Extensions
app.ConfigureSwagger("NetworkResurrector Server API"); app.ConfigureSwagger("NetworkResurrector Server API");
} }
private static Assembly[] GetMediatRAssemblies() private Assembly[] GetMediatRAssemblies()
{ {
var assembly = typeof(Application.CommandHandlers.PingMachineHandler).Assembly; var assembly = typeof(Application.CommandHandlers.PingMachineHandler).Assembly;
return new Assembly[] { assembly }; return new Assembly[] { assembly };

View File

@ -1,33 +1,19 @@
{ {
"Urls": "http://*:5062", "urls": "http://*:5062",
"ConnectionStrings": { "ConnectionStrings": {
"DatabaseConnection": "Server=#########;Database=#########;User Id=#########;Password=#########;MultipleActiveResultSets=true" "DatabaseConnection": "Server=#########;Database=#########;User Id=#########;Password=#########;MultipleActiveResultSets=true"
}, },
"Host": { "Logging": {
"UseWindowsService": false "LogLevel": {
}, "Default": "Debug",
"Serilog": { "Microsoft": "Warning",
"MinimumLevel": { "Microsoft.Hosting.Lifetime": "Information"
"Default": "Information",
"Override": {
"Microsoft": "Information"
}
}
},
"Logs": {
"SqlServer": {
"Enabled": false,
"Connection": "Server=#########;Database=#########;User Id=#########;Password=#########;MultipleActiveResultSets=true"
},
"Seq": {
"Enabled": false,
"Url": "",
"ApiKey": ""
} }
}, },
"AllowedHosts": "*", "AllowedHosts": "*",
"IdentityServer": { "IdentityServer": {
"BaseAddress": "http://<host>:<port>/api/" //"BaseAddress": "http://localhost:5063/"
"BaseAddress": "https://lab.code-rove.com/identity-server-api/"
}, },
"WakeOnLan": { "WakeOnLan": {
"Provider": { "Provider": {
@ -35,5 +21,5 @@
"Options": [ "Inhouse", "Nikeee" ] "Options": [ "Inhouse", "Nikeee" ]
} }
}, },
"Shutdown": {} "Shutdown": { }
} }