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; //The methods of the Db class are located in multiple files (Create.cs, Read,cs, Delete.cs, Update.cs) //That's why the class definition is partial public static partial class Db { private static SQLiteConnection Connection { get; } = InitConnection(); 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 TableQuery UserActions => 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 //When a class is loaded, the fields are initialized before the constructor's code is executed. //The TableQuery fields are lazy meaning that they will be initialized when they get accessed //The connection searches for the latest backup and binds all the tables to it. } //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(); Connection.CreateTable(); }); //UpdateKeys(); CleanupSessions().SupressAwaitWarning(); DeleteSnapshots().SupressAwaitWarning(); } private static SQLiteConnection InitConnection() { var latestDb = new DirectoryInfo("DbBackups") .GetFiles() .OrderBy(f => f.LastWriteTime) .Last().Name; Console.WriteLine("latestdb is "+latestDb); //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(); fileConnection.CreateTable(); return fileConnection; //return CopyDbToMemory(fileConnection); } public static void BackupDatabase() { var filename = "db-" + DateTimeOffset.UtcNow.ToUnixTimeSeconds() + ".sqlite"; Connection.Backup("DbBackups/" + filename); } //Delete all except 10 snapshots every 24 hours. private static async Task DeleteSnapshots() { while (true) { try { var files = new DirectoryInfo("DbBackups") .GetFiles() .OrderByDescending(f => f.LastWriteTime); var filesToDelete = files.Skip(10); foreach (var file in filesToDelete) { Console.WriteLine("File to delete is " + file.Name); file.Delete(); } } catch(Exception e) { Console.WriteLine("An error has occured when cleaning database snapshots, exception is:\n"+e); } await Task.Delay(TimeSpan.FromHours(24)); } } //Delete all expired sessions every half an hour. An expired session is a session remained for more than 1 day. private static async Task CleanupSessions() { while (true) { try { var deadline = DateTime.Now.AddDays(-Session.MaxAge.Days); foreach (var session in Sessions) { if (session.LastSeen < deadline) { Console.WriteLine("Need to remove session of user id " + session.User.Name + "last time is "+session.LastSeen); } } Sessions.Delete(s => s.LastSeen < deadline); } 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 RemoveNonExistingKeys() { while (true) { try { var validReadKeys = Installations .Select(i => i.S3Key) .Distinct() .ToList(); var validWriteKeys = Installations .Select(i => i.S3WriteKey) .Distinct() .ToList(); Console.WriteLine("VALID READ KEYS"); for (int i = 0; i < validReadKeys.Count; i++) { Console.WriteLine(validReadKeys[i]); } Console.WriteLine("VALID WRITE KEYS"); for (int i = 0; i < validReadKeys.Count; i++) { Console.WriteLine(validWriteKeys[i]); } const String provider = "exo.io"; var S3keys = await ExoCmd.GetAccessKeys(); foreach (var keyMetadata in S3keys) { if (keyMetadata["key"].ToString()!="EXOa0b53cf10517307cec1bf00e" && !validReadKeys.Contains(keyMetadata["key"].ToString()) && !validWriteKeys.Contains(keyMetadata["key"].ToString())) { //await ExoCmd.RevokeReadKey(keyMetadata["key"].ToString()); Console.WriteLine("Deleted key "+keyMetadata["key"]); } } } 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 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 RemoveNonExistingKeys(); 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 async Task UpdateS3Urls() { var regions = Installations .Select(i => i.S3Region) .Distinct() .ToList(); const String provider = "exo.io"; Console.WriteLine("-----------------------UPDATED READ KEYS-------------------------------------------------------------------"); 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); } }