using System.Reactive.Concurrency; using System.Reactive.Linq; using InnovEnergy.App.Backend.DataTypes; using InnovEnergy.App.Backend.DataTypes.Methods; using InnovEnergy.App.Backend.Relations; using InnovEnergy.Lib.S3Utils; using InnovEnergy.Lib.S3Utils.DataTypes; using InnovEnergy.Lib.Utils; using SQLite; using SQLiteConnection = SQLite.SQLiteConnection; namespace InnovEnergy.App.Backend.Database; public static partial class Db { private static SQLiteConnection Connection { get; } = InitConnection(); private static SQLiteConnection InitConnection() { var latestDb = new DirectoryInfo("DbBackups") .GetFiles() .OrderBy(f => f.LastWriteTime) .Last().Name; //This is the file connection from the DbBackups folder var fileConnection = new SQLiteConnection("DbBackups/" + latestDb); //Create a table if it does not exist fileConnection.CreateTable(); fileConnection.CreateTable(); fileConnection.CreateTable(); fileConnection.CreateTable(); fileConnection.CreateTable(); fileConnection.CreateTable(); fileConnection.CreateTable(); fileConnection.CreateTable(); fileConnection.CreateTable(); return CopyDbToMemory(fileConnection); } private static SQLiteConnection CopyDbToMemory(SQLiteConnection fileConnection) { var memoryConnection = new SQLiteConnection(":memory:"); //Create a table if it does not exist in main memory memoryConnection.CreateTable(); memoryConnection.CreateTable(); memoryConnection.CreateTable(); memoryConnection.CreateTable(); memoryConnection.CreateTable(); memoryConnection.CreateTable(); memoryConnection.CreateTable(); memoryConnection.CreateTable(); memoryConnection.CreateTable(); //Copy all the existing tables from the disk to main memory fileConnection.Table().ForEach(memoryConnection.Insert); fileConnection.Table().ForEach(memoryConnection.Insert); fileConnection.Table().ForEach(memoryConnection.Insert); fileConnection.Table().ForEach(memoryConnection.Insert); fileConnection.Table().ForEach(memoryConnection.Insert); fileConnection.Table().ForEach(memoryConnection.Insert); fileConnection.Table().ForEach(memoryConnection.Insert); fileConnection.Table().ForEach(memoryConnection.Insert); fileConnection.Table().ForEach(memoryConnection.Insert); return memoryConnection; } public static void BackupDatabase() { var filename = "db-" + DateTimeOffset.UtcNow.ToUnixTimeSeconds() + ".sqlite"; Connection.Backup("DbBackups/" + filename); } public static TableQuery Sessions => Connection.Table(); public static TableQuery Folders => Connection.Table(); public static TableQuery Installations => Connection.Table(); public static TableQuery Users => Connection.Table(); public static TableQuery FolderAccess => Connection.Table(); public static TableQuery InstallationAccess => Connection.Table(); public static TableQuery OrderNumber2Installation => Connection.Table(); public static TableQuery Errors => Connection.Table(); public static TableQuery Warnings => Connection.Table(); public static void Init() { // used to force static constructor //Since this class is static, we call Init method from the Program.cs to initialize all the fields of the class } //This is the constructor of the class static Db() { Connection.RunInTransaction(() => { Connection.CreateTable(); Connection.CreateTable(); Connection.CreateTable(); Connection.CreateTable(); Connection.CreateTable(); Connection.CreateTable(); Connection.CreateTable(); Connection.CreateTable(); Connection.CreateTable(); }); UpdateKeys().SupressAwaitWarning(); CleanupSessions().SupressAwaitWarning(); } private static async Task CleanupSessions() { while (true) { try { DeleteStaleSessions(); } catch(Exception e) { Console.WriteLine("An error has occured when cleaning stale sessions, exception is:\n"+e); } await Task.Delay(TimeSpan.FromHours(0.5)); } } private static async Task UpdateKeys() { while (true) { try { await UpdateS3Urls(); } catch(Exception e) { Console.WriteLine("An error has occured when updating S3 keys, exception is:\n"+e); } await Task.Delay(TimeSpan.FromHours(24)); } } private static Boolean RunTransaction(Func func) { var savepoint = Connection.SaveTransactionPoint(); var success = false; try { success = func(); } finally { if (success) Connection.Release(savepoint); else Connection.RollbackTo(savepoint); } return success; } private static void DeleteStaleSessions() { var deadline = DateTime.Now.AddDays((-1) * Session.MaxAge.Days); Sessions.Delete(s => s.LastSeen < deadline); } private static async Task UpdateS3Urls() { var regions = Installations .Select(i => i.S3Region) .Distinct() .ToList(); const String provider = "exo.io"; foreach (var region in regions) { var s3Region = new S3Region($"https://{region}.{provider}", ExoCmd.S3Credentials!); var bucketList = await s3Region.ListAllBuckets(); var installations = from bucket in bucketList.Buckets from installation in Installations where installation.BucketName() == bucket.BucketName select installation; foreach (var installation in installations) { await installation.RenewS3Credentials(); } } } public static async Task SendPasswordResetEmail(User user, String sessionToken) { try { await user.SendPasswordResetEmail(sessionToken); return true; } catch { return false; } } public static async Task SendNewUserEmail(User user) { try { await user.SendNewUserWelcomeMessage(); return true; } catch { return false; } } public static Boolean DeleteUserPassword(User user) { user.Password = ""; user.MustResetPassword = true; return Update(user); } }