2020-01-18 22:09:32 +00:00
|
|
|
|
using System;
|
2020-02-01 05:41:09 +00:00
|
|
|
|
using System.Collections;
|
|
|
|
|
using System.Collections.Generic;
|
2020-01-22 03:43:53 +00:00
|
|
|
|
using System.IO;
|
2020-02-13 12:29:59 +00:00
|
|
|
|
using System.IO.Compression;
|
2020-01-22 03:43:53 +00:00
|
|
|
|
using System.Linq;
|
2020-01-30 04:29:20 +00:00
|
|
|
|
using System.Net;
|
2020-01-19 05:51:12 +00:00
|
|
|
|
using System.Net.Http;
|
2020-01-18 22:09:32 +00:00
|
|
|
|
using System.Reactive.Linq;
|
2020-02-13 12:29:59 +00:00
|
|
|
|
using System.Text;
|
2020-01-29 23:41:53 +00:00
|
|
|
|
using System.Threading;
|
2020-01-19 05:51:12 +00:00
|
|
|
|
using System.Threading.Tasks;
|
2020-01-18 22:09:32 +00:00
|
|
|
|
using Wabbajack.Common;
|
2020-02-13 12:29:59 +00:00
|
|
|
|
using Wabbajack.Lib.Downloaders;
|
2020-01-22 03:43:53 +00:00
|
|
|
|
using File = Alphaleonis.Win32.Filesystem.File;
|
|
|
|
|
using Path = Alphaleonis.Win32.Filesystem.Path;
|
2020-01-18 22:09:32 +00:00
|
|
|
|
|
|
|
|
|
namespace Wabbajack.Lib.FileUploader
|
|
|
|
|
{
|
|
|
|
|
public class AuthorAPI
|
|
|
|
|
{
|
2020-04-05 21:15:01 +00:00
|
|
|
|
public static IObservable<bool> HaveAuthorAPIKey => Utils.HaveEncryptedJsonObservable(Consts.AuthorAPIKeyFile);
|
2020-01-18 22:09:32 +00:00
|
|
|
|
|
2020-04-10 01:29:53 +00:00
|
|
|
|
public static string? ApiKeyOverride = null;
|
2020-04-08 04:19:36 +00:00
|
|
|
|
|
2020-04-10 01:29:53 +00:00
|
|
|
|
public static async Task<string> GetAPIKey(string? apiKey = null)
|
2020-01-18 22:09:32 +00:00
|
|
|
|
{
|
2020-04-08 04:19:36 +00:00
|
|
|
|
if (ApiKeyOverride != null) return ApiKeyOverride;
|
2020-04-05 21:15:01 +00:00
|
|
|
|
return apiKey ?? (await Consts.LocalAppDataPath.Combine(Consts.AuthorAPIKeyFile).ReadAllTextAsync()).Trim();
|
2020-01-18 22:09:32 +00:00
|
|
|
|
}
|
2020-01-19 05:51:12 +00:00
|
|
|
|
|
2020-03-30 20:38:46 +00:00
|
|
|
|
public static Uri UploadURL => new Uri($"{Consts.WabbajackBuildServerUri}upload_file");
|
2020-01-29 23:41:53 +00:00
|
|
|
|
public static long BLOCK_SIZE = (long)1024 * 1024 * 2;
|
2020-01-30 04:29:20 +00:00
|
|
|
|
public static int MAX_CONNECTIONS = 8;
|
2020-04-10 01:29:53 +00:00
|
|
|
|
public static Task<string> UploadFile(AbsolutePath filename, Action<double> progressFn, string? apikey = null)
|
2020-01-19 05:51:12 +00:00
|
|
|
|
{
|
|
|
|
|
var tcs = new TaskCompletionSource<string>();
|
2020-01-29 23:41:53 +00:00
|
|
|
|
Task.Run(async () =>
|
2020-01-19 05:51:12 +00:00
|
|
|
|
{
|
2020-03-30 20:38:46 +00:00
|
|
|
|
var client = await GetAuthorizedClient(apikey);
|
2020-02-01 05:41:09 +00:00
|
|
|
|
|
2020-03-25 22:30:43 +00:00
|
|
|
|
var fsize = filename.Size;
|
2020-03-30 20:38:46 +00:00
|
|
|
|
var hashTask = filename.FileHashAsync();
|
2020-02-11 05:04:56 +00:00
|
|
|
|
|
2020-03-30 20:38:46 +00:00
|
|
|
|
Utils.Log($"{UploadURL}/{filename.FileName.ToString()}/start");
|
2020-04-01 12:51:37 +00:00
|
|
|
|
using var response = await client.PutAsync($"{UploadURL}/{filename.FileName.ToString()}/start", new StringContent(""));
|
2020-01-22 03:43:53 +00:00
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
|
|
|
{
|
2020-04-01 12:51:37 +00:00
|
|
|
|
Utils.Log("Error starting upload");
|
|
|
|
|
Utils.Log(await response.Content.ReadAsStringAsync());
|
2020-01-30 04:29:20 +00:00
|
|
|
|
tcs.SetException(new Exception($"Start Error: {response.StatusCode} {response.ReasonPhrase}"));
|
2020-01-22 03:43:53 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
2020-01-19 05:51:12 +00:00
|
|
|
|
|
2020-02-01 05:41:09 +00:00
|
|
|
|
IEnumerable<long> Blocks(long fsize)
|
|
|
|
|
{
|
|
|
|
|
for (long block = 0; block * BLOCK_SIZE < fsize; block ++)
|
|
|
|
|
yield return block;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-22 03:43:53 +00:00
|
|
|
|
var key = await response.Content.ReadAsStringAsync();
|
2020-01-29 23:41:53 +00:00
|
|
|
|
long sent = 0;
|
2020-01-30 04:29:20 +00:00
|
|
|
|
using (var iqueue = new WorkQueue(MAX_CONNECTIONS))
|
2020-01-22 03:43:53 +00:00
|
|
|
|
{
|
2020-02-08 04:35:08 +00:00
|
|
|
|
iqueue.Report("Starting Upload", Percent.One);
|
2020-02-01 05:41:09 +00:00
|
|
|
|
await Blocks(fsize)
|
2020-03-30 20:38:46 +00:00
|
|
|
|
.PMap(iqueue, async blockIdx =>
|
2020-01-29 23:41:53 +00:00
|
|
|
|
{
|
2020-01-30 04:29:20 +00:00
|
|
|
|
if (tcs.Task.IsFaulted) return;
|
2020-03-30 20:38:46 +00:00
|
|
|
|
var blockOffset = blockIdx * BLOCK_SIZE;
|
|
|
|
|
var blockSize = blockOffset + BLOCK_SIZE > fsize
|
|
|
|
|
? fsize - blockOffset
|
2020-01-29 23:41:53 +00:00
|
|
|
|
: BLOCK_SIZE;
|
2020-03-30 20:38:46 +00:00
|
|
|
|
Interlocked.Add(ref sent, blockSize);
|
2020-01-29 23:41:53 +00:00
|
|
|
|
progressFn((double)sent / fsize);
|
2020-01-30 04:29:20 +00:00
|
|
|
|
|
2020-03-30 20:38:46 +00:00
|
|
|
|
var data = new byte[blockSize];
|
|
|
|
|
await using (var fs = filename.OpenRead())
|
|
|
|
|
{
|
|
|
|
|
fs.Position = blockOffset;
|
|
|
|
|
await fs.ReadAsync(data, 0, data.Length);
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-19 05:51:12 +00:00
|
|
|
|
|
2020-03-30 20:38:46 +00:00
|
|
|
|
var offsetResponse = await client.PutAsync(UploadURL + $"/{key}/data/{blockOffset}",
|
2020-03-25 22:30:43 +00:00
|
|
|
|
new ByteArrayContent(data));
|
|
|
|
|
|
2020-03-30 20:38:46 +00:00
|
|
|
|
if (!offsetResponse.IsSuccessStatusCode)
|
2020-03-25 22:30:43 +00:00
|
|
|
|
{
|
2020-03-30 20:38:46 +00:00
|
|
|
|
Utils.Log(await offsetResponse.Content.ReadAsStringAsync());
|
|
|
|
|
tcs.SetException(new Exception($"Put Error: {offsetResponse.StatusCode} {offsetResponse.ReasonPhrase}"));
|
2020-03-25 22:30:43 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-30 20:38:46 +00:00
|
|
|
|
var val = long.Parse(await offsetResponse.Content.ReadAsStringAsync());
|
|
|
|
|
if (val != blockOffset + data.Length)
|
2020-03-25 22:30:43 +00:00
|
|
|
|
{
|
2020-03-30 20:38:46 +00:00
|
|
|
|
tcs.SetResult($"Sync Error {val} vs {blockOffset + data.Length} Offset {blockOffset} Size {data.Length}");
|
|
|
|
|
tcs.SetException(new Exception($"Sync Error {val} vs {blockOffset + data.Length}"));
|
2020-01-29 23:41:53 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
2020-01-19 05:51:12 +00:00
|
|
|
|
}
|
2020-01-22 03:43:53 +00:00
|
|
|
|
|
2020-01-30 04:29:20 +00:00
|
|
|
|
if (!tcs.Task.IsFaulted)
|
|
|
|
|
{
|
|
|
|
|
progressFn(1.0);
|
2020-03-30 20:38:46 +00:00
|
|
|
|
var hash = (await hashTask).ToHex();
|
2020-04-01 12:51:37 +00:00
|
|
|
|
using var finalResponse = await client.PutAsync(UploadURL + $"/{key}/finish/{hash}", new StringContent(""));
|
|
|
|
|
if (finalResponse.IsSuccessStatusCode)
|
|
|
|
|
tcs.SetResult(await finalResponse.Content.ReadAsStringAsync());
|
2020-01-30 04:29:20 +00:00
|
|
|
|
else
|
2020-04-04 17:10:40 +00:00
|
|
|
|
{
|
|
|
|
|
Utils.Log("Finalization Error: ");
|
|
|
|
|
Utils.Log(await finalResponse.Content.ReadAsStringAsync());
|
|
|
|
|
tcs.SetException(new Exception(
|
|
|
|
|
$"Finalization Error: {finalResponse.StatusCode} {finalResponse.ReasonPhrase}"));
|
|
|
|
|
}
|
2020-01-30 04:29:20 +00:00
|
|
|
|
}
|
2020-01-22 03:43:53 +00:00
|
|
|
|
|
2020-01-29 23:41:53 +00:00
|
|
|
|
progressFn(0.0);
|
|
|
|
|
|
2020-01-19 05:51:12 +00:00
|
|
|
|
});
|
|
|
|
|
return tcs.Task;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-10 01:29:53 +00:00
|
|
|
|
public static async Task<Common.Http.Client> GetAuthorizedClient(string? apiKey = null)
|
2020-02-11 05:04:56 +00:00
|
|
|
|
{
|
2020-02-26 04:00:28 +00:00
|
|
|
|
var client = new Common.Http.Client();
|
2020-03-30 20:38:46 +00:00
|
|
|
|
client.Headers.Add(("X-API-KEY", await GetAPIKey(apiKey)));
|
2020-02-11 05:04:56 +00:00
|
|
|
|
return client;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static async Task<string> RunJob(string jobtype)
|
|
|
|
|
{
|
2020-03-25 22:30:43 +00:00
|
|
|
|
var client = await GetAuthorizedClient();
|
2020-04-08 04:19:36 +00:00
|
|
|
|
return await client.GetStringAsync($"{Consts.WabbajackBuildServerUri}jobs/enqueue_job/{jobtype}");
|
2020-02-11 05:04:56 +00:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static async Task<string> UpdateNexusCache()
|
|
|
|
|
{
|
|
|
|
|
return await RunJob("GetNexusUpdatesJob");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static async Task<string> UpdateServerModLists()
|
|
|
|
|
{
|
|
|
|
|
return await RunJob("UpdateModLists");
|
|
|
|
|
}
|
2020-02-13 12:29:59 +00:00
|
|
|
|
|
2020-03-31 22:05:36 +00:00
|
|
|
|
public static async Task<bool> UploadPackagedInis(IEnumerable<Archive> archives)
|
2020-02-13 12:29:59 +00:00
|
|
|
|
{
|
|
|
|
|
archives = archives.ToArray(); // defensive copy
|
|
|
|
|
Utils.Log($"Packaging {archives.Count()} inis");
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
await using var ms = new MemoryStream();
|
|
|
|
|
using (var z = new ZipArchive(ms, ZipArchiveMode.Create, true))
|
|
|
|
|
{
|
2020-02-14 13:30:58 +00:00
|
|
|
|
foreach (var e in archives)
|
2020-02-13 12:29:59 +00:00
|
|
|
|
{
|
2020-02-14 13:30:58 +00:00
|
|
|
|
if (e.State == null) continue;
|
|
|
|
|
var entry = z.CreateEntry(Path.GetFileName(e.Name));
|
2020-02-13 12:29:59 +00:00
|
|
|
|
await using var os = entry.Open();
|
2020-02-14 13:30:58 +00:00
|
|
|
|
await os.WriteAsync(Encoding.UTF8.GetBytes(string.Join("\n", e.State.GetMetaIni())));
|
2020-02-13 12:29:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-31 22:05:36 +00:00
|
|
|
|
var client = new Common.Http.Client();
|
2020-04-03 03:57:59 +00:00
|
|
|
|
var response = await client.PostAsync($"{Consts.WabbajackBuildServerUri}indexed_files/notify", new ByteArrayContent(ms.ToArray()));
|
|
|
|
|
|
|
|
|
|
if (response.IsSuccessStatusCode) return true;
|
|
|
|
|
|
|
|
|
|
Utils.Log("Error sending Inis");
|
|
|
|
|
Utils.Log(await response.Content.ReadAsStringAsync());
|
|
|
|
|
return false;
|
|
|
|
|
|
2020-02-13 12:29:59 +00:00
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Utils.Log(ex.ToString());
|
2020-03-31 22:05:36 +00:00
|
|
|
|
return false;
|
2020-02-13 12:29:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-02-25 23:10:41 +00:00
|
|
|
|
|
|
|
|
|
public static async Task<string> GetServerLog()
|
|
|
|
|
{
|
2020-04-30 21:43:54 +00:00
|
|
|
|
return await (await GetAuthorizedClient()).GetStringAsync($"{Consts.WabbajackBuildServerUri}heartbeat/logs");
|
2020-02-25 23:10:41 +00:00
|
|
|
|
}
|
2020-03-02 23:16:15 +00:00
|
|
|
|
|
|
|
|
|
public static async Task<IEnumerable<string>> GetMyFiles()
|
|
|
|
|
{
|
2020-04-30 21:43:54 +00:00
|
|
|
|
return (await (await GetAuthorizedClient()).GetStringAsync($"{Consts.WabbajackBuildServerUri}uploaded_files/list")).FromJsonString<string[]>();
|
2020-03-02 23:16:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static async Task<string> DeleteFile(string name)
|
|
|
|
|
{
|
2020-03-25 22:30:43 +00:00
|
|
|
|
var result = await (await GetAuthorizedClient())
|
2020-04-30 21:43:54 +00:00
|
|
|
|
.DeleteStringAsync($"{Consts.WabbajackBuildServerUri}uploaded_files/{name}");
|
2020-03-02 23:16:15 +00:00
|
|
|
|
return result;
|
|
|
|
|
}
|
2020-01-18 22:09:32 +00:00
|
|
|
|
}
|
|
|
|
|
}
|