2using System.Collections.Generic;
3using System.Data.Common;
4using System.Data.SqlClient;
5using System.Globalization;
9using System.Text.RegularExpressions;
10using System.Threading;
11using System.Threading.Tasks;
13using Microsoft.Data.Sqlite;
14using Microsoft.Extensions.Configuration;
15using Microsoft.Extensions.Hosting;
16using Microsoft.Extensions.Logging;
17using Microsoft.Extensions.Options;
29using YamlDotNet.Serialization;
108 IConfiguration configuration,
109 IOptions<GeneralConfiguration> generalConfigurationOptions)
112 this.console =
console ??
throw new ArgumentNullException(nameof(
console));
119 ArgumentNullException.ThrowIfNull(configuration);
121 generalConfiguration = generalConfigurationOptions?.Value ??
throw new ArgumentNullException(nameof(generalConfigurationOptions));
125 .RegisterChangeCallback(
131 public async Task
StartAsync(CancellationToken cancellationToken)
138 public Task
StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
146 async Task<bool>
PromptYesNo(
string question, CancellationToken cancellationToken)
152 var upperResponse = responseString.ToUpperInvariant();
153 if (upperResponse ==
"Y" || upperResponse ==
"YES")
155 else if (upperResponse ==
"N" || upperResponse ==
"NO")
170 await
console.
WriteAsync(
"What port would you like to connect to TGS on?",
true, cancellationToken);
171 await
console.
WriteAsync(
"Note: If this is a docker container with the default port already mapped, use the default.",
true, cancellationToken);
176 $
"API Port (leave blank for default of {GeneralConfiguration.DefaultApiPort}): ",
180 if (String.IsNullOrWhiteSpace(portString))
182 if (UInt16.TryParse(portString, out var port) && port != 0)
184 await
console.
WriteAsync(
"Invalid port! Please enter a value between 1 and 65535",
true, cancellationToken);
199 DbConnection testConnection,
203 CancellationToken cancellationToken)
205 bool isSqliteDB = databaseConfiguration.DatabaseType ==
DatabaseType.Sqlite;
206 using (testConnection)
209 await testConnection.OpenAsync(cancellationToken);
216 await
console.
WriteAsync($
"Checking {databaseConfiguration.DatabaseType} version...",
true, cancellationToken);
217 using var command = testConnection.CreateCommand();
218 command.CommandText =
"SELECT VERSION()";
219 var fullVersion = (string)await command.ExecuteScalarAsync(cancellationToken);
220 await
console.
WriteAsync(String.Format(CultureInfo.InvariantCulture,
"Found {0}", fullVersion),
true, cancellationToken);
224 var splits = fullVersion.Split(
' ');
225 databaseConfiguration.ServerVersion = splits[1].TrimEnd(
',');
229 var splits = fullVersion.Split(
'-');
230 databaseConfiguration.ServerVersion = splits.First();
234 if (!isSqliteDB && !dbExists)
236 await
console.
WriteAsync(
"Testing create DB permission...",
true, cancellationToken);
237 using (var command = testConnection.CreateCommand())
240#pragma warning disable CA2100
241 command.CommandText = $
"CREATE DATABASE {databaseName}";
242#pragma warning restore CA2100
243 await command.ExecuteNonQueryAsync(cancellationToken);
248 using (var command = testConnection.CreateCommand())
250#pragma warning disable CA2100
251 command.CommandText = $
"DROP DATABASE {databaseName}";
252#pragma warning restore CA2100
255 await command.ExecuteNonQueryAsync(cancellationToken);
257 catch (OperationCanceledException)
265 await
console.
WriteAsync(
"This should be okay, but you may want to manually drop the database before continuing!",
true, cancellationToken);
266 await
console.
WriteAsync(
"Press any key to continue...",
true, cancellationToken);
272 await testConnection.CloseAsync();
275 if (isSqliteDB && !dbExists)
277 await
console.
WriteAsync(
"Deleting test database file...",
true, cancellationToken);
279 SqliteConnection.ClearAllPools();
304 if (!directoryExisted)
314 if (!Path.IsPathRooted(databaseName))
316 await
console.
WriteAsync(
"Note, this relative path (currently) resolves to the following:",
true, cancellationToken);
319 "Would you like to save the relative path in the configuration? If not, the full path will be saved. (y/n): ",
323 databaseName = resolvedPath;
342 "NOTE: It is HIGHLY reccommended that TGS runs on a complete relational database, specfically *NOT* Sqlite.",
346 "Sqlite, by nature cannot perform several DDL operations. Because of this future compatiblility cannot be guaranteed.",
350 "This means that you may not be able to update to the next minor version of TGS without a clean re-installation!",
354 "Please consider taking the time to set up a relational database if this is meant to be a long-standing server.",
362 await
console.
WriteAsync(
"What SQL database type will you be using?",
true, cancellationToken);
367 CultureInfo.InvariantCulture,
368 "Please enter one of {0}, {1}, {2}, {3} or {4}: ",
377 if (Enum.TryParse<
DatabaseType>(databaseTypeString, out var databaseType))
390#pragma warning disable CA1502
393 bool firstTime =
true;
404 string serverAddress =
null;
405 ushort? serverPort =
null;
407 bool isSqliteDB = databaseConfiguration.DatabaseType ==
DatabaseType.Sqlite;
412 await
console.
WriteAsync(
"Enter the server's address and port [<server>:<port> or <server>] (blank for local): ",
false, cancellationToken);
414 if (String.IsNullOrWhiteSpace(serverAddress))
415 serverAddress =
null;
416 else if (databaseConfiguration.DatabaseType ==
DatabaseType.SqlServer)
418 var match = Regex.Match(serverAddress,
@"^(?<server>.+):(?<port>.+)$");
421 serverAddress = match.Groups[
"server"].Value;
422 var portString = match.Groups[
"port"].Value;
423 if (UInt16.TryParse(portString, out var port))
427 await
console.
WriteAsync($
"Failed to parse port \"{portString}\", please try again.",
true, cancellationToken);
438 await
console.
WriteAsync($
"Enter the database {(isSqliteDB ? "file path
" : "name
")} (Can be from previous installation. Otherwise, should not exist): ",
false, cancellationToken);
441 bool dbExists =
false;
445 if (!String.IsNullOrWhiteSpace(databaseName))
454 dbExists = await
PromptYesNo(
"Does this database already exist? If not, we will attempt to CREATE it. (y/n): ", cancellationToken);
457 if (String.IsNullOrWhiteSpace(databaseName))
466 useWinAuth = await
PromptYesNo(
"Use Windows Authentication? (y/n): ", cancellationToken);
472 string username =
null;
473 string password =
null;
484 await
console.
WriteAsync(
"IMPORTANT: If using the service runner, ensure this computer's LocalSystem account has CREATE DATABASE permissions on the target server!",
true, cancellationToken);
485 await
console.
WriteAsync(
"The account it uses in MSSQL is usually \"NT AUTHORITY\\SYSTEM\" and the role it needs is usually \"dbcreator\".",
true, cancellationToken);
486 await
console.
WriteAsync(
"We'll run a sanity test here, but it won't be indicative of the service's permissions if that is the case",
true, cancellationToken);
491 DbConnection testConnection;
492 void CreateTestConnection(
string connectionString) =>
495 databaseConfiguration.DatabaseType);
497 switch (databaseConfiguration.DatabaseType)
501 var csb =
new SqlConnectionStringBuilder
504 DataSource = serverAddress ??
"(local)",
508 csb.IntegratedSecurity =
true;
511 csb.UserID = username;
512 csb.Password = password;
515 CreateTestConnection(csb.ConnectionString);
516 csb.InitialCatalog = databaseName;
517 databaseConfiguration.ConnectionString = csb.ConnectionString;
525 var csb =
new MySqlConnectionStringBuilder
527 Server = serverAddress ??
"127.0.0.1",
532 if (serverPort.HasValue)
533 csb.Port = serverPort.Value;
535 CreateTestConnection(csb.ConnectionString);
536 csb.Database = databaseName;
537 databaseConfiguration.ConnectionString = csb.ConnectionString;
543 var csb =
new SqliteConnectionStringBuilder
545 DataSource = databaseName,
546 Mode = dbExists ? SqliteOpenMode.ReadOnly : SqliteOpenMode.ReadWriteCreate,
549 CreateTestConnection(csb.ConnectionString);
550 databaseConfiguration.ConnectionString = csb.ConnectionString;
556 var csb =
new NpgsqlConnectionStringBuilder
559 Host = serverAddress ??
"127.0.0.1",
564 if (serverPort.HasValue)
565 csb.Port = serverPort.Value;
567 CreateTestConnection(csb.ConnectionString);
568 csb.Database = databaseName;
569 databaseConfiguration.ConnectionString = csb.ConnectionString;
574 throw new InvalidOperationException(
"Invalid DatabaseType!");
579 await
TestDatabaseConnection(testConnection, databaseConfiguration, databaseName, dbExists, cancellationToken);
581 return databaseConfiguration;
583 catch (OperationCanceledException)
591 await
console.
WriteAsync(
"Retrying database configuration...",
true, cancellationToken);
596#pragma warning restore CA1502
613 await
console.
WriteAsync(String.Format(CultureInfo.InvariantCulture,
"Minimum database user password length (leave blank for default of {0}): ", newGeneralConfiguration.MinimumPasswordLength),
false, cancellationToken);
615 if (String.IsNullOrWhiteSpace(passwordLengthString))
617 if (UInt32.TryParse(passwordLengthString, out var passwordLength) && passwordLength >= 0)
619 newGeneralConfiguration.MinimumPasswordLength = passwordLength;
623 await
console.
WriteAsync(
"Please enter a positive integer!",
true, cancellationToken);
630 await
console.
WriteAsync(String.Format(CultureInfo.InvariantCulture,
"Default timeout for sending and receiving BYOND topics (ms, 0 for infinite, leave blank for default of {0}): ", newGeneralConfiguration.ByondTopicTimeout),
false, cancellationToken);
632 if (String.IsNullOrWhiteSpace(topicTimeoutString))
634 if (UInt32.TryParse(topicTimeoutString, out var topicTimeout) && topicTimeout >= 0)
636 newGeneralConfiguration.ByondTopicTimeout = topicTimeout;
640 await
console.
WriteAsync(
"Please enter a positive integer!",
true, cancellationToken);
645 await
console.
WriteAsync(
"Enter a GitHub personal access token to bypass some rate limits (this is optional and does not require any scopes)",
true, cancellationToken);
646 await
console.
WriteAsync(
"GitHub personal access token: ",
false, cancellationToken);
647 newGeneralConfiguration.GitHubAccessToken = await
console.
ReadLineAsync(
true, cancellationToken);
648 if (String.IsNullOrWhiteSpace(newGeneralConfiguration.GitHubAccessToken))
649 newGeneralConfiguration.GitHubAccessToken =
null;
651 newGeneralConfiguration.HostApiDocumentation = await
PromptYesNo(
"Host API Documentation? (y/n): ", cancellationToken);
653 return newGeneralConfiguration;
665 fileLoggingConfiguration.Disable = !await
PromptYesNo(
"Enable file logging? (y/n): ", cancellationToken);
667 if (!fileLoggingConfiguration.Disable)
671 await
console.
WriteAsync(
"Log file directory path (leave blank for default): ",
false, cancellationToken);
673 if (String.IsNullOrWhiteSpace(fileLoggingConfiguration.Directory))
675 fileLoggingConfiguration.Directory =
null;
681 await
console.
WriteAsync(
"Testing directory access...",
true, cancellationToken);
685 var testFile =
ioManager.
ConcatPath(fileLoggingConfiguration.Directory, String.Format(CultureInfo.InvariantCulture,
"WizardAccesTest.{0}.deleteme", Guid.NewGuid()));
691 catch (OperationCanceledException)
697 await
console.
WriteAsync(String.Format(CultureInfo.InvariantCulture,
"Error deleting test log file: {0}", testFile),
true, cancellationToken);
704 catch (OperationCanceledException)
712 await
console.
WriteAsync(
"Please verify the path is valid and you have access to it!",
true, cancellationToken);
717 async Task<LogLevel?> PromptLogLevel(
string question)
723 await
console.
WriteAsync(String.Format(CultureInfo.InvariantCulture,
"Enter one of {0}/{1}/{2}/{3}/{4}/{5} (leave blank for default): ", nameof(LogLevel.Trace), nameof(LogLevel.Debug), nameof(LogLevel.Information), nameof(LogLevel.Warning), nameof(LogLevel.Error), nameof(LogLevel.Critical)),
false, cancellationToken);
725 if (String.IsNullOrWhiteSpace(responseString))
727 if (Enum.TryParse<LogLevel>(responseString, out var logLevel) && logLevel != LogLevel.None)
734 fileLoggingConfiguration.LogLevel = await PromptLogLevel(String.Format(CultureInfo.InvariantCulture,
"Enter the level limit for normal logs (default {0}).", fileLoggingConfiguration.LogLevel)) ?? fileLoggingConfiguration.LogLevel;
735 fileLoggingConfiguration.MicrosoftLogLevel = await PromptLogLevel(String.Format(CultureInfo.InvariantCulture,
"Enter the level limit for Microsoft logs (VERY verbose, default {0}).", fileLoggingConfiguration.MicrosoftLogLevel)) ?? fileLoggingConfiguration.MicrosoftLogLevel;
738 return fileLoggingConfiguration;
750 elasticsearchConfiguration.Enable = await
PromptYesNo(
"Enable logging to an external ElasticSearch server? (y/n): ", cancellationToken);
752 if (elasticsearchConfiguration.Enable)
756 await
console.
WriteAsync(
"ElasticSearch server endpoint (Include protocol and port, leave blank for http://127.0.0.1:9200): ",
false, cancellationToken);
758 if (!String.IsNullOrWhiteSpace(elasticsearchConfiguration.Host))
767 await
console.
WriteAsync(
"Enter Elasticsearch username: ",
false, cancellationToken);
769 if (!String.IsNullOrWhiteSpace(elasticsearchConfiguration.Username))
780 if (!String.IsNullOrWhiteSpace(elasticsearchConfiguration.Username))
788 return elasticsearchConfiguration;
800 Enable = await
PromptYesNo(
"Enable the web control panel? (y/n): ", cancellationToken),
801 AllowAnyOrigin = await
PromptYesNo(
"Allow web control panels hosted elsewhere to access the server? (Access-Control-Allow-Origin: *) (y/n): ", cancellationToken),
804 if (!config.AllowAnyOrigin)
806 await
console.
WriteAsync(
"Enter a comma seperated list of CORS allowed origins (optional): ",
false, cancellationToken);
808 if (!String.IsNullOrWhiteSpace(commaSeperatedOrigins))
810 var splits = commaSeperatedOrigins.Split(
',');
811 config.AllowedOrigins =
new List<string>(splits.Select(x => x.Trim()));
823 async Task<SwarmConfiguration>
ConfigureSwarm(CancellationToken cancellationToken)
825 var enable = await
PromptYesNo(
"Enable swarm mode? (y/n): ", cancellationToken);
832 await
console.
WriteAsync(
"Enter this server's identifer: ",
false, cancellationToken);
835 while (String.IsNullOrWhiteSpace(identifer));
837 async Task<Uri> ParseAddress(
string question)
844 if (Uri.TryCreate(addressString, UriKind.Absolute, out address)
845 && address.Scheme != Uri.UriSchemeHttp
846 && address.Scheme != Uri.UriSchemeHttps)
849 while (address ==
null);
854 var address = await ParseAddress(
"Enter this server's HTTP(S) address: ");
858 await
console.
WriteAsync(
"Enter the swarm private key: ",
false, cancellationToken);
861 while (String.IsNullOrWhiteSpace(privateKey));
863 var controller = await
PromptYesNo(
"Is this server the swarm's controller? (y/n): ", cancellationToken);
864 Uri controllerAddress =
null;
866 controllerAddress = await ParseAddress(
"Enter the swarm controller's HTTP(S) address: ");
871 ControllerAddress = controllerAddress,
872 Identifier = identifer,
873 PrivateKey = privateKey,
891 string userConfigFileName,
899 CancellationToken cancellationToken)
901 await
console.
WriteAsync(String.Format(CultureInfo.InvariantCulture,
"Configuration complete! Saving to {0}", userConfigFileName),
true, cancellationToken);
905 var map =
new Dictionary<string, object>()
915 var builder =
new SerializerBuilder()
918 if (userConfigFileName.EndsWith(
".json", StringComparison.OrdinalIgnoreCase))
919 builder.JsonCompatible();
921 var serializer =
new SerializerBuilder()
925 var serializedYaml = serializer.Serialize(map);
928 serializedYaml = serializedYaml.Replace(
929 $
"\n {nameof(ControlPanelConfiguration.Channel)}: ",
931 StringComparison.Ordinal)
932 .Replace(
"\r", String.Empty, StringComparison.Ordinal);
934 var configBytes = Encoding.UTF8.GetBytes(serializedYaml);
944 using (cancellationToken.Register(() =>
reloadTcs.TrySetCanceled()))
947 catch (OperationCanceledException)
955 await
console.
WriteAsync(
"For your convienence, here's the yaml we tried to write out:",
true, cancellationToken);
961 throw new OperationCanceledException();
971 async Task
RunWizard(
string userConfigFileName, CancellationToken cancellationToken)
974 await
console.
WriteAsync(
"Welcome to tgstation-server!",
true, cancellationToken);
975 await
console.
WriteAsync(
"This wizard will help you configure your server.",
true, cancellationToken);
996 databaseConfiguration,
997 newGeneralConfiguration,
998 fileLoggingConfiguration,
999 elasticSearchConfiguration,
1000 controlPanelConfiguration,
1016 var forceRun = setupWizardMode == SetupWizardMode.Force || setupWizardMode ==
SetupWizardMode.Only;
1020 throw new InvalidOperationException(
"Asked to run setup wizard with no console avaliable!");
1024 var userConfigFileName = String.Format(CultureInfo.InvariantCulture,
"appsettings.{0}.yml",
hostingEnvironment.EnvironmentName);
1026 async Task HandleSetupCancel()
1034 Task finalTask = Task.CompletedTask;
1035 using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken,
console.
CancelKeyPress))
1036 using ((cancellationToken = cts.Token).Register(() => finalTask = HandleSetupCancel()))
1042 var legacyJsonFileName = $
"appsettings.{hostingEnvironment.EnvironmentName}.json";
1045 userConfigFileName = legacyJsonFileName;
1048 bool shouldRunBasedOnAutodetect;
1052 var contents = Encoding.UTF8.GetString(bytes);
1053 var lines = contents.Split(
'\n', StringSplitOptions.RemoveEmptyEntries);
1054 var existingConfigIsEmpty = lines
1055 .Select(line => line.Trim())
1056 .All(line => line[0] ==
'#' || line ==
"{}" || line.Length == 0);
1057 shouldRunBasedOnAutodetect = existingConfigIsEmpty;
1060 shouldRunBasedOnAutodetect =
true;
1062 if (!shouldRunBasedOnAutodetect)
1066 await
console.
WriteAsync(String.Format(CultureInfo.InvariantCulture,
"The configuration settings are requesting the setup wizard be run, but you already appear to have a configuration file ({0})!", userConfigFileName),
true, cancellationToken);
1068 forceRun = await
PromptYesNo(
"Continue running setup wizard? (y/n): ", cancellationToken);
1078 await
RunWizard(userConfigFileName, cancellationToken);
Configuration options for the web control panel.
const string Section
The key for the Microsoft.Extensions.Configuration.IConfigurationSection the ControlPanelConfiguratio...
Configuration options for the Database.DatabaseContext.
const string Section
The key for the Microsoft.Extensions.Configuration.IConfigurationSection the DatabaseConfiguration re...
DatabaseType DatabaseType
The Configuration.DatabaseType to create.
Configuration options pertaining to elasticsearch log storage.
const string Section
The key for the Microsoft.Extensions.Configuration.IConfigurationSection the ElasticsearchConfigurati...
File logging configuration options.
const string Section
The key for the Microsoft.Extensions.Configuration.IConfigurationSection the FileLoggingConfiguration...
General configuration options.
const string Section
The key for the Microsoft.Extensions.Configuration.IConfigurationSection the GeneralConfiguration res...
static readonly Version CurrentConfigVersion
The current ConfigVersion.
const ushort DefaultApiPort
The default value of ApiPort.
SetupWizardMode SetupWizardMode
The SetupWizardMode.
Configuration for the server swarm system.
const string Section
The key for the Microsoft.Extensions.Configuration.IConfigurationSection the SwarmConfiguration resid...
JsonConverter and IYamlTypeConverter for serializing global::System.Versions in semver format.
readonly IIOManager ioManager
The IIOManager for the SetupWizard.
Task StopAsync(CancellationToken cancellationToken)
readonly IHostEnvironment hostingEnvironment
The IHostEnvironment for the SetupWizard.
async Task CheckRunWizard(CancellationToken cancellationToken)
Check if it should and run the SetupWizard if necessary.
readonly GeneralConfiguration generalConfiguration
The GeneralConfiguration for the SetupWizard.
async Task< DatabaseType > PromptDatabaseType(bool firstTime, CancellationToken cancellationToken)
Prompt the user for the DatabaseType.
async Task< GeneralConfiguration > ConfigureGeneral(CancellationToken cancellationToken)
Prompts the user to create a GeneralConfiguration.
readonly IAssemblyInformationProvider assemblyInformationProvider
The IAssemblyInformationProvider for the SetupWizard.
async Task< string > ValidateNonExistantSqliteDBName(string databaseName, CancellationToken cancellationToken)
Check that a given SQLite databaseName is can be accessed. Also prompts the user if they want to use...
async Task< ElasticsearchConfiguration > ConfigureElasticsearch(CancellationToken cancellationToken)
Prompts the user to create a ElasticsearchConfiguration.
async Task< FileLoggingConfiguration > ConfigureLogging(CancellationToken cancellationToken)
Prompts the user to create a FileLoggingConfiguration.
async Task< ushort?> PromptForHostingPort(CancellationToken cancellationToken)
Prompts the user to enter the port to host TGS on.
async Task< bool > PromptYesNo(string question, CancellationToken cancellationToken)
A prompt for a yes or no value.
readonly IPlatformIdentifier platformIdentifier
The IPlatformIdentifier for the SetupWizard.
readonly IDatabaseConnectionFactory dbConnectionFactory
The IDatabaseConnectionFactory for the SetupWizard.
readonly IConsole console
The IConsole for the SetupWizard.
TaskCompletionSource reloadTcs
A TaskCompletionSource that will complete when the IConfiguration is reloaded.
async Task TestDatabaseConnection(DbConnection testConnection, DatabaseConfiguration databaseConfiguration, string databaseName, bool dbExists, CancellationToken cancellationToken)
Ensure a given testConnection works.
async Task SaveConfiguration(string userConfigFileName, ushort? hostingPort, DatabaseConfiguration databaseConfiguration, GeneralConfiguration newGeneralConfiguration, FileLoggingConfiguration fileLoggingConfiguration, ElasticsearchConfiguration elasticsearchConfiguration, ControlPanelConfiguration controlPanelConfiguration, SwarmConfiguration swarmConfiguration, CancellationToken cancellationToken)
Saves a given Configuration set to userConfigFileName .
async Task< DatabaseConfiguration > ConfigureDatabase(CancellationToken cancellationToken)
Prompts the user to create a DatabaseConfiguration.
async Task< SwarmConfiguration > ConfigureSwarm(CancellationToken cancellationToken)
Prompts the user to create a SwarmConfiguration.
readonly IHostApplicationLifetime applicationLifetime
The IHostApplicationLifetime for the SetupWizard.
SetupWizard(IIOManager ioManager, IConsole console, IHostEnvironment hostingEnvironment, IAssemblyInformationProvider assemblyInformationProvider, IDatabaseConnectionFactory dbConnectionFactory, IPlatformIdentifier platformIdentifier, IAsyncDelayer asyncDelayer, IHostApplicationLifetime applicationLifetime, IConfiguration configuration, IOptions< GeneralConfiguration > generalConfigurationOptions)
Initializes a new instance of the SetupWizard class.
readonly IAsyncDelayer asyncDelayer
The IAsyncDelayer for the SetupWizard.
async Task StartAsync(CancellationToken cancellationToken)
async Task< ControlPanelConfiguration > ConfigureControlPanel(CancellationToken cancellationToken)
Prompts the user to create a ControlPanelConfiguration.
async Task RunWizard(string userConfigFileName, CancellationToken cancellationToken)
Runs the SetupWizard.
For creating raw DbConnections.
DbConnection CreateConnection(string connectionString, DatabaseType databaseType)
Create a DbConnection.
Abstraction for global::System.Console.
CancellationToken CancelKeyPress
Gets a CancellationToken that triggers if Crtl+C or an equivalent is pressed.
bool Available
If the IConsole is visible to the user.
Task< string > ReadLineAsync(bool usePasswordChar, CancellationToken cancellationToken)
Read a line from the IConsole.
Task PressAnyKeyAsync(CancellationToken cancellationToken)
Wait for a key press on the IConsole.
Task WriteAsync(string text, bool newLine, CancellationToken cancellationToken)
Write some text to the IConsole.
Interface for using filesystems.
string ResolvePath()
Retrieve the full path of the current working directory.
string ConcatPath(params string[] paths)
Combines an array of strings into a path.
string GetDirectoryName(string path)
Gets the directory portion of a given path .
Task CreateDirectory(string path, CancellationToken cancellationToken)
Create a directory at path .
Task DeleteFile(string path, CancellationToken cancellationToken)
Deletes a file at path .
Task< byte[]> ReadAllBytes(string path, CancellationToken cancellationToken)
Returns all the contents of a file at path as a byte array.
Task DeleteDirectory(string path, CancellationToken cancellationToken)
Recursively delete a directory, removes and does not enter any symlinks encounterd.
Task< bool > FileExists(string path, CancellationToken cancellationToken)
Check that the file at path exists.
Task< bool > DirectoryExists(string path, CancellationToken cancellationToken)
Check that the directory at path exists.
Task WriteAllBytes(string path, byte[] contents, CancellationToken cancellationToken)
Writes some contents to a file at path overwriting previous content.
For waiting asynchronously.
Task Delay(TimeSpan timeSpan, CancellationToken cancellationToken)
Create a Task that completes after a given timeSpan .
DatabaseType
Type of database to user.
SetupWizardMode
Determines if the SetupWizard will run.