impl/r2 #10

Merged
waffle.lord merged 3 commits from impl/r2 into main 2024-05-04 10:49:55 -04:00
17 changed files with 219 additions and 270 deletions
Showing only changes of commit 67203037a1 - Show all commits

View File

@ -5,8 +5,8 @@
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AssemblyVersion>1.5.10</AssemblyVersion>
<FileVersion>1.5.10</FileVersion>
<AssemblyVersion>1.6.0</AssemblyVersion>
<FileVersion>1.6.0</FileVersion>
</PropertyGroup>
<ItemGroup>
@ -24,9 +24,6 @@
</ItemGroup>
<ItemGroup>
<Reference Include="Gitea">
<HintPath>Resources\Gitea.dll</HintPath>
</Reference>
<Reference Include="WinSCPnet">
<HintPath>Resources\WinSCPnet.dll</HintPath>
</Reference>

View File

@ -1,6 +1,7 @@
using System.Net;
using Amazon.Runtime;
using Amazon.S3;
using Amazon.S3.Model;
using EftPatchHelper.Model;
using Spectre.Console;
@ -8,21 +9,21 @@ namespace EftPatchHelper.Helpers;
public class R2Helper
{
private readonly IAmazonS3? _client;
public bool IsReady => _client != null;
private Settings _settings;
private readonly AmazonS3Client? _client;
public string ConnectedDomain { get; private set; }
public string BucketName { get; private set; }
public R2Helper(Settings settings, Options options)
{
_settings = settings;
ConnectedDomain = settings.R2ConnectedDomainUrl;
BucketName = settings.R2BucketName;
if (_settings.UsingR2() && options.UplaodToR2)
if (settings.UsingR2())
{
var creds = new BasicAWSCredentials(_settings.R2AccessKeyId, _settings.R2SecretKeyId);
var creds = new BasicAWSCredentials(settings.R2AccessKeyId, settings.R2SecretKeyId);
_client = new AmazonS3Client(creds, new AmazonS3Config
{
ServiceURL = _settings.R2ServiceUrl,
ServiceURL = settings.R2ServiceUrl,
});
}
}
@ -30,37 +31,86 @@ public class R2Helper
/// <summary>
/// Deletes all content in the bucket
/// </summary>
/// <returns></returns>
/// <returns>True if all contents of the bucket were deleted, otherwise false</returns>
public async Task<bool> ClearBucketAsync()
{
AnsiConsole.MarkupLine($"[blue]Getting bucket contents: {_settings.R2BucketName}[/]");
var listBucketReponse = await _client.ListObjectsAsync(_settings.R2BucketName);
if (_client == null)
{
AnsiConsole.MarkupLine("[red]Client is unavailable[/]");
return false;
}
if (listBucketReponse.HttpStatusCode != HttpStatusCode.OK)
AnsiConsole.MarkupLine($"[blue]Getting bucket contents: {BucketName}[/]");
var listBucketResponse = await _client.ListObjectsAsync(BucketName);
if (listBucketResponse.HttpStatusCode != HttpStatusCode.OK)
{
AnsiConsole.MarkupLine("[red]failed to get bucket contents[/]");
return false;
}
AnsiConsole.MarkupLine("[blue]Removing old content");
foreach (var s3Object in listBucketReponse.S3Objects)
AnsiConsole.MarkupLine("[blue]Removing old content[/]");
foreach (var s3Object in listBucketResponse.S3Objects)
{
var deleteRepsonse = await _client.DeleteObjectAsync(_settings.R2BucketName, s3Object.Key);
var deleteResponse = await _client.DeleteObjectAsync(BucketName, s3Object.Key);
if (deleteRepsonse.HttpStatusCode != HttpStatusCode.OK)
if (deleteResponse.HttpStatusCode != HttpStatusCode.OK)
{
AnsiConsole.MarkupLine($"[red]failed to delete {_settings.R2BucketName}::{s3Object.Key}[/]");
AnsiConsole.MarkupLine($"[red]failed to delete {BucketName}::{s3Object.Key}[/]");
return false;
}
AnsiConsole.MarkupLine($"[green]{_settings.R2BucketName}::{s3Object.Key} removed[/]");
AnsiConsole.MarkupLine($"[green]{BucketName}::{s3Object.Key} removed[/]");
}
return true;
}
public async Task<bool> UplaodToBucketAsync(FileInfo file, IProgress<double> progress = null)
/// <summary>
/// Upload a file into the bucket
/// </summary>
/// <param name="file">The file to upload</param>
/// <param name="progress">A progress object to track upload progress</param>
/// <returns>True if the file was uploaded successfully, otherwise false</returns>
public async Task<bool> UploadToBucketAsync(FileInfo file, IProgress<double>? progress = null)
{
// todo: this
if (_client == null)
{
AnsiConsole.MarkupLine("[red]Client is unavailable[/]");
return false;
}
file.Refresh();
if (!file.Exists)
{
AnsiConsole.MarkupLine($"[red]File '{file.Name}' does not exist[/]");
return false;
}
var request = new PutObjectRequest
{
BucketName = BucketName,
FilePath = file.FullName,
DisablePayloadSigning = true,
};
if (progress != null)
{
request.StreamTransferProgress = (sender, progressArgs) =>
{
progress.Report(progressArgs.PercentDone);
};
}
var uploadResponse = await _client.PutObjectAsync(request);
if (uploadResponse.HttpStatusCode != HttpStatusCode.OK)
{
AnsiConsole.MarkupLine("[red]failed to upload file[/]");
return false;
}
return true;
}
}

View File

@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace EftPatchHelper.Interfaces
{
public interface IReleaseCreator : ITaskable
public interface IMirrorUploader : ITaskable
{
}
}

View File

@ -24,21 +24,19 @@ namespace EftPatchHelper.Model
/// </summary>
public string OutputPatchPath = null;
/// <summary>
/// Whether or not the user opted to create a release on gitea
/// </summary>
public bool CreateRelease = false;
/// <summary>
/// Whether or not to upload the patcher to gofile.io
/// </summary>
public bool UploadToGoFile = false;
/// <summary>
/// Whether or not to upload the pather to mega.io
/// Whether or not to upload the patcher to mega.io
/// </summary>
public bool UploadToMega = false;
/// <summary>
/// Whether or not to upload the patcher and mirror list to r2
/// </summary>
public bool UplaodToR2 = false;
/// <summary>

View File

@ -1,9 +1,5 @@
using System.Net;
using Amazon.Runtime;
using Amazon.S3;
using Amazon.S3.Model;
using EftPatchHelper.Helpers;
using EftPatchHelper.Interfaces;
using Spectre.Console;
namespace EftPatchHelper.Model;
@ -15,48 +11,25 @@ public class R2Upload : IFileUpload
public FileInfo UploadFileInfo { get; }
public bool AddHubEntry { get; }
private readonly string _bucketName;
private readonly string _connectedDomainUrl;
private readonly IAmazonS3 _s3Client;
private readonly R2Helper _r2;
public R2Upload(string connectedDomainUrl, string serviceUrl, string accessKey, string secretKey, string bucketName)
public R2Upload(FileInfo file, R2Helper r2)
{
_bucketName = bucketName;
_connectedDomainUrl = connectedDomainUrl;
var creds = new BasicAWSCredentials(accessKey, secretKey);
_s3Client = new AmazonS3Client(creds, new AmazonS3Config
{
ServiceURL = serviceUrl,
});
_r2 = r2;
UploadFileInfo = file;
ServiceName = $"R2::{_r2.BucketName} Upload";
DisplayName = $"{ServiceName} Upload";
HubEntryText = $"Download from {ServiceName}";
AddHubEntry = false;
}
public string GetLink()
{
return $"{_connectedDomainUrl}/{UploadFileInfo.Name}";
return $"{_r2.ConnectedDomain}/{UploadFileInfo.Name}";
}
public async Task<bool> UploadAsync(IProgress<double>? progress = null)
{
var uploadRequest = new PutObjectRequest
{
BucketName = _bucketName,
FilePath = UploadFileInfo.FullName,
DisablePayloadSigning = true,
};
if (progress != null)
{
uploadRequest.StreamTransferProgress = (sender, progressArgs) =>
{
progress.Report(progressArgs.PercentDone);
};
}
var uploadResponse = await _s3Client.PutObjectAsync(uploadRequest);
return uploadResponse.HttpStatusCode == HttpStatusCode.OK;
return await _r2.UploadToBucketAsync(UploadFileInfo, progress);
}
}

View File

@ -0,0 +1,8 @@
namespace EftPatchHelper.Model;
public class ReleaseInfo
{
public string AkiVersion { get; set; }
public string ClientVersion { get; set; }
public List<ReleaseInfoMirror> Mirrors { get; set; }
}

View File

@ -0,0 +1,7 @@
namespace EftPatchHelper.Model;
public class ReleaseInfoMirror
{
public string DownloadUrl { get; set; }
public string Hash { get; set; }
}

View File

@ -30,17 +30,8 @@ namespace EftPatchHelper.Model
[JsonPropertyName("patcherExePath")]
public string PatcherEXEPath { get; set; } = "";
[JsonPropertyName("giteaApiBasePath")]
public string GiteaApiBasePath { get; set; } = "";
[JsonPropertyName("giteaApiKey")]
public string GiteaApiKey { get; set; } = "";
[JsonPropertyName("giteaReleaseRepoOwner")]
public string GiteaReleaseRepoOwner { get; set; } = "";
[JsonPropertyName("giteaReleaseRepoName")]
public string GiteaReleaseRepoName { get; set; } = "";
[JsonPropertyName("latestReleaseUrl")]
public string LatestReleaseUrl { get; set; } = "";
[JsonPropertyName("megaEmail")]
public string MegaEmail { get; set; } = "";
@ -63,7 +54,7 @@ namespace EftPatchHelper.Model
[JsonPropertyName("r2ServiceUrl")]
public string R2ServiceUrl { get; set; } = "";
[JsonPropertyName("r2Bucketname")]
[JsonPropertyName("r2BucketName")]
public string R2BucketName { get; set; } = "";
[JsonPropertyName("r2AccessKeyId")]
@ -98,19 +89,6 @@ namespace EftPatchHelper.Model
}
}
public bool UsingGitea()
{
if (string.IsNullOrWhiteSpace(GiteaApiBasePath)) return false;
if (string.IsNullOrWhiteSpace(GiteaReleaseRepoOwner)) return false;
if (string.IsNullOrWhiteSpace(GiteaReleaseRepoName)) return false;
if (string.IsNullOrWhiteSpace(GiteaApiKey)) return false;
return true;
}
public bool UsingMega()
{
if (string.IsNullOrWhiteSpace(MegaEmail)) return false;

View File

@ -32,7 +32,7 @@ public class SftpUpload : IFileUpload
ServiceName = _sftpInfo.Hostname;
DisplayName = $"{ServiceName} Upload";
HubEntryText = $"Download from {ServiceName}";
AddHubEntry = false;
AddHubEntry = sftpInfo.AllowHubEntry;
}
public string GetLink()

View File

@ -25,6 +25,9 @@ public class SftpUploadInfo
[JsonPropertyName("httpPath")]
public string HttpPath { get; set; } = "";
[JsonPropertyName("allowHubEntry")]
public bool AllowHubEntry { get; set; } = false;
public bool IsValid()
{
if (string.IsNullOrWhiteSpace(Username))

View File

@ -20,9 +20,9 @@ namespace EftPatchHelper
ITaskable _fileProcessingTasks;
ITaskable _patchGenTasks;
ITaskable _patchTestingTasks;
private ITaskable _compressPatcherTasks;
ITaskable _createReleaseTasks;
ITaskable _compressPatcherTasks;
ITaskable _uploadTasks;
ITaskable _uploadMirrorList;
public static void Main(string[] args)
{
@ -48,7 +48,7 @@ namespace EftPatchHelper
IPatchTestingTasks patchTestingTasks,
ICompressPatcherTasks compressPatcherTasks,
IUploadTasks uploadTasks,
IReleaseCreator createReleaseTasks
IMirrorUploader uploadMirrorList
)
{
_settingsTasks = settingsTasks;
@ -58,7 +58,7 @@ namespace EftPatchHelper
_patchGenTasks = patchGenTasks;
_patchTestingTasks = patchTestingTasks;
_compressPatcherTasks = compressPatcherTasks;
_createReleaseTasks = createReleaseTasks;
_uploadMirrorList = uploadMirrorList;
_uploadTasks = uploadTasks;
}
@ -72,14 +72,17 @@ namespace EftPatchHelper
_patchTestingTasks.Run();
_compressPatcherTasks.Run();
_uploadTasks.Run();
_createReleaseTasks.Run();
_uploadMirrorList.Run();
}
private static IHost ConfigureHost(string[] args)
{
return Host.CreateDefaultBuilder(args).ConfigureServices((_, services) =>
{
HttpClient client = new HttpClient() { Timeout = TimeSpan.FromHours(1) };
services.AddSingleton<Options>();
services.AddSingleton(client);
services.AddSingleton<Settings>(serviceProvider =>
{
var configuration = serviceProvider.GetRequiredService<IConfiguration>();
@ -90,6 +93,7 @@ namespace EftPatchHelper
services.AddSingleton<FileHelper>();
services.AddSingleton<ZipHelper>();
services.AddSingleton<R2Helper>();
services.AddScoped<EftClientSelector>();
@ -100,8 +104,8 @@ namespace EftPatchHelper
services.AddTransient<IPatchGenTasks, PatchGenTasks>();
services.AddTransient<IPatchTestingTasks, PatchTestingTasks>();
services.AddTransient<ICompressPatcherTasks, CompressPatcherTasks>();
services.AddTransient<IReleaseCreator, CreateReleaseTasks>();
services.AddTransient<IUploadTasks, UploadTasks>();
services.AddTransient<IMirrorUploader, UploadMirrorListTasks>();
services.AddTransient<Program>();
})
.ConfigureAppConfiguration((_, config) =>

View File

@ -1,8 +1,8 @@
using EftPatchHelper.Extensions;
using System.Net.Http.Json;
using EftPatchHelper.Extensions;
using EftPatchHelper.Helpers;
using EftPatchHelper.Interfaces;
using EftPatchHelper.Model;
using Gitea.Api;
using Spectre.Console;
namespace EftPatchHelper.Tasks
@ -12,11 +12,13 @@ namespace EftPatchHelper.Tasks
private Settings _settings;
private Options _options;
private EftClientSelector _clientSelector;
private HttpClient _http;
public ClientSelectionTask(Settings settings, Options options, EftClientSelector clientSelector)
public ClientSelectionTask(Settings settings, Options options, EftClientSelector clientSelector, HttpClient client)
{
_settings = settings;
_options = options;
_http = client;
_clientSelector = clientSelector;
}
@ -58,22 +60,16 @@ namespace EftPatchHelper.Tasks
private string GetCurrentReleaseVersion()
{
if (!_settings.UsingGitea())
return "";
return AnsiConsole.Status().Start("Starting...", ctx =>
return AnsiConsole.Status().Start("Starting...", async ctx =>
{
RepositoryApi repo = new RepositoryApi();
ctx.Spinner = Spinner.Known.Dots8;
ctx.Status = "Getting latest release ...";
var releases = repo.RepoListReleases("SPT-AKI", "Stable-releases");
var blah = await _http.GetAsync(_settings.LatestReleaseUrl);
var release = await blah.Content.ReadFromJsonAsync<ReleaseInfo>();
var release = releases.First(x => !x.Prerelease).Name.Split('(')[1];
return release.Remove(release.Length - 1);
});
return release?.ClientVersion ?? "failed to get version :(";
}).GetAwaiter().GetResult();
}
public void Run()

View File

@ -1,127 +0,0 @@
using EftPatchHelper.Interfaces;
using EftPatchHelper.Model;
using Gitea.Api;
using Gitea.Model;
using Gitea.Client;
using Spectre.Console;
using EftPatchHelper.Extensions;
using System.Text.Json;
namespace EftPatchHelper.Tasks
{
public class CreateReleaseTasks : IReleaseCreator
{
private Settings _settings;
private Options _options;
public CreateReleaseTasks(Settings settings, Options options)
{
_settings = settings;
_options = options;
}
private bool UploadMirrorList(FileInfo file)
{
var r2Uplaod = new R2Upload(_settings.R2ConnectedDomainUrl, _settings.R2ServiceUrl, _settings.R2AccessKeyId,
_settings.R2SecretKeyId, _settings.R2BucketName);
return true;
}
// private bool UploadAsset(FileInfo file, Release release, RepositoryApi repo)
// {
// return AnsiConsole.Status().Spinner(Spinner.Known.Point).Start("Uploading Asset", (StatusContext context) =>
// {
// AnsiConsole.MarkupLine($"[blue]Adding release asset: {file.Name.EscapeMarkup()}[/]");
//
// file.Refresh();
//
// if (!file.Exists)
// {
// AnsiConsole.MarkupLine($"[red]File does not exist: {file.FullName}[/]");
// }
//
// using var fileStream = file.OpenRead();
//
// try
// {
// var attachment = repo.RepoCreateReleaseAttachment(_settings.GiteaReleaseRepoOwner, _settings.GiteaReleaseRepoName, release.Id, fileStream, file.Name);
//
// AnsiConsole.MarkupLine("[green]Upload Complete[/]");
//
// return true;
// }
// catch (Exception ex)
// {
// AnsiConsole.MarkupLine("[red]Failed to upload asset[/]");
//
// AnsiConsole.WriteException(ex);
//
// return false;
// }
// });
// }
private Release? MakeRelease(RepositoryApi repo)
{
AnsiConsole.Write("Adding release to gitea ... ");
string sourceTail = _options.SourceClient.Version.Split('.').Last();
string targetTail = _options.TargetClient.Version.Split('.').Last();
string releaseName = $"{sourceTail} to {targetTail}";
string tag = $"{sourceTail}_{new Random().Next(100, 999)}";
try
{
var release = repo.RepoCreateRelease(_settings.GiteaReleaseRepoOwner, _settings.GiteaReleaseRepoName, new CreateReleaseOption(null, false, releaseName, false, tag, null));
AnsiConsole.MarkupLine($"[green]Release added: {release.Name.EscapeMarkup()}[/]");
return release;
}
catch(Exception ex)
{
AnsiConsole.MarkupLine($"[red]Failed to create release[/]");
AnsiConsole.WriteException(ex);
return null;
}
}
public bool CreateMirrorList(FileInfo mirrorListFileInfo)
{
List<DownloadMirror> mirrors = _options.MirrorList.Values.ToList();
string json = JsonSerializer.Serialize(mirrors, new JsonSerializerOptions() { WriteIndented = true });
File.WriteAllText(mirrorListFileInfo.FullName, json);
mirrorListFileInfo.Refresh();
return mirrorListFileInfo.Exists;
}
public void Run()
{
AnsiConsole.WriteLine();
var fileInfo = new FileInfo(Path.Join(Environment.CurrentDirectory, "mirrors.json"));
CreateMirrorList(fileInfo);
if (!_options.CreateRelease) return;
var repo = new RepositoryApi(Configuration.Default);
var release = MakeRelease(repo).ValidateOrExit<Release>();
//UploadAsset(fileInfo, release, repo);
}
}
}

View File

@ -1,7 +1,6 @@
using EftPatchHelper.Extensions;
using EftPatchHelper.Interfaces;
using EftPatchHelper.Model;
using Gitea.Client;
using Spectre.Console;
namespace EftPatchHelper.Tasks
@ -63,14 +62,6 @@ namespace EftPatchHelper.Tasks
{
_options.IgnoreExistingDirectories = new ConfirmationPrompt("Skip existing directories? (you will be prompted if no)").Show(AnsiConsole.Console);
if (_settings.UsingGitea())
{
Configuration.Default.BasePath = _settings.GiteaApiBasePath;
Configuration.Default.AddApiKey("token", _settings.GiteaApiKey);
_options.CreateRelease = new ConfirmationPrompt("Create a release on gitea?").Show(AnsiConsole.Console);
}
if (_settings.UsingMega())
{
_options.UploadToMega = new ConfirmationPrompt("Upload to Mega?").Show(AnsiConsole.Console);

View File

@ -0,0 +1,64 @@
using EftPatchHelper.Interfaces;
using EftPatchHelper.Model;
using Spectre.Console;
using System.Text.Json;
using EftPatchHelper.Helpers;
namespace EftPatchHelper.Tasks
{
public class UploadMirrorListTasks : IMirrorUploader
{
private Options _options;
private R2Helper _r2;
public UploadMirrorListTasks(Options options, R2Helper r2)
{
_options = options;
_r2 = r2;
}
private async Task<bool> UploadMirrorList(FileInfo file)
{
return await AnsiConsole.Progress().Columns(new ProgressColumn[]
{
new TaskDescriptionColumn() { Alignment = Justify.Left },
new ProgressBarColumn(),
new PercentageColumn(),
new RemainingTimeColumn(),
new SpinnerColumn(Spinner.Known.Dots2),
})
.StartAsync(async ctx =>
{
var uploadTask = ctx.AddTask("mirrors.json upload");
var progress = new Progress<double>((p) => { uploadTask.Value = p; });
return await _r2.UploadToBucketAsync(file, progress);
});
}
public bool CreateMirrorList(FileInfo mirrorListFileInfo)
{
List<DownloadMirror> mirrors = _options.MirrorList.Values.ToList();
string json = JsonSerializer.Serialize(mirrors, new JsonSerializerOptions() { WriteIndented = true });
File.WriteAllText(mirrorListFileInfo.FullName, json);
mirrorListFileInfo.Refresh();
return mirrorListFileInfo.Exists;
}
public void Run()
{
AnsiConsole.WriteLine();
var fileInfo = new FileInfo(Path.Join(Environment.CurrentDirectory, "mirrors.json"));
CreateMirrorList(fileInfo);
UploadMirrorList(fileInfo).GetAwaiter().GetResult();
}
}
}

View File

@ -3,20 +3,23 @@ using EftPatchHelper.Interfaces;
using EftPatchHelper.Model;
using Spectre.Console;
using System.Security.Cryptography;
using EftPatchHelper.Helpers;
namespace EftPatchHelper.Tasks
{
public class UploadTasks : IUploadTasks
{
private Options _options;
private Settings _settings;
private List<IFileUpload> _fileUploads = new List<IFileUpload>();
private Dictionary<IFileUpload, ProgressTask> uploadTasks = new Dictionary<IFileUpload, ProgressTask>();
private readonly Options _options;
private readonly Settings _settings;
private readonly R2Helper _r2;
private readonly List<IFileUpload> _fileUploads = new();
private readonly Dictionary<IFileUpload, ProgressTask> _uploadTasks = new();
public UploadTasks(Options options, Settings settings)
public UploadTasks(Options options, Settings settings, R2Helper r2)
{
_options = options;
_settings = settings;
_r2 = r2;
}
private static string GetFileHash(FileInfo file)
@ -50,10 +53,14 @@ namespace EftPatchHelper.Tasks
if (_settings.UsingR2() && _options.UplaodToR2)
{
var r2 = new R2Upload(_settings.R2ConnectedDomainUrl, _settings.R2ServiceUrl, _settings.R2AccessKeyId,
_settings.R2SecretKeyId, _settings.R2BucketName);
if (!await _r2.ClearBucketAsync())
{
return false;
}
var r2 = new R2Upload(patcherFile, _r2);
_fileUploads.Add(r2);
AnsiConsole.WriteLine($"Added R2: {_settings.R2BucketName}");
AnsiConsole.WriteLine($"Added R2::{_r2.BucketName}");
}
if (_settings.SftpUploads.Count > 0 && _options.UploadToSftpSites)
@ -157,10 +164,10 @@ namespace EftPatchHelper.Tasks
{
var task = context.AddTask($"[purple][[Pending]][/] {file.DisplayName} - [blue]{BytesToString(file.UploadFileInfo.Length)}[/]");
task.IsIndeterminate = true;
uploadTasks.Add(file, task);
_uploadTasks.Add(file, task);
}
foreach(var pair in uploadTasks)
foreach(var pair in _uploadTasks)
{
// set the value of the progress task object
var progress = new System.Progress<double>((d) => pair.Value.Value = d);
@ -193,7 +200,7 @@ namespace EftPatchHelper.Tasks
public void Run()
{
if (!_options.UploadToGoFile && !_options.UploadToMega && !_options.UploadToSftpSites) return;
if (!_options.UploadToGoFile && !_options.UploadToMega && !_options.UploadToSftpSites && !_options.UplaodToR2) return;
UploadAllFiles().GetAwaiter().GetResult().ValidateOrExit();

View File

@ -6,15 +6,17 @@
"patcherExePath": "",
"autoZip": true,
"autoClose": false,
"giteaApiBasePath": "", //You can leave the gitea settings blank if you don't need to create releases on gitea
"giteaApiKey": "",
"giteaReleaseRepoOwner": "",
"giteaReleaseRepoName": "",
"latestReleaseUrl": "",
"megaEmail": "",
"megaPassword": "",
"megaUploadFolder": "",
"goFileApiKey": "",
"goFileFolderId": "",
"r2ConnectedDomainUrl": "",
"r2ServiceUrl": "",
"r2BucketName": "",
"r2AccessKeyId": "",
"r2SecretKeyId": "",
"sftpUploads": [
{
"username": "example-remove-before-using",
@ -27,5 +29,3 @@
}
]
}