mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Several server-side changes to make the system more durable to API call limits.
This commit is contained in:
parent
29d327d747
commit
88e3db0b2a
@ -1,5 +1,8 @@
|
||||
### Changelog
|
||||
|
||||
#### Version - 2.0.5.1 - 5/16/2020
|
||||
* Close automatically Wabbajack when opening the CLI, resolves the "RocksDB is in use" errors
|
||||
|
||||
#### Version - 2.0.5.0 - 5/14/2020
|
||||
* Make the CDN downloads multi-threaded
|
||||
* Optimize installation of included files
|
||||
|
@ -292,11 +292,18 @@ namespace Wabbajack.Lib
|
||||
|
||||
public async Task HashArchives()
|
||||
{
|
||||
var hashResults = await
|
||||
DownloadFolder.EnumerateFiles()
|
||||
Utils.Log("Looking for files to hash");
|
||||
var toHash = DownloadFolder.EnumerateFiles()
|
||||
.Concat(Game.GameLocation().EnumerateFiles())
|
||||
.Where(e => e.Extension != Consts.HashFileExtension)
|
||||
.PMap(Queue, async e => (await e.FileHashCachedAsync(), e));
|
||||
.ToList();
|
||||
|
||||
Utils.Log($"Found {toHash} files to hash");
|
||||
|
||||
var hashResults = await
|
||||
toHash
|
||||
.PMap(Queue, async e => (await e.FileHashCachedAsync(), e));
|
||||
|
||||
HashedArchives.SetTo(hashResults
|
||||
.OrderByDescending(e => e.Item2.LastModified)
|
||||
.GroupBy(e => e.Item1)
|
||||
|
@ -121,7 +121,8 @@ namespace Wabbajack.Lib.Downloaders
|
||||
$"Authenticating for the Nexus failed. A nexus account is required to automatically download mods."));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
await Metrics.Send("nexus_login", _client.ApiKey!);
|
||||
|
||||
if (!await _client.IsPremium())
|
||||
{
|
||||
|
@ -157,8 +157,11 @@ namespace Wabbajack.Lib.NexusApi
|
||||
{
|
||||
var url = "https://api.nexusmods.com/v1/users/validate.json";
|
||||
using var response = await HttpClient.GetAsync(url);
|
||||
return (int.Parse(response.Headers.GetValues("X-RL-Daily-Remaining").First()),
|
||||
var result = (int.Parse(response.Headers.GetValues("X-RL-Daily-Remaining").First()),
|
||||
int.Parse(response.Headers.GetValues("X-RL-Hourly-Remaining").First()));
|
||||
_dailyRemaining = result.Item1;
|
||||
_hourlyRemaining = result.Item2;
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -177,6 +180,13 @@ namespace Wabbajack.Lib.NexusApi
|
||||
return _dailyRemaining;
|
||||
}
|
||||
}
|
||||
protected set
|
||||
{
|
||||
lock (RemainingLock)
|
||||
{
|
||||
_dailyRemaining = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int _hourlyRemaining;
|
||||
@ -189,10 +199,18 @@ namespace Wabbajack.Lib.NexusApi
|
||||
return _hourlyRemaining;
|
||||
}
|
||||
}
|
||||
protected set
|
||||
{
|
||||
lock (RemainingLock)
|
||||
{
|
||||
_hourlyRemaining = value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void UpdateRemaining(HttpResponseMessage response)
|
||||
protected virtual async Task UpdateRemaining(HttpResponseMessage response)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -221,7 +239,7 @@ namespace Wabbajack.Lib.NexusApi
|
||||
|
||||
#endregion
|
||||
|
||||
private NexusApiClient(string? apiKey = null)
|
||||
protected NexusApiClient(string? apiKey = null)
|
||||
{
|
||||
ApiKey = apiKey;
|
||||
|
||||
@ -250,7 +268,7 @@ namespace Wabbajack.Lib.NexusApi
|
||||
try
|
||||
{
|
||||
using var response = await HttpClient.GetAsync(url);
|
||||
UpdateRemaining(response);
|
||||
await UpdateRemaining(response);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
Utils.Log($"Nexus call failed: {response.RequestMessage.RequestUri}");
|
||||
|
@ -60,7 +60,7 @@ namespace Wabbajack.BuildServer.Test
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanQueryAndFindNexusModfilesSlow()
|
||||
public async Task CanQueryAndFindNexusModfilesFast()
|
||||
{
|
||||
var startTime = DateTime.UtcNow;
|
||||
var sql = Fixture.GetService<SqlService>();
|
||||
@ -68,8 +68,7 @@ namespace Wabbajack.BuildServer.Test
|
||||
await sql.DeleteNexusModFilesUpdatedBeforeDate(Game.SkyrimSpecialEdition, 1137, DateTime.UtcNow);
|
||||
await sql.DeleteNexusModInfosUpdatedBeforeDate(Game.SkyrimSpecialEdition, 1137, DateTime.UtcNow);
|
||||
|
||||
var result = await validator.SlowNexusModStats(new ValidationData(),
|
||||
new NexusDownloader.State {Game = Game.SkyrimSpecialEdition, ModID = 1137, FileID = 121449});
|
||||
var result = await validator.FastNexusModStats(new NexusDownloader.State {Game = Game.SkyrimSpecialEdition, ModID = 1137, FileID = 121449});
|
||||
Assert.Equal(ArchiveStatus.Valid, result);
|
||||
|
||||
var gameId = Game.SkyrimSpecialEdition.MetaData().NexusGameId;
|
||||
@ -119,6 +118,13 @@ namespace Wabbajack.BuildServer.Test
|
||||
Assert.Equal(b, new Box{Value = b}.ToJson().FromJsonString<Box>().Value);
|
||||
Assert.NotEqual(a.Hour, b.Hour);
|
||||
Assert.Equal(b.Hour, new Box{Value = a}.ToJson().FromJsonString<Box>().Value.Hour);
|
||||
|
||||
|
||||
var ts = (long)1589528640;
|
||||
var ds = DateTime.Parse("2020-05-15 07:44:00.000");
|
||||
Assert.Equal(ds, ts.AsUnixTime());
|
||||
Assert.Equal(ts, (long)ds.AsUnixTime());
|
||||
Assert.Equal(ts, (long)ts.AsUnixTime().AsUnixTime());
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -551,6 +551,24 @@ CONSTRAINT [PK_NexusModFilesSlow] PRIMARY KEY CLUSTERED
|
||||
) ON [PRIMARY]
|
||||
GO
|
||||
|
||||
/****** Object: Table [dbo].[NexusKeys] Script Date: 5/15/2020 5:20:02 PM ******/
|
||||
SET ANSI_NULLS ON
|
||||
GO
|
||||
|
||||
SET QUOTED_IDENTIFIER ON
|
||||
GO
|
||||
|
||||
CREATE TABLE [dbo].[NexusKeys](
|
||||
[ApiKey] [nvarchar](162) NOT NULL,
|
||||
[DailyRemain] [int] NOT NULL,
|
||||
[HourlyRemain] [int] NOT NULL,
|
||||
CONSTRAINT [PK_NexusKeys] PRIMARY KEY CLUSTERED
|
||||
(
|
||||
[ApiKey] ASC
|
||||
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
|
||||
) ON [PRIMARY]
|
||||
GO
|
||||
|
||||
/****** Object: StoredProcedure [dbo].[MergeAllFilesInArchive] Script Date: 3/28/2020 4:58:59 PM ******/
|
||||
SET ANSI_NULLS ON
|
||||
GO
|
||||
|
@ -25,6 +25,9 @@ namespace Wabbajack.BuildServer
|
||||
public bool RunFrontEndJobs { get; set; }
|
||||
public bool RunBackEndJobs { get; set; }
|
||||
|
||||
public bool RunNexusPolling { get; set; }
|
||||
public bool RunDownloader { get; set; }
|
||||
|
||||
public string BunnyCDN_StorageZone { get; set; }
|
||||
public string SqlConnection { get; set; }
|
||||
|
||||
|
@ -52,7 +52,7 @@ namespace Wabbajack.BuildServer.Controllers
|
||||
string method = "CACHED";
|
||||
if (result == null)
|
||||
{
|
||||
var api = await NexusApiClient.Get(Request.Headers["apikey"].FirstOrDefault());
|
||||
var api = await GetClient();
|
||||
result = await api.GetModInfo(game, ModId, false);
|
||||
await _sql.AddNexusModInfo(game, ModId, result.updated_time, result);
|
||||
|
||||
@ -69,6 +69,21 @@ namespace Wabbajack.BuildServer.Controllers
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<NexusApiClient> GetClient()
|
||||
{
|
||||
var key = Request.Headers["apikey"].FirstOrDefault();
|
||||
if (key == null)
|
||||
return await NexusApiClient.Get(null);
|
||||
|
||||
if (await _sql.HaveKey(key))
|
||||
return await NexusApiClient.Get(key);
|
||||
|
||||
var client = await NexusApiClient.Get(key);
|
||||
var (daily, hourly) = await client.GetRemainingApiCalls();
|
||||
await _sql.SetNexusAPIKey(key, daily, hourly);
|
||||
return client;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("{GameName}/mods/{ModId}/files.json")]
|
||||
public async Task<NexusApiClient.GetModFilesResponse> GetModFiles(string GameName, long ModId)
|
||||
|
@ -30,11 +30,6 @@ namespace Wabbajack.Server.DataLayer
|
||||
@"DELETE FROM dbo.NexusModFiles WHERE Game = @Game AND ModID = @ModId AND LastChecked < @Date
|
||||
SELECT @@ROWCOUNT AS Deleted",
|
||||
new {Game = game.MetaData().NexusGameId, ModId = modId, Date = date});
|
||||
|
||||
deleted += await conn.ExecuteScalarAsync<long>(
|
||||
@"DELETE FROM dbo.NexusModFilesSlow WHERE GameId = @Game AND ModID = @ModId AND LastChecked < @Date
|
||||
SELECT @@ROWCOUNT AS Deleted",
|
||||
new {Game = game.MetaData().NexusGameId, ModId = modId, Date = date});
|
||||
return deleted;
|
||||
}
|
||||
|
||||
|
51
Wabbajack.Server/DataLayer/NexusKeys.cs
Normal file
51
Wabbajack.Server/DataLayer/NexusKeys.cs
Normal file
@ -0,0 +1,51 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Dapper;
|
||||
|
||||
namespace Wabbajack.Server.DataLayer
|
||||
{
|
||||
public partial class SqlService
|
||||
{
|
||||
public async Task SetNexusAPIKey(string key, long daily, long hourly)
|
||||
{
|
||||
await using var conn = await Open();
|
||||
await using var trans = await conn.BeginTransactionAsync();
|
||||
await conn.ExecuteAsync(@"DELETE FROM NexusKeys WHERE ApiKey = @ApiKey", new {ApiKey = key}, trans);
|
||||
await conn.ExecuteAsync(@"INSERT INTO NexusKeys (ApiKey, DailyRemain, HourlyRemain) VALUES (@ApiKey, @DailyRemain, @HourlyRemain)",
|
||||
new {ApiKey = key, DailyRemain = daily, HourlyRemain = hourly}, trans);
|
||||
await trans.CommitAsync();
|
||||
}
|
||||
|
||||
|
||||
public async Task DeleteNexusAPIKey(string key)
|
||||
{
|
||||
await using var conn = await Open();
|
||||
await conn.ExecuteAsync(@"DELETE FROM NexusKeys WHERE ApiKey = @ApiKey", new {ApiKey = key});
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetNexusApiKeys(int threshold = 1500)
|
||||
{
|
||||
await using var conn = await Open();
|
||||
return (await conn.QueryAsync<string>(@"SELECT ApiKey FROM NexusKeys WHERE DailyRemain >= @Threshold ORDER BY DailyRemain DESC",
|
||||
new {Threshold = threshold})).ToList();
|
||||
}
|
||||
|
||||
public async Task<List<(string Key, int Daily, int Hourly)>> GetNexusApiKeysWithCounts(int threshold = 1500)
|
||||
{
|
||||
await using var conn = await Open();
|
||||
return (await conn.QueryAsync<(string, int, int)>(@"SELECT ApiKey, DailyRemain, HourlyRemain FROM NexusKeys WHERE DailyRemain >= @Threshold ORDER BY DailyRemain DESC",
|
||||
new {Threshold = threshold})).ToList();
|
||||
}
|
||||
|
||||
|
||||
public async Task<bool> HaveKey(string key)
|
||||
{
|
||||
await using var conn = await Open();
|
||||
return (await conn.QueryAsync<string>(@"SELECT ApiKey FROM NexusKeys WHERE ApiKey = @ApiKey",
|
||||
new {ApiKey = key})).Any();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -31,8 +31,8 @@ namespace Wabbajack.Server.Services
|
||||
while (true)
|
||||
{
|
||||
var (daily, hourly) = await _nexusClient.GetRemainingApiCalls();
|
||||
//bool ignoreNexus = hourly < 25;
|
||||
var ignoreNexus = true;
|
||||
bool ignoreNexus = (daily < 100 && hourly < 10);
|
||||
//var ignoreNexus = true;
|
||||
if (ignoreNexus)
|
||||
_logger.LogWarning($"Ignoring Nexus Downloads due to low hourly api limit (Daily: {daily}, Hourly:{hourly})");
|
||||
else
|
||||
@ -89,6 +89,7 @@ namespace Wabbajack.Server.Services
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Log(LogLevel.Warning, $"Error downloading {nextDownload.Archive.State.PrimaryKeyString}");
|
||||
await nextDownload.Fail(_sql, ex.ToString());
|
||||
}
|
||||
|
||||
|
@ -22,22 +22,25 @@ namespace Wabbajack.Server.Services
|
||||
public class ListValidator : AbstractService<ListValidator, int>
|
||||
{
|
||||
private SqlService _sql;
|
||||
private NexusApiClient _nexusClient;
|
||||
private DiscordWebHook _discord;
|
||||
private NexusKeyMaintainance _nexus;
|
||||
|
||||
public IEnumerable<(ModListSummary Summary, DetailedStatus Detailed)> Summaries { get; private set; } =
|
||||
new (ModListSummary Summary, DetailedStatus Detailed)[0];
|
||||
|
||||
|
||||
public ListValidator(ILogger<ListValidator> logger, AppSettings settings, SqlService sql)
|
||||
public ListValidator(ILogger<ListValidator> logger, AppSettings settings, SqlService sql, DiscordWebHook discord, NexusKeyMaintainance nexus)
|
||||
: base(logger, settings, TimeSpan.FromMinutes(10))
|
||||
{
|
||||
_sql = sql;
|
||||
_discord = discord;
|
||||
_nexus = nexus;
|
||||
}
|
||||
|
||||
public override async Task<int> Execute()
|
||||
{
|
||||
var data = await _sql.GetValidationData();
|
||||
|
||||
|
||||
using var queue = new WorkQueue();
|
||||
|
||||
var results = await data.ModLists.PMap(queue, async list =>
|
||||
@ -94,7 +97,7 @@ namespace Wabbajack.Server.Services
|
||||
nexusState.Game.MetaData().NexusGameId, nexusState.ModID, nexusState.FileID)):
|
||||
return (archive, ArchiveStatus.Valid);
|
||||
case NexusDownloader.State ns:
|
||||
return (archive, await SlowNexusModStats(data, ns));
|
||||
return (archive, await FastNexusModStats(ns));
|
||||
case ManualDownloader.State _:
|
||||
return (archive, ArchiveStatus.Valid);
|
||||
default:
|
||||
@ -109,125 +112,88 @@ namespace Wabbajack.Server.Services
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private readonly AsyncLock _slowQueryLock = new AsyncLock();
|
||||
public async Task<ArchiveStatus> SlowNexusModStats(ValidationData data, NexusDownloader.State ns)
|
||||
private AsyncLock _lock = new AsyncLock();
|
||||
|
||||
public async Task<ArchiveStatus> FastNexusModStats(NexusDownloader.State ns)
|
||||
{
|
||||
var gameId = ns.Game.MetaData().NexusGameId;
|
||||
//using var _ = await _slowQueryLock.WaitAsync();
|
||||
_logger.Log(LogLevel.Warning, $"Slow querying for {ns.Game} {ns.ModID} {ns.FileID}");
|
||||
|
||||
|
||||
if (data.NexusFiles.Contains((gameId, ns.ModID, ns.FileID)))
|
||||
return ArchiveStatus.Valid;
|
||||
|
||||
if (data.NexusFiles.Contains((gameId, ns.ModID, -1)))
|
||||
return ArchiveStatus.InValid;
|
||||
|
||||
if (data.SlowQueriedFor.Contains((ns.Game, ns.ModID)))
|
||||
return ArchiveStatus.InValid;
|
||||
|
||||
var queryTime = DateTime.UtcNow;
|
||||
var regex_id = new Regex("(?<=[?;&]id\\=)\\d+");
|
||||
var regex_file_id = new Regex("(?<=[?;&]file_id\\=)\\d+");
|
||||
var client = new Common.Http.Client();
|
||||
var result =
|
||||
await client.GetHtmlAsync(
|
||||
$"https://www.nexusmods.com/{ns.Game.MetaData().NexusName}/mods/{ns.ModID}/?tab=files");
|
||||
|
||||
var fileIds = result.DocumentNode.Descendants()
|
||||
.Select(f => f.GetAttributeValue("href", ""))
|
||||
.Select(f =>
|
||||
{
|
||||
var match = regex_id.Match(f);
|
||||
if (match.Success)
|
||||
return match.Value;
|
||||
match = regex_file_id.Match(f);
|
||||
if (match.Success)
|
||||
return match.Value;
|
||||
return null;
|
||||
})
|
||||
.Where(m => m != null)
|
||||
.Select(m => long.Parse(m))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
_logger.Log(LogLevel.Warning, $"Slow queried {fileIds.Count} files");
|
||||
foreach (var id in fileIds)
|
||||
{
|
||||
await _sql.AddNexusModFileSlow(ns.Game, ns.ModID, id, queryTime);
|
||||
data.NexusFiles.Add((gameId, ns.ModID, id));
|
||||
}
|
||||
|
||||
// Add in the default marker
|
||||
await _sql.AddNexusModFileSlow(ns.Game, ns.ModID, -1, queryTime);
|
||||
data.NexusFiles.Add((gameId, ns.ModID, -1));
|
||||
|
||||
return fileIds.Contains(ns.FileID) ? ArchiveStatus.Valid : ArchiveStatus.InValid;
|
||||
}
|
||||
|
||||
private async Task<ArchiveStatus> FastNexusModStats(NexusDownloader.State ns)
|
||||
{
|
||||
|
||||
// Check if some other thread has added them
|
||||
var mod = await _sql.GetNexusModInfoString(ns.Game, ns.ModID);
|
||||
var files = await _sql.GetModFiles(ns.Game, ns.ModID);
|
||||
|
||||
try
|
||||
if (mod == null || files == null)
|
||||
{
|
||||
if (mod == null)
|
||||
// Aquire the lock
|
||||
using var lck = await _lock.WaitAsync();
|
||||
|
||||
// Check again
|
||||
mod = await _sql.GetNexusModInfoString(ns.Game, ns.ModID);
|
||||
files = await _sql.GetModFiles(ns.Game, ns.ModID);
|
||||
|
||||
if (mod == null || files == null)
|
||||
{
|
||||
_nexusClient ??= await NexusApiClient.Get();
|
||||
_logger.Log(LogLevel.Information, $"Found missing Nexus mod info {ns.Game} {ns.ModID}");
|
||||
|
||||
NexusApiClient nexusClient = await _nexus.GetClient();
|
||||
var queryTime = DateTime.UtcNow;
|
||||
|
||||
try
|
||||
{
|
||||
mod = await _nexusClient.GetModInfo(ns.Game, ns.ModID, false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
mod = new ModInfo
|
||||
if (mod == null)
|
||||
{
|
||||
mod_id = ns.ModID.ToString(), game_id = ns.Game.MetaData().NexusGameId, available = false
|
||||
};
|
||||
}
|
||||
_logger.Log(LogLevel.Information, $"Found missing Nexus mod info {ns.Game} {ns.ModID}");
|
||||
try
|
||||
{
|
||||
mod = await nexusClient.GetModInfo(ns.Game, ns.ModID, false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
mod = new ModInfo
|
||||
{
|
||||
mod_id = ns.ModID.ToString(),
|
||||
game_id = ns.Game.MetaData().NexusGameId,
|
||||
available = false
|
||||
};
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _sql.AddNexusModInfo(ns.Game, ns.ModID, mod.updated_time, mod);
|
||||
}
|
||||
catch (Exception _)
|
||||
{
|
||||
// Could be a PK constraint failure
|
||||
}
|
||||
try
|
||||
{
|
||||
await _sql.AddNexusModInfo(ns.Game, ns.ModID, queryTime, mod);
|
||||
}
|
||||
catch (Exception _)
|
||||
{
|
||||
// Could be a PK constraint failure
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (files == null)
|
||||
{
|
||||
_logger.Log(LogLevel.Information, $"Found missing Nexus mod info {ns.Game} {ns.ModID}");
|
||||
try
|
||||
{
|
||||
files = await _nexusClient.GetModFiles(ns.Game, ns.ModID, false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
files = new NexusApiClient.GetModFilesResponse {files = new List<NexusFileInfo>()};
|
||||
}
|
||||
if (files == null)
|
||||
{
|
||||
_logger.Log(LogLevel.Information, $"Found missing Nexus mod info {ns.Game} {ns.ModID}");
|
||||
try
|
||||
{
|
||||
files = await nexusClient.GetModFiles(ns.Game, ns.ModID, false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
files = new NexusApiClient.GetModFilesResponse {files = new List<NexusFileInfo>()};
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await _sql.AddNexusModFiles(ns.Game, ns.ModID, mod.updated_time, files);
|
||||
try
|
||||
{
|
||||
await _sql.AddNexusModFiles(ns.Game, ns.ModID, queryTime, files);
|
||||
}
|
||||
catch (Exception _)
|
||||
{
|
||||
// Could be a PK constraint failure
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception _)
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Could be a PK constraint failure
|
||||
return ArchiveStatus.InValid;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ArchiveStatus.InValid;
|
||||
}
|
||||
|
||||
if (mod.available && files.files.Any(f => !string.IsNullOrEmpty(f.category_name) && f.file_id == ns.FileID))
|
||||
return ArchiveStatus.Valid;
|
||||
|
@ -11,6 +11,7 @@ using Wabbajack.Lib;
|
||||
using Wabbajack.Lib.Downloaders;
|
||||
using Wabbajack.Lib.ModListRegistry;
|
||||
using Wabbajack.Server.DataLayer;
|
||||
using Wabbajack.Server.DTOs;
|
||||
|
||||
namespace Wabbajack.Server.Services
|
||||
{
|
||||
@ -20,13 +21,15 @@ namespace Wabbajack.Server.Services
|
||||
private AppSettings _settings;
|
||||
private ArchiveMaintainer _maintainer;
|
||||
private SqlService _sql;
|
||||
private DiscordWebHook _discord;
|
||||
|
||||
public ModListDownloader(ILogger<ModListDownloader> logger, AppSettings settings, ArchiveMaintainer maintainer, SqlService sql)
|
||||
public ModListDownloader(ILogger<ModListDownloader> logger, AppSettings settings, ArchiveMaintainer maintainer, SqlService sql, DiscordWebHook discord)
|
||||
{
|
||||
_logger = logger;
|
||||
_settings = settings;
|
||||
_maintainer = maintainer;
|
||||
_sql = sql;
|
||||
_discord = discord;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
@ -69,6 +72,8 @@ namespace Wabbajack.Server.Services
|
||||
if (!_maintainer.HaveArchive(list.DownloadMetadata!.Hash))
|
||||
{
|
||||
_logger.Log(LogLevel.Information, $"Downloading {list.Links.MachineURL}");
|
||||
await _discord.Send(Channel.Ham,
|
||||
new DiscordMessage {Content = $"Downloading {list.Links.MachineURL} - {list.DownloadMetadata.Hash}"});
|
||||
var tf = new TempFile();
|
||||
var state = DownloadDispatcher.ResolveArchive(list.Links.Download);
|
||||
if (state == null)
|
||||
@ -100,7 +105,9 @@ namespace Wabbajack.Server.Services
|
||||
{
|
||||
if (entry == null)
|
||||
{
|
||||
Utils.Log($"Bad Modlist {list.Links.MachineURL}");
|
||||
_logger.LogWarning($"Bad Modlist {list.Links.MachineURL}");
|
||||
await _discord.Send(Channel.Ham,
|
||||
new DiscordMessage {Content = $"Bad Modlist {list.Links.MachineURL} - {list.DownloadMetadata.Hash}"});
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -110,7 +117,9 @@ namespace Wabbajack.Server.Services
|
||||
}
|
||||
catch (JsonReaderException ex)
|
||||
{
|
||||
Utils.Log($"Bad JSON format for {list.Links.MachineURL}");
|
||||
_logger.LogWarning($"Bad Modlist {list.Links.MachineURL}");
|
||||
await _discord.Send(Channel.Ham,
|
||||
new DiscordMessage {Content = $"Bad Modlist {list.Links.MachineURL} - {list.DownloadMetadata.Hash}"});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -120,12 +129,20 @@ namespace Wabbajack.Server.Services
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, $"Error downloading modlist {list.Links.MachineURL}");
|
||||
await _discord.Send(Channel.Ham,
|
||||
new DiscordMessage {Content = $"Error downloading modlist {list.Links.MachineURL} - {list.DownloadMetadata.Hash}"});
|
||||
}
|
||||
}
|
||||
_logger.Log(LogLevel.Information, $"Done checking modlists. Downloaded {downloaded} new lists");
|
||||
if (downloaded > 0)
|
||||
await _discord.Send(Channel.Ham,
|
||||
new DiscordMessage {Content = $"Downloaded {downloaded} new lists"});
|
||||
|
||||
var fc = await _sql.EnqueueModListFilesForIndexing();
|
||||
_logger.Log(LogLevel.Information, $"Enqueing {fc} files for downloading");
|
||||
if (fc > 0)
|
||||
await _discord.Send(Channel.Ham,
|
||||
new DiscordMessage {Content = $"Enqueing {fc} files for downloading"});
|
||||
|
||||
return downloaded;
|
||||
}
|
||||
|
87
Wabbajack.Server/Services/NexusKeyMaintainance.cs
Normal file
87
Wabbajack.Server/Services/NexusKeyMaintainance.cs
Normal file
@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Wabbajack.BuildServer;
|
||||
using Wabbajack.Lib.NexusApi;
|
||||
using Wabbajack.Server.DataLayer;
|
||||
|
||||
namespace Wabbajack.Server.Services
|
||||
{
|
||||
public class NexusKeyMaintainance : AbstractService<NexusKeyMaintainance, int>
|
||||
{
|
||||
private SqlService _sql;
|
||||
|
||||
public NexusKeyMaintainance(ILogger<NexusKeyMaintainance> logger, AppSettings settings, SqlService sql) : base(logger, settings, TimeSpan.FromHours(1))
|
||||
{
|
||||
_sql = sql;
|
||||
}
|
||||
|
||||
public async Task<NexusApiClient> GetClient()
|
||||
{
|
||||
var keys = await _sql.GetNexusApiKeysWithCounts(1500);
|
||||
foreach (var key in keys)
|
||||
{
|
||||
return new TrackingClient(_sql, key);
|
||||
}
|
||||
|
||||
return await NexusApiClient.Get();
|
||||
}
|
||||
|
||||
public override async Task<int> Execute()
|
||||
{
|
||||
var keys = await _sql.GetNexusApiKeysWithCounts(0);
|
||||
_logger.Log(LogLevel.Information, $"Verifying {keys.Count} API Keys");
|
||||
foreach (var key in keys)
|
||||
{
|
||||
try
|
||||
{
|
||||
var client = new TrackingClient(_sql, key);
|
||||
|
||||
var status = await client.GetUserStatus();
|
||||
if (!status.is_premium)
|
||||
{
|
||||
await _sql.DeleteNexusAPIKey(key.Key);
|
||||
continue;
|
||||
}
|
||||
|
||||
var (daily, hourly) = await client.GetRemainingApiCalls();
|
||||
await _sql.SetNexusAPIKey(key.Key, daily, hourly);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Log(LogLevel.Warning, "Update error, purging API key");
|
||||
await _sql.DeleteNexusAPIKey(key.Key);
|
||||
}
|
||||
}
|
||||
|
||||
return keys.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public class TrackingClient : NexusApiClient
|
||||
{
|
||||
private SqlService _sql;
|
||||
public TrackingClient(SqlService sql, (string Key, int Daily, int Hourly) key) : base(key.Key)
|
||||
{
|
||||
_sql = sql;
|
||||
DailyRemaining = key.Daily;
|
||||
HourlyRemaining = key.Hourly;
|
||||
}
|
||||
|
||||
protected virtual async Task UpdateRemaining(HttpResponseMessage response)
|
||||
{
|
||||
await base.UpdateRemaining(response);
|
||||
try
|
||||
{
|
||||
var dailyRemaining = int.Parse(response.Headers.GetValues("x-rl-daily-remaining").First());
|
||||
var hourlyRemaining = int.Parse(response.Headers.GetValues("x-rl-hourly-remaining").First());
|
||||
await _sql.SetNexusAPIKey(ApiKey, dailyRemaining, hourlyRemaining);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -98,14 +98,15 @@ namespace Wabbajack.Server.Services
|
||||
// Mod activity could hide files
|
||||
var b = d.mod.LastestModActivity.AsUnixTime();
|
||||
|
||||
return new {Game = d.game.Game, Date = (a > b ? a : b), ModId = d.mod.ModId};
|
||||
return new {Game = d.game.Game, Date = (a > b) ? a : b, ModId = d.mod.ModId};
|
||||
});
|
||||
|
||||
var purged = await collected.PMap(queue, async t =>
|
||||
{
|
||||
var resultA = await _sql.DeleteNexusModInfosUpdatedBeforeDate(t.Game, t.ModId, t.Date);
|
||||
var resultB = await _sql.DeleteNexusModFilesUpdatedBeforeDate(t.Game, t.ModId, t.Date);
|
||||
return resultA + resultB;
|
||||
long purgeCount = 0;
|
||||
purgeCount += await _sql.DeleteNexusModInfosUpdatedBeforeDate(t.Game, t.ModId, t.Date);
|
||||
purgeCount += await _sql.DeleteNexusModFilesUpdatedBeforeDate(t.Game, t.ModId, t.Date);
|
||||
return purgeCount;
|
||||
});
|
||||
|
||||
_logger.Log(LogLevel.Information, $"Purged {purged.Sum()} cache entries");
|
||||
|
@ -64,6 +64,7 @@ namespace Wabbajack.Server
|
||||
services.AddSingleton<ListValidator>();
|
||||
services.AddSingleton<ArchiveDownloader>();
|
||||
services.AddSingleton<DiscordWebHook>();
|
||||
services.AddSingleton<NexusKeyMaintainance>();
|
||||
|
||||
services.AddMvc();
|
||||
services.AddControllers()
|
||||
@ -114,6 +115,7 @@ namespace Wabbajack.Server
|
||||
app.UseService<ListValidator>();
|
||||
app.UseService<ArchiveDownloader>();
|
||||
app.UseService<DiscordWebHook>();
|
||||
app.UseService<NexusKeyMaintainance>();
|
||||
|
||||
app.Use(next =>
|
||||
{
|
||||
|
@ -155,6 +155,7 @@ namespace Wabbajack
|
||||
WorkingDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)
|
||||
};
|
||||
Process.Start(process);
|
||||
ShutdownApplication();
|
||||
}
|
||||
|
||||
private static bool IsStartingFromModlist(out AbsolutePath modlistPath)
|
||||
|
Loading…
Reference in New Issue
Block a user