using System; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Security.Policy; using System.Threading.Tasks; using MongoDB.Driver; using MongoDB.Driver.Linq; using Nancy; using Nettle; using Wabbajack.CacheServer.DTOs.JobQueue; using Wabbajack.CacheServer.Jobs; using Wabbajack.Common; using Wabbajack.Lib; using Wabbajack.Lib.CompilationSteps; using Wabbajack.Lib.Downloaders; using Directory = Alphaleonis.Win32.Filesystem.Directory; using Path = Alphaleonis.Win32.Filesystem.Path; namespace Wabbajack.CacheServer { public class JobQueueEndpoints : NancyModule { public JobQueueEndpoints() : base ("/jobs") { Get("/", HandleListJobs); Get("/enqueue_curated_for_indexing", HandleEnqueueAllCurated); Get("/enqueue_game_files_for_indexing", HandleEnqueueAllGameFiles); } private readonly Func HandleListJobsTemplate = NettleEngine.GetCompiler().Compile(@"

Jobs - {{$.jobs.Count}} Pending

{{$.time}}

    {{each $.jobs}}
  1. {{$.Description}}
  2. {{/each}}
"); private async Task HandleListJobs(object arg) { var jobs = await Server.Config.JobQueue.Connect() .AsQueryable() .Where(j => j.Ended == null) .OrderByDescending(j => j.Priority) .ThenBy(j => j.Created) .ToListAsync(); var response = (Response)HandleListJobsTemplate(new {jobs, time = DateTime.Now}); response.ContentType = "text/html"; return response; } private async Task HandleEnqueueAllCurated(object arg) { var states = await Server.Config.ListValidation.Connect() .AsQueryable() .SelectMany(lst => lst.DetailedStatus.Archives) .Select(a => a.Archive) .ToListAsync(); var jobs = states.Select(state => new IndexJob {Archive = state}) .Select(j => new Job {Payload = j, RequiresNexus = j.UsesNexus}) .ToList(); if (jobs.Count > 0) await Server.Config.JobQueue.Connect().InsertManyAsync(jobs); return $"Enqueued {states.Count} jobs"; } private async Task HandleEnqueueAllGameFiles(object arg) { using (var queue = new WorkQueue(4)) { var states = GameRegistry.Games.Values .Where(game => game.GameLocation() != null && game.MainExecutable != null) .SelectMany(game => Directory.EnumerateFiles(game.GameLocation(), "*", SearchOption.AllDirectories) .Select(file => new GameFileSourceDownloader.State { Game = game.Game, GameVersion = game.InstalledVersion, GameFile = file.RelativeTo(game.GameLocation()), })) .ToList(); await states.PMap(queue, state => { state.Hash = Path.Combine(state.Game.MetaData().GameLocation(), state.GameFile).FileHash(); }); var jobs = states.Select(state => new IndexJob {Archive = new Archive {Name = Path.GetFileName(state.GameFile), State = state}}) .Select(j => new Job {Payload = j, RequiresNexus = j.UsesNexus}) .ToList(); if (jobs.Count > 0) await Server.Config.JobQueue.Connect().InsertManyAsync(jobs); return $"Enqueued {states.Count} Jobs"; } } public static async Task StartJobQueue() { foreach (var task in Enumerable.Range(0, 4)) { var tsk = StartJobQueueInner(); } } private static async Task StartJobQueueInner() { while (true) { try { var job = await Job.GetNext(); if (job == null) { await Task.Delay(5000); continue; } var result = await job.Payload.Execute(); await Job.Finish(job, result); } catch (Exception ex) { } } } } }