You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Ombi/src/Ombi/Program.cs

386 lines
16 KiB

using System;
using System.Linq;
using System.Text;
using Microsoft.AspNetCore.Hosting;
using Ombi.Store.Context;
using Ombi.Store.Entities;
using CommandLine;
using CommandLine.Text;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Ombi.Extensions;
using Ombi.Helpers;
using System.Threading.Tasks;
using System.Collections.Generic;
using Newtonsoft.Json;
using Ombi.Settings.Settings.Models;
using System.IO;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging;
using Ombi.Api.TheMovieDb.Models;
using System.Net.Http;
using System.Text.RegularExpressions;
namespace Ombi
{
public static class Program
{
private static string UrlArgs { get; set; }
public static async Task Main(string[] args)
{
Console.Title = "Ombi";
var host = string.Empty;
var storagePath = string.Empty;
var baseUrl = string.Empty;
var demo = false;
var migrate = false;
var result = Parser.Default.ParseArguments<Options>(args)
.WithParsed(o =>
{
host = o.Host;
storagePath = o.StoragePath;
baseUrl = o.BaseUrl;
demo = o.Demo;
migrate = o.Migrate;
}).WithNotParsed(err =>
{
foreach (var e in err)
{
Console.WriteLine(e);
}
});
Console.WriteLine(HelpOutput(result));
if (baseUrl.HasValue())
{
Console.WriteLine($"Base Url: {baseUrl}");
}
UrlArgs = host;
var urlValue = string.Empty;
var instance = StartupSingleton.Instance;
var demoInstance = DemoSingleton.Instance;
demoInstance.Demo = demo;
instance.StoragePath = storagePath ?? string.Empty;
var services = new ServiceCollection();
services.ConfigureDatabases(null);
#pragma warning disable ASP0000 // Do not call 'IServiceCollection.BuildServiceProvider' in 'ConfigureServices'
using var provider = services.BuildServiceProvider();
#pragma warning restore ASP0000 // Do not call 'IServiceCollection.BuildServiceProvider' in 'ConfigureServices'
var settingsDb = provider.GetRequiredService<SettingsContext>();
var ombiDb = provider.GetRequiredService<OmbiContext>();
if (migrate)
{
Console.WriteLine("Migrate in progress...");
var migrationTasks = new List<Task>();
var externalDb = provider.GetRequiredService<ExternalContext>();
migrationTasks.Add(settingsDb.Database.MigrateAsync());
migrationTasks.Add(ombiDb.Database.MigrateAsync());
migrationTasks.Add(externalDb.Database.MigrateAsync());
Task.WaitAll(migrationTasks.ToArray());
Console.WriteLine("Migrate complete.");
Environment.Exit(0);
}
var config = await settingsDb.ApplicationConfigurations.ToListAsync();
var url = config.FirstOrDefault(x => x.Type == ConfigurationTypes.Url);
var ombiSettingsContent = await settingsDb.Settings.FirstOrDefaultAsync(x => x.SettingsName == "OmbiSettings");
var securityToken = config.FirstOrDefault(x => x.Type == ConfigurationTypes.SecurityToken);
await CheckSecurityToken(securityToken, settingsDb, instance);
if (url == null)
{
url = new ApplicationConfiguration
{
Type = ConfigurationTypes.Url,
Value = "http://*:5000"
};
var strat = settingsDb.Database.CreateExecutionStrategy();
await strat.ExecuteAsync(async () =>
{
using var tran = await settingsDb.Database.BeginTransactionAsync();
settingsDb.ApplicationConfigurations.Add(url);
await settingsDb.SaveChangesAsync();
await tran.CommitAsync();
});
urlValue = url.Value;
}
if (!url.Value.Equals(host))
{
url.Value = UrlArgs;
var strat = settingsDb.Database.CreateExecutionStrategy();
await strat.ExecuteAsync(async () =>
{
using var tran = await settingsDb.Database.BeginTransactionAsync();
await settingsDb.SaveChangesAsync();
await tran.CommitAsync();
});
urlValue = url.Value;
}
else if (string.IsNullOrEmpty(urlValue))
{
urlValue = host;
}
await SortOutBaseUrl(baseUrl, settingsDb, ombiSettingsContent);
var httpClient = new HttpClient();
var api = new Ombi.Api.Api(new Logger<Api.Api>(NullLoggerFactory.Instance), httpClient);
await MigrateOldTvDbIds(ombiDb, ombiSettingsContent, settingsDb, new Ombi.Api.TheMovieDb.TheMovieDbApi(null, (Api.IApi)api, null));
Console.WriteLine($"We are running on {urlValue}");
CreateHostBuilder(args).Build().Run();
}
private static async Task CheckSecurityToken(ApplicationConfiguration securityToken, SettingsContext ctx, StartupSingleton instance)
{
if (securityToken == null || string.IsNullOrEmpty(securityToken.Value))
{
securityToken = new ApplicationConfiguration
{
Type = ConfigurationTypes.SecurityToken,
Value = Guid.NewGuid().ToString("N")
};
var strat = ctx.Database.CreateExecutionStrategy();
await strat.ExecuteAsync(async () =>
{
using var tran = await ctx.Database.BeginTransactionAsync();
ctx.ApplicationConfigurations.Add(securityToken);
await ctx.SaveChangesAsync();
await tran.CommitAsync();
});
}
instance.SecurityKey = securityToken.Value;
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(_ =>
{
// Set properties and call methods on options
});
webBuilder.PreferHostingUrls(true)
.UseUrls(UrlArgs)
.UseStartup<Startup>();
});
private static string HelpOutput(ParserResult<Options> args)
{
var result = new StringBuilder();
result.AppendLine("Hello, welcome to Ombi");
result.AppendLine("Valid options are:");
result.AppendLine(HelpText.AutoBuild(args, null, null));
return result.ToString();
}
private static async Task SortOutBaseUrl(string baseUrl, SettingsContext settingsDb, GlobalSettings ombiSettingsContent)
{
var setBaseUrl = false;
if (ombiSettingsContent == null)
{
Console.WriteLine("Creating new Settings entity");
ombiSettingsContent = new GlobalSettings
{
SettingsName = "OmbiSettings",
Content = JsonConvert.SerializeObject(new OmbiSettings())
};
var strat = settingsDb.Database.CreateExecutionStrategy();
await strat.ExecuteAsync(async () =>
{
using var tran = await settingsDb.Database.BeginTransactionAsync();
settingsDb.Add(ombiSettingsContent);
await settingsDb.SaveChangesAsync();
await tran.CommitAsync();
});
}
var ombiSettings = JsonConvert.DeserializeObject<OmbiSettings>(ombiSettingsContent.Content);
if (ombiSettings == null)
{
if (baseUrl.HasValue() && baseUrl.StartsWith("/"))
{
setBaseUrl = true;
ombiSettings = new OmbiSettings
{
BaseUrl = baseUrl
};
ombiSettingsContent.Content = JsonConvert.SerializeObject(ombiSettings);
var strat = settingsDb.Database.CreateExecutionStrategy();
await strat.ExecuteAsync(async () =>
{
using (var tran = await settingsDb.Database.BeginTransactionAsync())
{
settingsDb.Update(ombiSettingsContent);
await settingsDb.SaveChangesAsync();
await tran.CommitAsync();
}
});
}
}
else if (baseUrl.HasValue() && !baseUrl.Equals(ombiSettings.BaseUrl))
{
setBaseUrl = true;
ombiSettings.BaseUrl = baseUrl;
ombiSettingsContent.Content = JsonConvert.SerializeObject(ombiSettings);
var strat = settingsDb.Database.CreateExecutionStrategy();
await strat.ExecuteAsync(async () =>
{
using (var tran = await settingsDb.Database.BeginTransactionAsync())
{
settingsDb.Update(ombiSettingsContent);
await settingsDb.SaveChangesAsync();
await tran.CommitAsync();
}
});
}
else
{
// The base url might have changed in the settings, so just rewrite
setBaseUrl = true;
baseUrl = ombiSettings.BaseUrl.HasValue() ? ombiSettings.BaseUrl : string.Empty;
}
if (setBaseUrl)
{
var trimmedBaseUrl = baseUrl.EndsWith('/') ? baseUrl.TrimEnd('/') : baseUrl;
var process = AppContext.BaseDirectory;
var ombiInstalledDir = Path.GetDirectoryName(process);
var indexPath = Path.Combine(ombiInstalledDir, "ClientApp", "dist", "index.html");
if (!File.Exists(indexPath))
{
var error = $"Can't set the base URL because we cannot find the file at '{indexPath}', if you are trying to set a base url please report this on Github!";
Console.WriteLine(error);
return;
}
var indexHtml = await File.ReadAllTextAsync(indexPath);
var sb = new StringBuilder(indexHtml);
const string scriptRegex = "<script.type=.+>window.+=.+;<.script>";
const string baseUrlRegex = "<base.href=\".+\">";
indexHtml = Regex.Replace(indexHtml, scriptRegex, $"<script type='text/javascript'>window[\"baseHref\"] = '{trimmedBaseUrl}';</script>");
indexHtml = Regex.Replace(indexHtml, baseUrlRegex, $"<base href=\"{trimmedBaseUrl}/\">");
try
{
await File.WriteAllTextAsync(indexPath, indexHtml);
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("Error attempting to write Base URL, see here: https://docs.ombi.app/info/known-faults/#unauthorized-access-to-indexhtml");
}
Console.WriteLine($"Wrote new baseurl at {indexPath}");
}
}
private static async Task MigrateOldTvDbIds(OmbiContext ctx, GlobalSettings ombiSettingsContent, SettingsContext settingsContext, Api.TheMovieDb.TheMovieDbApi api)
{
if (ombiSettingsContent == null)
{
return;
}
var ombiSettings = JsonConvert.DeserializeObject<OmbiSettings>(ombiSettingsContent.Content);
if (ombiSettings.HasMigratedOldTvDbData)
{
return;
}
var tvRequests = ctx.TvRequests;
Console.WriteLine($"Total Requests to migrate {await tvRequests.CountAsync()}");
foreach (var request in tvRequests)
{
var findResuilt = await api.Find(request.TvDbId.ToString(), ExternalSource.tvdb_id);
var found = findResuilt.tv_results.FirstOrDefault();
if (found == null)
{
var seriesFound = findResuilt.tv_season_results.FirstOrDefault();
if (seriesFound == null)
{
Console.WriteLine($"Cannot find TheMovieDb Record for request {request.Title}");
continue;
}
request.ExternalProviderId = seriesFound.show_id;
Console.WriteLine($"Cannot find TheMovieDb Record for request, found potential match Title: {request.Title}, Match: {seriesFound.show_id}");
}
else
{
request.ExternalProviderId = found.id;
}
}
Console.WriteLine($"Finished Migration");
var strat = ctx.Database.CreateExecutionStrategy();
await strat.ExecuteAsync(async () =>
{
using (var tran = await ctx.Database.BeginTransactionAsync())
{
ctx.TvRequests.UpdateRange(tvRequests);
await ctx.SaveChangesAsync();
await tran.CommitAsync();
}
});
var settingsStrat = settingsContext.Database.CreateExecutionStrategy();
await settingsStrat.ExecuteAsync(async () =>
{
using (var tran = await settingsContext.Database.BeginTransactionAsync())
{
ombiSettings.HasMigratedOldTvDbData = true;
ombiSettingsContent.Content = JsonConvert.SerializeObject(ombiSettings);
settingsContext.Update(ombiSettingsContent);
await settingsContext.SaveChangesAsync();
await tran.CommitAsync();
}
});
return;
}
}
public class Options
{
[Option("host", Required = false, HelpText =
"Set to a semicolon-separated (;) list of URL prefixes to which the server should respond. For example, http://localhost:123." +
" Use \"localhost\" to indicate that the server should listen for requests on any IP address or hostname using the specified port and protocol (for example, http://localhost:5000). " +
"The protocol (http:// or https://) must be included with each URL. Supported formats vary between servers.", Default = "http://*:5000")]
public string Host { get; set; }
[Option("storage", Required = false, HelpText = "Storage path, where we save the logs and database")]
public string StoragePath { get; set; }
[Option("baseurl", Required = false, HelpText = "The base URL for reverse proxy scenarios")]
public string BaseUrl { get; set; }
[Option("demo", Required = false, HelpText = "Demo mode, you will never need to use this, fuck that fruit company...")]
public bool Demo { get; set; }
[Option("migrate", Required = false, HelpText = "Will run the migrations then exit the application")]
public bool Migrate { get; set; }
}
}