1 using Byond.TopicSender;
2 using Cyberboss.AspNetCore.AsyncInitializer;
3 using Microsoft.AspNetCore.Authentication.JwtBearer;
4 using Microsoft.AspNetCore.Builder;
5 using Microsoft.AspNetCore.Cors.Infrastructure;
6 using Microsoft.AspNetCore.Hosting;
7 using Microsoft.AspNetCore.Identity;
8 using Microsoft.Extensions.Configuration;
9 using Microsoft.Extensions.DependencyInjection;
10 using Microsoft.Extensions.Hosting;
11 using Microsoft.Extensions.Logging;
12 using Microsoft.Extensions.Options;
13 using Microsoft.Extensions.Primitives;
14 using Newtonsoft.Json;
17 using Serilog.Formatting.Display;
19 using System.Globalization;
20 using System.IdentityModel.Tokens.Jwt;
21 using System.Threading.Tasks;
46 #pragma warning disable CA1506 65 IConfiguration configuration,
66 IWebHostEnvironment hostingEnvironment)
69 this.hostingEnvironment = hostingEnvironment ??
throw new ArgumentNullException(nameof(hostingEnvironment));
88 ConfigureServices(services);
90 if (postSetupServices == null)
91 throw new ArgumentNullException(nameof(postSetupServices));
98 services.AddOptions();
100 static LogEventLevel? ConvertSeriLogLevel(LogLevel logLevel) =>
103 LogLevel.Critical => LogEventLevel.Fatal,
104 LogLevel.Debug => LogEventLevel.Debug,
105 LogLevel.Error => LogEventLevel.Error,
106 LogLevel.Information => LogEventLevel.Information,
107 LogLevel.Trace => LogEventLevel.Verbose,
108 LogLevel.Warning => LogEventLevel.Warning,
109 LogLevel.None => null,
110 _ =>
throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture,
"Invalid log level {0}", logLevel)),
114 services.SetupLogging(
117 if (microsoftEventLevel.HasValue)
118 config.MinimumLevel.Override(
"Microsoft", microsoftEventLevel.Value);
128 : IOManager.ConcatPath(
129 Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
135 var formatter =
new MessageTemplateTextFormatter(
138 +
": [{Level:u3}] {SourceContext:l}: {Message} ({EventId:x8}){NewLine}{Exception}",
141 logPath = IOManager.ConcatPath(logPath,
"tgs-.log");
142 var rollingFileConfig = sinkConfig.File(
145 logEventLevel ?? LogEventLevel.Verbose,
147 flushToDiskInterval: TimeSpan.FromSeconds(2),
148 rollingInterval: RollingInterval.Day,
149 rollOnFileSizeLimit:
true);
153 services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(jwtBearerOptions =>
158 jwtBearerOptions.Events =
new JwtBearerEvents
162 OnTokenValidated = ctx => ctx
166 .InjectClaimsIntoContext(
168 ctx.HttpContext.RequestAborted)
175 JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
181 options.EnableEndpointRouting =
false;
183 .AddNewtonsoftJson(options =>
185 options.AllowInputFormatterExceptionMessages =
true;
186 options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
187 options.SerializerSettings.CheckAdditionalContent =
true;
188 options.SerializerSettings.MissingMemberHandling = MissingMemberHandling.Error;
189 options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
193 if (hostingEnvironment.IsDevelopment())
195 static string GetDocumentationFilePath(
string assemblyLocation) => IOManager.ConcatPath(IOManager.GetDirectoryName(assemblyLocation), String.Concat(IOManager.GetFileNameWithoutExtension(assemblyLocation),
".xml"));
196 var assemblyDocumentationPath = GetDocumentationFilePath(typeof(
Application).Assembly.Location);
197 var apiDocumentationPath = GetDocumentationFilePath(typeof(
ApiHeaders).Assembly.Location);
199 services.AddSwaggerGenNewtonsoftSupport();
203 services.AddDetectionCore().AddBrowser();
210 services.AddDbContext<TContext>(builder =>
212 if (hostingEnvironment.IsDevelopment())
213 builder.EnableSensitiveDataLogging();
215 services.AddScoped<
IDatabaseContext>(x => x.GetRequiredService<TContext>());
224 AddTypedContext<MySqlDatabaseContext>();
227 AddTypedContext<SqlServerDatabaseContext>();
230 AddTypedContext<SqliteDatabaseContext>();
233 AddTypedContext<PostgresSqlDatabaseContext>();
236 throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture,
"Invalid {0}: {1}!", nameof(
DatabaseType), dbType));
249 services.AddSingleton<IPasswordHasher<Models.User>, PasswordHasher<Models.User>>();
285 services.AddSingleton<ITopicClient, TopicClient>();
286 services.AddSingleton(
new SocketParameters
310 protected override void ConfigureHostedService(IServiceCollection services)
324 IApplicationBuilder applicationBuilder,
328 IOptions<ControlPanelConfiguration> controlPanelConfigurationOptions,
329 IOptions<GeneralConfiguration> generalConfigurationOptions,
330 ILogger<Application> logger)
332 if (applicationBuilder == null)
333 throw new ArgumentNullException(nameof(applicationBuilder));
334 if (serverControl == null)
335 throw new ArgumentNullException(nameof(serverControl));
337 this.tokenFactory = tokenFactory ??
throw new ArgumentNullException(nameof(tokenFactory));
339 var controlPanelConfiguration = controlPanelConfigurationOptions?.Value ??
throw new ArgumentNullException(nameof(controlPanelConfigurationOptions));
340 var generalConfiguration = generalConfigurationOptions?.Value ??
throw new ArgumentNullException(nameof(generalConfigurationOptions));
343 throw new ArgumentNullException(nameof(logger));
345 logger.LogDebug(
"Content Root: {0}", hostingEnvironment.ContentRootPath);
346 logger.LogTrace(
"Web Root: {0}", hostingEnvironment.WebRootPath);
350 logger.LogCritical(
"Configured minimum password length ({0}) is greater than the maximum database string length ({1})!");
351 serverControl.
Die(
new InvalidOperationException(
"Minimum password length greater than database limit!"));
357 ChangeToken.OnChange(Configuration.GetReloadToken, () => serverControl.
Restart());
361 applicationBuilder.UseServerErrorHandling();
364 applicationBuilder.UseAsyncInitialization(async (cancellationToken) =>
366 var tcs =
new TaskCompletionSource<object>();
367 using (cancellationToken.Register(() => tcs.SetCanceled()))
368 await Task.WhenAny(tcs.Task, instanceManager.
Ready).ConfigureAwait(
false);
372 applicationBuilder.UseCancelledRequestSuppression();
374 if (hostingEnvironment.IsDevelopment())
376 applicationBuilder.UseSwagger();
377 applicationBuilder.UseSwaggerUI(c => c.SwaggerEndpoint(
"/swagger/v1/swagger.json",
"TGS API V4"));
378 logger.LogTrace(
"Swagger API generation enabled");
382 Action<CorsPolicyBuilder> corsBuilder = null;
383 if (controlPanelConfiguration.AllowAnyOrigin)
385 logger.LogTrace(
"Access-Control-Allow-Origin: *");
386 corsBuilder = builder => builder.AllowAnyOrigin();
388 else if (controlPanelConfiguration.AllowedOrigins?.Count > 0)
390 logger.LogTrace(
"Access-Control-Allow-Origin: ", String.Join(
',', controlPanelConfiguration.AllowedOrigins));
391 corsBuilder = builder => builder.WithOrigins(controlPanelConfiguration.AllowedOrigins.ToArray());
394 var originalBuilder = corsBuilder;
395 corsBuilder = builder =>
397 builder.AllowAnyHeader().AllowAnyMethod();
398 originalBuilder?.Invoke(builder);
400 applicationBuilder.UseCors(corsBuilder);
403 if (controlPanelConfiguration.Enable)
405 logger.LogWarning(
"Web control panel enabled. This is a highly WIP feature!");
406 applicationBuilder.UseStaticFiles();
409 logger.LogDebug(
"Web control panel disabled!");
412 applicationBuilder.UseAuthentication();
415 applicationBuilder.UseDbConflictHandling();
418 applicationBuilder.UseMvc();
LogLevel MicrosoftLogLevel
The minimum Microsoft.Extensions.Logging.LogLevel to display in logs for Microsoft library sources ...
For creating and accessing authentication contexts
ISymlinkFactory for windows systems
const int MaximumStringLength
Length limit for strings in fields.
IPostWriteHandler for POSIX systems
Handles changing file modes/permissions after writing
bool Disable
If file logging is disabled
void ConfigureServices(IServiceCollection services, IPostSetupServices postSetupServices)
Configure the Application's services.
Factory for scoping usage of IDatabaseContexts. Meant for use by Components
For low level interactions with a LibGit2Sharp.IRepository.
bool WatchdogPresent
if live updates are supported, . ApplyUpdate(Version, Uri, IIOManager) and Restart will fail if this ...
Task Die(Exception exception)
Kill the server with a fatal exception
LogLevel LogLevel
The minimum Microsoft.Extensions.Logging.LogLevel to display in logs
const string SerilogContextTemplate
Common template used for adding our custom log context to serilog.
For initially seeding a database
string Directory
Where log files are stored
ITokenFactory tokenFactory
The ITokenFactory for the Application
IPlatformIdentifier PlatformIdentifier
The IPlatformIdentifier.
Sanity limits to prevent users from overloading
For creating filesystem symbolic links
Sets up dependency injection.
Implements various filters for Swashbuckle.
Implementation of IServerFactory.
ISystemIdentityFactory for windows systems. Uses long running tasks due to potential networked domain...
Task Ready
Task that completes when the IInstanceManager finishes initializing.
Contains various cryptographic functions
For creating IGitHubClients
Backend abstract implementation of IDatabaseContext
ISymlinkFactory for posix systems
Application(IConfiguration configuration, IWebHostEnvironment hostingEnvironment)
Construct an Application
Extensions for IServiceCollection
Task Restart()
Restarts the Host
readonly IWebHostEnvironment hostingEnvironment
The IWebHostEnvironment for the Application.
Factory for creating LibGit2Sharp.IRepositorys.
DatabaseConfiguration DatabaseConfiguration
The Configuration.DatabaseConfiguration.
Configuration for the automatic update system
For downloading and installing BYOND extractions for a given system
void Configure(IApplicationBuilder applicationBuilder, IServerControl serverControl, ITokenFactory tokenFactory, IInstanceManager instanceManager, IOptions< ControlPanelConfiguration > controlPanelConfigurationOptions, IOptions< GeneralConfiguration > generalConfigurationOptions, ILogger< Application > logger)
Configure the Application
For accessing the disk in a synchronous manner
DatabaseType DatabaseType
The Configuration.DatabaseType to create
Factory for creating IInstances
IByondInstaller for Posix systems
Factory for ISystemIdentitys
bool UseBasicWatchdogOnWindows
If the Components.Watchdog.WindowsWatchdog should not be used if it is available. ...
IPostWriteHandler for Windows systems
DI root for configuring a SetupWizard.
Configuration options for the web control panel
IWatchdogFactory for creating WindowsWatchdogs.
For creating IChatManagers
Set of objects needed to configure an Core.Application.
JsonConverter for serializing Versions for BYOND.
static void Configure(SwaggerGenOptions swaggerGenOptions, string assemblyDocumentationPath, string apiDocumentationPath)
Configure the swagger settings.
GeneralConfiguration GeneralConfiguration
The Configuration.GeneralConfiguration.
TokenValidationParameters ValidationParameters
The TokenValidationParameters for the ITokenFactory
ISystemIdentityFactory for posix systems
Manages the runtime of Jobs
int ByondTopicTimeout
The timeout in milliseconds for sending and receiving topics to/from DreamDaemon. Note that a single ...
On Windows, DreamDaemon will show an unskippable prompt when using /world/proc/OpenPort(). This looks out for those prompts and immediately clicks "Yes" if the owning process has registered for it
For injecting global::System.Security.Claims.Claims that Controllers.TgsAuthorizeAttribute can look f...
Represents a BYOND installation
DatabaseType
Type of database to user
FileLoggingConfiguration FileLoggingConfiguration
The Configuration.FileLoggingConfiguration.
Abstraction for suspending and resuming processes.
Provides access to the server's HttpApiPort.
Handler for BridgeParameters.
For launching IProcess'
Represents a service that may take an updated Host assembly and run it, stopping the current assembly...
IByondInstaller for windows systems
For caching ISystemIdentitys