using Couchbase.Lite; using Microsoft.Extensions.Options; using WorkTracker.Configuration; namespace WorkTracker.Services.Storage; public sealed class CouchbaseLiteDatabaseProvider : IDisposable { private const string AppSettingsCollectionName = "app_settings"; private const string SystemCollectionName = "system"; private const string UsersCollectionName = "users"; private const string WorkDaysCollectionName = "workdays"; private const string SchemaVersionDocumentId = "database_schema"; public const int CurrentDatabaseSchemaVersion = 1; private readonly Database database; private readonly IReadOnlyDictionary backupCollections; public CouchbaseLiteDatabaseProvider(IOptions options, IHostEnvironment environment) { var configuredOptions = options.Value; var databaseDirectory = ResolveDirectory(configuredOptions.Directory, environment.ContentRootPath); Directory.CreateDirectory(databaseDirectory); database = new Database( configuredOptions.DatabaseName, new DatabaseConfiguration { Directory = databaseDirectory }); System = database.GetCollection(SystemCollectionName) ?? database.CreateCollection(SystemCollectionName); AppSettings = database.GetCollection(AppSettingsCollectionName) ?? database.CreateCollection(AppSettingsCollectionName); Users = database.GetCollection(UsersCollectionName) ?? database.CreateCollection(UsersCollectionName); WorkDays = database.GetCollection(WorkDaysCollectionName) ?? database.CreateCollection(WorkDaysCollectionName); backupCollections = new Dictionary(StringComparer.Ordinal) { [AppSettingsCollectionName] = AppSettings, [UsersCollectionName] = Users, [WorkDaysCollectionName] = WorkDays }; DatabaseSchemaVersion = EnsureDatabaseSchemaVersion(); } public Collection System { get; } public Collection AppSettings { get; } public Collection Users { get; } public Collection WorkDays { get; } public int DatabaseSchemaVersion { get; } public IReadOnlyDictionary BackupCollections => backupCollections; public void ExecuteInBatch(Action action) { ArgumentNullException.ThrowIfNull(action); database.InBatch(action); } public void Dispose() { database.Close(); database.Dispose(); } private static string ResolveDirectory(string configuredDirectory, string contentRootPath) { if (string.IsNullOrWhiteSpace(configuredDirectory)) { return Path.Combine(contentRootPath, "App_Data", "couchbase"); } return Path.IsPathRooted(configuredDirectory) ? configuredDirectory : Path.GetFullPath(Path.Combine(contentRootPath, configuredDirectory)); } private int EnsureDatabaseSchemaVersion() { var document = System.GetDocument(SchemaVersionDocumentId); if (document is not null && document.Contains("schemaVersion")) { return document.GetInt("schemaVersion"); } var now = DateTimeOffset.UtcNow; var mutableDocument = new MutableDocument(SchemaVersionDocumentId); mutableDocument.SetInt("schemaVersion", CurrentDatabaseSchemaVersion); mutableDocument.SetString("createdAtUtc", now.ToString("O")); mutableDocument.SetString("updatedAtUtc", now.ToString("O")); System.Save(mutableDocument); return CurrentDatabaseSchemaVersion; } }