adds application infra and first draft of persistence entities
This commit is contained in:
parent
18124da30f
commit
28f4ec09f2
@ -9,7 +9,7 @@
|
||||
]
|
||||
},
|
||||
"dotnet-ef": {
|
||||
"version": "7.0.3",
|
||||
"version": "7.0.10",
|
||||
"commands": [
|
||||
"dotnet-ef"
|
||||
]
|
||||
|
@ -72,6 +72,7 @@
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=appsettings/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
|
||||
|
||||
|
17
PiwigoDirectorySync/AppSettings.cs
Normal file
17
PiwigoDirectorySync/AppSettings.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace PiwigoDirectorySync;
|
||||
|
||||
public static class AppSettings
|
||||
{
|
||||
public static IConfigurationRoot Config { get; } = new ConfigurationBuilder().SetBasePath(AppContext.BaseDirectory)
|
||||
.AddJsonFile("appsettings.json", true)
|
||||
.AddJsonFile(Path.Combine(Environment.CurrentDirectory, "appsettings.json"), true)
|
||||
.AddJsonFile("/etc/PiwigoDirectorySync/appsettings.json", true)
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
|
||||
public static Settings Settings { get; } = Config.GetSection("settings").Get<Settings>() ?? throw new InvalidOperationException("Could not parse settings");
|
||||
|
||||
public static string ConnectionString => Config.GetConnectionString(Settings.DbProvider) ?? throw new InvalidOperationException($"Could not find connection string for provider {Settings.DbProvider}");
|
||||
}
|
37
PiwigoDirectorySync/Commands/ScanCommand.cs
Normal file
37
PiwigoDirectorySync/Commands/ScanCommand.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Channels;
|
||||
using NLog;
|
||||
using PiwigoDirectorySync.Service;
|
||||
using Spectre.Console.Cli;
|
||||
|
||||
namespace PiwigoDirectorySync.Commands;
|
||||
|
||||
public class ScanCommand : AsyncCommand<ScanSettings>
|
||||
{
|
||||
private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public override async Task<int> ExecuteAsync(CommandContext context, ScanSettings settings)
|
||||
{
|
||||
Logger.Info("Starting scanner and remover");
|
||||
var stopWatch = Stopwatch.StartNew();
|
||||
|
||||
var cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
var fileQueue = Channel.CreateUnbounded<string>();
|
||||
|
||||
var indexer = new FileIndexer(fileQueue);
|
||||
var indexerTask = indexer.StartProcessingAsync(cancellationTokenSource.Token);
|
||||
|
||||
var scanner = new FileScanner(fileQueue);
|
||||
await scanner.ScanAsync(cancellationTokenSource.Token);
|
||||
|
||||
fileQueue.Writer.Complete();
|
||||
|
||||
await Task.WhenAll(fileQueue.Reader.Completion, indexerTask);
|
||||
|
||||
stopWatch.Stop();
|
||||
Logger.Info($"Processed {indexer.TotalFilesScanned} image files in {stopWatch.Elapsed.TotalSeconds} seconds");
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
7
PiwigoDirectorySync/Commands/ScanSettings.cs
Normal file
7
PiwigoDirectorySync/Commands/ScanSettings.cs
Normal file
@ -0,0 +1,7 @@
|
||||
using Spectre.Console.Cli;
|
||||
|
||||
namespace PiwigoDirectorySync.Commands;
|
||||
|
||||
public class ScanSettings : CommandSettings
|
||||
{
|
||||
}
|
22
PiwigoDirectorySync/Persistence/AlbumEntity.cs
Normal file
22
PiwigoDirectorySync/Persistence/AlbumEntity.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace PiwigoDirectorySync.Persistence;
|
||||
|
||||
public class AlbumEntity
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int Id { get; set; }
|
||||
|
||||
public int? ParentId { get; set; }
|
||||
public AlbumEntity? Parent { get; set; }
|
||||
public required int ServerId { get; set; }
|
||||
public PiwigoServerEntity Server { get; set; } = null!;
|
||||
public required string Name { get; set; }
|
||||
public required string DirectoryName { get; set; }
|
||||
public string FullDirectory => Parent is not null ? $"{Parent.FullDirectory}{Path.DirectorySeparatorChar}{DirectoryName}" : DirectoryName;
|
||||
|
||||
public int? PiwigoAlbumId { get; set; }
|
||||
}
|
20
PiwigoDirectorySync/Persistence/ImageEntity.cs
Normal file
20
PiwigoDirectorySync/Persistence/ImageEntity.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace PiwigoDirectorySync.Persistence;
|
||||
|
||||
public class ImageEntity
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int Id { get; set; }
|
||||
public required string Filename { get; set; }
|
||||
public DateTime LastChange { get; set; }
|
||||
public string? Md5Sum { get; set; }
|
||||
public required int AlbumId { get; set; }
|
||||
public AlbumEntity Album { get; set; } = null!;
|
||||
public int ServerImageId { get; set; }
|
||||
public bool UploadRequired { get; set; }
|
||||
public bool DeleteRequired { get; set; }
|
||||
}
|
25
PiwigoDirectorySync/Persistence/PersistenceContext.cs
Normal file
25
PiwigoDirectorySync/Persistence/PersistenceContext.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace PiwigoDirectorySync.Persistence;
|
||||
|
||||
public class PersistenceContext : DbContext
|
||||
{
|
||||
public DbSet<PiwigoServerEntity> PiwigoServers { get; set; }
|
||||
public DbSet<AlbumEntity> PiwigoAlbums { get; set; }
|
||||
public DbSet<ImageEntity> PiwigoImages { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder options)
|
||||
{
|
||||
switch (AppSettings.Settings.DbProvider)
|
||||
{
|
||||
case "Sqlite":
|
||||
options.UseSqlite(AppSettings.ConnectionString);
|
||||
break;
|
||||
case "InMemory":
|
||||
options.UseInMemoryDatabase(AppSettings.ConnectionString);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"DbProvider {AppSettings.Settings.DbProvider} is not supported");
|
||||
}
|
||||
}
|
||||
}
|
19
PiwigoDirectorySync/Persistence/PiwigoServerEntity.cs
Normal file
19
PiwigoDirectorySync/Persistence/PiwigoServerEntity.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace PiwigoDirectorySync.Persistence;
|
||||
|
||||
[Index(nameof(Name), IsUnique = true)]
|
||||
public class PiwigoServerEntity
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public int Id { get; set; }
|
||||
|
||||
public required string Name { get; set; }
|
||||
public required string Url { get; set; }
|
||||
public required string Username { get; set; }
|
||||
public required string Password { get; set; }
|
||||
public required string RootDirectory { get; set; }
|
||||
}
|
@ -1,17 +1,46 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<PublishTrimmed>false</PublishTrimmed>
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<UserSecretsId>c68c0447-8c7d-4e88-bcc6-96a9853828c7</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Piwigo.Client" Version="0.1.0.17"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0"/>
|
||||
<PackageReference Include="NLog.Extensions.Logging" Version="5.2.0"/>
|
||||
<PackageReference Include="Spectre.Console.Analyzer" Version="0.47.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Spectre.Console.Cli" Version="0.47.0"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.10">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.10"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\.dockerignore">
|
||||
<Link>.dockerignore</Link>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="appsettings.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
|
@ -1,3 +1,15 @@
|
||||
// See https://aka.ms/new-console-template for more information
|
||||
using NLog;
|
||||
using NLog.Extensions.Logging;
|
||||
using PiwigoDirectorySync;
|
||||
using PiwigoDirectorySync.Commands;
|
||||
using Spectre.Console.Cli;
|
||||
|
||||
Console.WriteLine("Hello, World!");
|
||||
LogManager.Configuration = new NLogLoggingConfiguration(AppSettings.Config.GetSection("NLog"));
|
||||
var logFactory = LogManager.Setup().LogFactory;
|
||||
var logger = logFactory.GetCurrentClassLogger();
|
||||
|
||||
logger.Info("Starting command app");
|
||||
var app = new CommandApp();
|
||||
app.Configure(c => { c.AddCommand<ScanCommand>("scan"); });
|
||||
|
||||
return app.Run(args);
|
17
PiwigoDirectorySync/Service/FileIndexer.cs
Normal file
17
PiwigoDirectorySync/Service/FileIndexer.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System.Threading.Channels;
|
||||
|
||||
namespace PiwigoDirectorySync.Service;
|
||||
|
||||
public class FileIndexer
|
||||
{
|
||||
private readonly Channel<string> _fileQueue;
|
||||
|
||||
public FileIndexer(Channel<string> fileQueue)
|
||||
{
|
||||
_fileQueue = fileQueue ?? throw new ArgumentNullException(nameof(fileQueue));
|
||||
}
|
||||
|
||||
public int TotalFilesScanned { get; }
|
||||
|
||||
public Task StartProcessingAsync(CancellationToken token) => throw new NotImplementedException();
|
||||
}
|
15
PiwigoDirectorySync/Service/FileScanner.cs
Normal file
15
PiwigoDirectorySync/Service/FileScanner.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System.Threading.Channels;
|
||||
|
||||
namespace PiwigoDirectorySync.Service;
|
||||
|
||||
public class FileScanner
|
||||
{
|
||||
private readonly Channel<string> _fileQueue;
|
||||
|
||||
public FileScanner(Channel<string> fileQueue)
|
||||
{
|
||||
_fileQueue = fileQueue ?? throw new ArgumentNullException(nameof(fileQueue));
|
||||
}
|
||||
|
||||
public async Task ScanAsync(CancellationToken token) => throw new NotImplementedException();
|
||||
}
|
9
PiwigoDirectorySync/Settings.cs
Normal file
9
PiwigoDirectorySync/Settings.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace PiwigoDirectorySync;
|
||||
|
||||
public class Settings
|
||||
{
|
||||
public string DbProvider { get; set; } = null!;
|
||||
public string ImageRootDirectory { get; set; } = null!;
|
||||
|
||||
public bool HasErrors => string.IsNullOrEmpty(DbProvider) || string.IsNullOrEmpty(ImageRootDirectory);
|
||||
}
|
5
PiwigoDirectorySync/addMigration.ps1
Normal file
5
PiwigoDirectorySync/addMigration.ps1
Normal file
@ -0,0 +1,5 @@
|
||||
$comment=$args[0]
|
||||
|
||||
write-host "adding migration for Sqlite"
|
||||
dotnet ef migrations add --project PiwigoDirectorySync.csproj --startup-project PiwigoDirectorySync.csproj --context PiwigoDirectorySync.Persistence.PersistenceContext "$comment" --output-dir Migrations -- --DbProvider Sqlite
|
||||
#dotnet ef migrations add --project PiwigoDirectorySync/PiwigoDirectorySync.csproj --startup-project PiwigoDirectorySync/PiwigoDirectorySync.csproj --context PiwigoDirectorySync.Persistence.Persistence.PersistenceContext "$comment" --output-dir Migrations -- --DbProvider Sqlite
|
68
PiwigoDirectorySync/appsettings.json
Normal file
68
PiwigoDirectorySync/appsettings.json
Normal file
@ -0,0 +1,68 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"Sqlite": "Data Source=piwigoSync.db",
|
||||
"InMemory": "InMemorySyncDb"
|
||||
},
|
||||
"Settings": {
|
||||
"DbProvider": "Sqlite",
|
||||
"ImageRootDirectory": ".\\"
|
||||
},
|
||||
"NLog": {
|
||||
"autoReload": true,
|
||||
"throwConfigExceptions": true,
|
||||
"default-wrapper": {
|
||||
"type": "AsyncWrapper",
|
||||
"overflowAction": "Block"
|
||||
},
|
||||
"targets": {
|
||||
"cli-console": {
|
||||
"type": "ColoredConsole",
|
||||
"layout": "${longdate} | ${uppercase:${level}} | ${logger} | ${message} ${exception:format=tostring}",
|
||||
"rowHighlightingRules": [
|
||||
{
|
||||
"condition": "level == LogLevel.Trace",
|
||||
"foregroundColor": "DarkGray"
|
||||
},
|
||||
{
|
||||
"condition": "level == LogLevel.Debug",
|
||||
"foregroundColor": "White"
|
||||
},
|
||||
{
|
||||
"condition": "level == LogLevel.Info",
|
||||
"foregroundColor": "DarkGreen"
|
||||
},
|
||||
{
|
||||
"condition": "level == LogLevel.Warn",
|
||||
"foregroundColor": "Yellow"
|
||||
},
|
||||
{
|
||||
"condition": "level == LogLevel.Error",
|
||||
"foregroundColor": "DarkMagenta"
|
||||
},
|
||||
{
|
||||
"condition": "level == LogLevel.Fatal",
|
||||
"foregroundColor": "DarkRed"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"rules": [
|
||||
{
|
||||
"logger": "*",
|
||||
"minLevel": "Info",
|
||||
"writeTo": "cli-console"
|
||||
},
|
||||
{
|
||||
"logger": "Microsoft.*",
|
||||
"maxLevel": "Info"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
34
README.md
34
README.md
@ -1,2 +1,36 @@
|
||||
# PiwigoDirectorySync
|
||||
|
||||
This application synchronizes the local directory structure with piwigo servers.
|
||||
Each directory level gets created as album or sub album in piwigo.
|
||||
|
||||
## Building / tooling
|
||||
|
||||
### Restoring dotnet tools
|
||||
|
||||
In the root path you may just run the command ``dotnet tool restore`` to install all dotnet tools and extensions used in this project.
|
||||
|
||||
### Handle development secrets
|
||||
|
||||
To make sure no local development db configuration gets committed, we use the dotnet-user-secrets tool.
|
||||
|
||||
When you configure your environment for the first time and you do not like the default settings, use the following commands to set the secrets:
|
||||
|
||||
``
|
||||
cd PiwigoDirectorySync
|
||||
dotnet user-secrets set "DbProvider" "MariaDb"
|
||||
dotnet user-secrets set "ConnectionStrings:MariaDb" "Server=localhost;User Id=photowfdev;Password=password123;Database=photowfdev"
|
||||
``
|
||||
|
||||
You'll find your secrets under `~/.microsoft/usersecrets/c68c0447-8c7d-4e88-bcc6-96a9853828c7/secrets.json`
|
||||
|
||||
### Docker
|
||||
|
||||
Build the application and docker image for hosting using docker.
|
||||
|
||||
TODO: add some docker build details
|
||||
|
||||
### Publish
|
||||
|
||||
Build the application for manual installation using publish.
|
||||
|
||||
TODO: add some publish details
|
Loading…
Reference in New Issue
Block a user