예제 #1
0
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            if (value != null)
            {
                var cert = (CertificateOptions)value;

                if (!string.IsNullOrEmpty(cert.Pfx) && !X509.TryValidate(cert.Pfx, cert.Password, out var certResult))
                {
                    return(new ValidationResult($"Invalid HTTPs certificate: {certResult}"));
                }
            }

            return(ValidationResult.Success);
        }
예제 #2
0
        /// <summary>
        ///     Entrypoint.
        /// </summary>
        /// <param name="args">Command line arguments.</param>
        public static void Main(string[] args)
        {
            // populate the properties above so that we can override the default config file if needed, and to
            // check if the application is being run in command mode (run task and quit).
            EnvironmentVariables.Populate(prefix: EnvironmentVariablePrefix);
            Arguments.Populate(clearExistingValues: false);

            if (ShowVersion)
            {
                Console.WriteLine(Version);
                return;
            }

            if (ShowHelp || ShowEnvironmentVariables)
            {
                if (!NoLogo)
                {
                    PrintLogo(Version);
                }

                if (ShowHelp)
                {
                    PrintCommandLineArguments(typeof(Options));
                }

                if (ShowEnvironmentVariables)
                {
                    PrintEnvironmentVariables(typeof(Options), EnvironmentVariablePrefix);
                }

                return;
            }

            if (GenerateCertificate)
            {
                GenerateX509Certificate(password: Guid.NewGuid().ToString(), filename: $"{AppName}.pfx");
                return;
            }

            // the application isn't bein run in command mode. load all configuration values and proceed
            // with bootstrapping.
            try
            {
                Configuration = new ConfigurationBuilder()
                                .AddConfigurationProviders(EnvironmentVariablePrefix, ConfigurationFile)
                                .Build();

                Configuration.GetSection(AppName)
                .Bind(Options, (o) => { o.BindNonPublicProperties = true; });

                if (!Options.TryValidate(out var result))
                {
                    Console.WriteLine(result.GetResultView("Invalid configuration:"));
                    return;
                }

                var cert = Options.Web.Https.Certificate;

                if (!string.IsNullOrEmpty(cert.Pfx) && !X509.TryValidate(cert.Pfx, cert.Password, out var certResult))
                {
                    Console.WriteLine($"Invalid HTTPs certificate: {certResult}");
                    return;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Invalid configuration: {(!Options.Debug ? ex : ex.Message)}");
                return;
            }

            if (!Options.NoLogo)
            {
                PrintLogo(Version);
            }

            if (Options.Debug)
            {
                Console.WriteLine("Configuration:");
                Console.WriteLine(Configuration.GetDebugView());

                Log.Logger = new LoggerConfiguration()
                             .MinimumLevel.Debug()
                             .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
                             .MinimumLevel.Override("slskd.API.Authentication.PassthroughAuthenticationHandler", LogEventLevel.Information)
                             .Enrich.WithProperty("Version", Version)
                             .Enrich.WithProperty("InstanceName", Options.InstanceName)
                             .Enrich.WithProperty("InvocationId", InvocationId)
                             .Enrich.WithProperty("ProcessId", ProcessId)
                             .Enrich.FromLogContext()
                             .WriteTo.Console(
                    outputTemplate: "[{SourceContext}] [{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}")
                             .WriteTo.Async(config =>
                                            config.File(
                                                Path.Combine(AppContext.BaseDirectory, "logs", $"{AppName}-.log"),
                                                outputTemplate: "[{SourceContext}] [{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}",
                                                rollingInterval: RollingInterval.Day))
                             .WriteTo.Conditional(
                    e => !string.IsNullOrEmpty(Options.Logger.Loki),
                    config => config.GrafanaLoki(
                        Options.Logger.Loki ?? string.Empty,
                        outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"))
                             .CreateLogger();
            }
            else
            {
                Log.Logger = new LoggerConfiguration()
                             .MinimumLevel.Information()
                             .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
                             .MinimumLevel.Override("slskd.API.Authentication.PassthroughAuthenticationHandler", LogEventLevel.Information)
                             .Enrich.WithProperty("Version", Version)
                             .Enrich.WithProperty("InstanceName", Options.InstanceName)
                             .Enrich.WithProperty("InvocationId", InvocationId)
                             .Enrich.WithProperty("ProcessId", ProcessId)
                             .Enrich.FromLogContext()
                             .WriteTo.Console(
                    outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}")
                             .WriteTo.Async(config =>
                                            config.File(
                                                Path.Combine(AppContext.BaseDirectory, "logs", $"{AppName}-.log"),
                                                outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}",
                                                rollingInterval: RollingInterval.Day))
                             .WriteTo.Conditional(
                    e => !string.IsNullOrEmpty(Options.Logger.Loki),
                    config => config.GrafanaLoki(
                        Options.Logger.Loki ?? string.Empty,
                        outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"))
                             .CreateLogger();
            }

            var logger = Log.ForContext(typeof(Program));

            if (ConfigurationFile != DefaultConfigurationFile && !File.Exists(ConfigurationFile))
            {
                logger.Warning($"Specified configuration file '{ConfigurationFile}' could not be found and was not loaded.");
            }

            logger.Information("Version: {Version}", Version);
            logger.Information("Instance Name: {InstanceName}", Options.InstanceName);
            logger.Information("Invocation ID: {InvocationId}", InvocationId);
            logger.Information("Process ID: {ProcessId}", ProcessId);

            if (!string.IsNullOrEmpty(Options.Logger.Loki))
            {
                logger.Information("Forwarding logs to Grafana Loki instance at {LoggerLokiUrl}", Options.Logger.Loki);
            }

            try
            {
                if (Options.Feature.Prometheus)
                {
                    using var runtimeMetrics = DotNetRuntimeStatsBuilder.Default().StartCollecting();
                }

                WebHost.CreateDefaultBuilder(args)
                .SuppressStatusMessages(true)
                .ConfigureAppConfiguration((hostingContext, builder) =>
                {
                    builder.Sources.Clear();
                    builder.AddConfigurationProviders(EnvironmentVariablePrefix, ConfigurationFile);
                })
                .UseSerilog()
                .UseUrls()
                .UseKestrel(options =>
                {
                    logger.Information($"Listening for HTTP requests at http://{IPAddress.Any}:{Options.Web.Port}/");
                    options.Listen(IPAddress.Any, Options.Web.Port);

                    logger.Information($"Listening for HTTPS requests at https://{IPAddress.Any}:{Options.Web.Https.Port}/");
                    options.Listen(IPAddress.Any, Options.Web.Https.Port, listenOptions =>
                    {
                        var cert = Options.Web.Https.Certificate;

                        if (!string.IsNullOrEmpty(cert.Pfx))
                        {
                            logger.Information($"Using certificate from {cert.Pfx}");
                            listenOptions.UseHttps(cert.Pfx, cert.Password);
                        }
                        else
                        {
                            logger.Information($"Using randomly generated self-signed certificate");
                            listenOptions.UseHttps(X509.Generate(subject: AppName));
                        }
                    });
                })
                .UseStartup <Startup>()
                .Build()
                .Run();
            }
            catch (Exception ex)
            {
                logger.Fatal(ex, "Application terminated unexpectedly");
            }
            finally
            {
                Log.CloseAndFlush();
            }
        }