diff --git a/.env b/.env index 76bcc5ba9..b3af88f32 100644 --- a/.env +++ b/.env @@ -1,3 +1,7 @@ +ARCHIVAL_GIT_REPOSITORY_DIRECTORY=archives +ARCHIVAL_GIT_USER_NAME=FilterLists Archival +ARCHIVAL_GIT_USER_EMAIL=noreply@filterlists.com + DIRECTORY_CONNECTION_STRING=Server=directory-db;Database=filterlists;User Id=filterlists;Password=filterlists; DIRECTORY_DB_USER=filterlists DIRECTORY_DB_PASSWORD=filterlists diff --git a/services/archival/src/FilterLists.Archival.Api/Startup.cs b/services/archival/src/FilterLists.Archival.Api/Startup.cs index 271dbb4f7..745257d05 100644 --- a/services/archival/src/FilterLists.Archival.Api/Startup.cs +++ b/services/archival/src/FilterLists.Archival.Api/Startup.cs @@ -1,16 +1,24 @@ using FilterLists.Archival.Application; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace FilterLists.Archival.Api { - public class Startup + internal class Startup { - public static void ConfigureServices(IServiceCollection services) + public Startup(IConfiguration configuration) { - services.AddApplicationServices(); + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public void ConfigureServices(IServiceCollection services) + { + services.AddApplicationServices(Configuration); } public static void Configure(IApplicationBuilder app, IWebHostEnvironment env) diff --git a/services/archival/src/FilterLists.Archival.Application/ServiceCollectionExtension.cs b/services/archival/src/FilterLists.Archival.Application/ServiceCollectionExtension.cs index 360942232..6cc9a52cb 100644 --- a/services/archival/src/FilterLists.Archival.Application/ServiceCollectionExtension.cs +++ b/services/archival/src/FilterLists.Archival.Application/ServiceCollectionExtension.cs @@ -1,15 +1,16 @@ using FilterLists.Archival.Infrastructure; using MediatR; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; namespace FilterLists.Archival.Application { public static class ServiceCollectionExtension { - public static void AddApplicationServices(this IServiceCollection services) + public static void AddApplicationServices(this IServiceCollection services, IConfiguration configuration) { services.AddMediatR(typeof(ServiceCollectionExtension).Assembly); - services.AddInfrastructureServices(); + services.AddInfrastructureServices(configuration); } } } \ No newline at end of file diff --git a/services/archival/src/FilterLists.Archival.Infrastructure/FilterLists.Archival.Infrastructure.csproj b/services/archival/src/FilterLists.Archival.Infrastructure/FilterLists.Archival.Infrastructure.csproj index 747a00669..234e53157 100644 --- a/services/archival/src/FilterLists.Archival.Infrastructure/FilterLists.Archival.Infrastructure.csproj +++ b/services/archival/src/FilterLists.Archival.Infrastructure/FilterLists.Archival.Infrastructure.csproj @@ -25,11 +25,14 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/services/archival/src/FilterLists.Archival.Infrastructure/Options/GitOptions.cs b/services/archival/src/FilterLists.Archival.Infrastructure/Options/GitOptions.cs new file mode 100644 index 000000000..c1015ab9a --- /dev/null +++ b/services/archival/src/FilterLists.Archival.Infrastructure/Options/GitOptions.cs @@ -0,0 +1,11 @@ +namespace FilterLists.Archival.Infrastructure.Options +{ + public class GitOptions + { + public const string Key = nameof(Key); + + public string RepositoryDirectory { get; set; } + public string UserName { get; set; } + public string UserEmail { get; set; } + } +} \ No newline at end of file diff --git a/services/archival/src/FilterLists.Archival.Infrastructure/Persistence/GitFileArchiver.cs b/services/archival/src/FilterLists.Archival.Infrastructure/Persistence/GitFileArchiver.cs new file mode 100644 index 000000000..59ed546cb --- /dev/null +++ b/services/archival/src/FilterLists.Archival.Infrastructure/Persistence/GitFileArchiver.cs @@ -0,0 +1,42 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using FilterLists.Archival.Infrastructure.Options; +using LibGit2Sharp; +using Microsoft.Extensions.Options; + +namespace FilterLists.Archival.Infrastructure.Persistence +{ + internal class GitFileArchiver : IArchiveFile + { + private readonly GitOptions _options; + private readonly IRepository _repository; + + public GitFileArchiver(IOptions gitOptions, IRepository gitRepository) + { + _options = gitOptions.Value; + _repository = gitRepository; + } + + public async Task ArchiveFileAsync( + Stream fileContents, + string fileName, + CancellationToken cancellationToken = default) + { + await using var output = File.OpenWrite(fileName); + await fileContents.CopyToAsync(output, cancellationToken); + if (!cancellationToken.IsCancellationRequested) + Commit(fileName); + } + + private void Commit(string fileName) + { + Commands.Stage(_repository, fileName); + var utcNow = DateTime.UtcNow; + var signature = new Signature(_options.UserName, _options.UserEmail, utcNow); + var message = $"archive {fileName}"; + _repository.Commit(message, signature, signature); + } + } +} \ No newline at end of file diff --git a/services/archival/src/FilterLists.Archival.Infrastructure/Persistence/IArchiveFile.cs b/services/archival/src/FilterLists.Archival.Infrastructure/Persistence/IArchiveFile.cs new file mode 100644 index 000000000..24642d736 --- /dev/null +++ b/services/archival/src/FilterLists.Archival.Infrastructure/Persistence/IArchiveFile.cs @@ -0,0 +1,11 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace FilterLists.Archival.Infrastructure.Persistence +{ + public interface IArchiveFile + { + Task ArchiveFileAsync(Stream fileContents, string fileName, CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/services/archival/src/FilterLists.Archival.Infrastructure/Persistence/ServiceCollectionExtension.cs b/services/archival/src/FilterLists.Archival.Infrastructure/Persistence/ServiceCollectionExtension.cs new file mode 100644 index 000000000..475d4dce4 --- /dev/null +++ b/services/archival/src/FilterLists.Archival.Infrastructure/Persistence/ServiceCollectionExtension.cs @@ -0,0 +1,25 @@ +using FilterLists.Archival.Infrastructure.Options; +using LibGit2Sharp; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace FilterLists.Archival.Infrastructure.Persistence +{ + public static class ServiceCollectionExtension + { + internal static void AddGitServices(this IServiceCollection services, IConfiguration configuration) + { + services.Configure(configuration.GetSection(GitOptions.Key)); + services.AddTransient(_ => + { + var gitOptions = new GitOptions(); + configuration.GetSection(GitOptions.Key).Bind(gitOptions); + + if (!Repository.IsValid(gitOptions.RepositoryDirectory)) + Repository.Init(gitOptions.RepositoryDirectory); + + return new Repository(gitOptions.RepositoryDirectory); + }); + } + } +} \ No newline at end of file diff --git a/services/archival/src/FilterLists.Archival.Infrastructure/ServiceCollectionExtension.cs b/services/archival/src/FilterLists.Archival.Infrastructure/ServiceCollectionExtension.cs index 4f7ffec47..a0a3ff86c 100644 --- a/services/archival/src/FilterLists.Archival.Infrastructure/ServiceCollectionExtension.cs +++ b/services/archival/src/FilterLists.Archival.Infrastructure/ServiceCollectionExtension.cs @@ -1,11 +1,18 @@ -using Microsoft.Extensions.DependencyInjection; +using System; +using FilterLists.Archival.Infrastructure.Persistence; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; namespace FilterLists.Archival.Infrastructure { public static class ServiceCollectionExtension { - public static void AddInfrastructureServices(this IServiceCollection services) + public static void AddInfrastructureServices(this IServiceCollection services, IConfiguration configuration) { + _ = configuration ?? throw new ArgumentNullException(nameof(configuration)); + + services.AddGitServices(configuration); + services.AddTransient(); } } } \ No newline at end of file