Przeglądaj źródła

Improved updating system.

Kenric Nugteren 2 tygodni temu
rodzic
commit
f14ffbb775

+ 31 - 19
prs.server/Forms/Configuration.xaml.cs

@@ -86,7 +86,7 @@ public partial class Configuration : ThemableWindow
             Close();
             return;
         }
-        else if (CheckForVersion9Update())
+        else if (CheckForMajorVersionUpdate(9))
         {
             Close();
             return;
@@ -99,20 +99,20 @@ public partial class Configuration : ThemableWindow
         Servers.Refresh(true, true);
     }
 
-    #region Version 9 Update
+    #region Major Version Update
 
-    private bool CheckForVersion9Update()
+    private bool CheckForMajorVersionUpdate(int targetMajorVersion)
     {
-        var location = $"{_autoUpdateSettings.Location}/v9";
+        var location = $"{_autoUpdateSettings.Location}/v{targetMajorVersion}";
         var result = Update.GetRemoteFile($"{GetChannelLocation(location, allowAlpha: true)}/version.txt");
-        if(result.StatusCode == System.Net.HttpStatusCode.NotFound)
+        if(result.StatusCode != System.Net.HttpStatusCode.OK)
         {
             return false;
         }
 
         var res = MessageWindow.New()
-            .Title("PRS Version 9")
-            .Message("PRS Version 9 is available!")
+            .Title($"PRS Version {targetMajorVersion}")
+            .Message($"PRS Version {targetMajorVersion} is available!")
             .AddOKButton("Proceed")
             .AddCancelButton()
             .Display()
@@ -122,27 +122,40 @@ public partial class Configuration : ThemableWindow
             return false;
         }
 
-        var updateDBWindow = new UpdateDatabaseFiles();
-        if(updateDBWindow.ShowDialog() != true)
+        var installerNameResult = Progress.ShowModal<string?>("Retrieving Update", progress =>
         {
+            return Result.Ok(Update.DownloadInstaller(
+                location =>
+                {
+                    var result = Update.GetRemoteFile($"{GetChannelLocation(location, allowAlpha: true)}/PRSSetup.exe");
+                    if(result.StatusCode == System.Net.HttpStatusCode.OK)
+                    {
+                        return result.RawBytes;
+                    }
+                    else
+                    {
+                        return null;
+                    }
+                },
+                location,
+                "PRSSetup.exe"));
+        });
+        if(!installerNameResult.GetOk(out var installerName) || installerName is null)
+        {
+            MessageWindow.ShowMessage($"Could not retrieve installer; cancelling update to version {targetMajorVersion}.", "Download failed.");
             return false;
         }
+        Servers.BeforeUpdate();
 
-        if(!MessageWindow.ShowYesNo("Proceed with update to version 9?", "Update?"))
+        if (!UpdateDatabaseFiles.UpdateDatabases(targetMajorVersion))
         {
             return false;
         }
 
         bool? bOK = null;
-        Progress.ShowModal("Retrieving Update", progress =>
+        Progress.ShowModal("Launching Update", progress =>
         {
-            bOK = Update.DownloadAndRunInstaller(
-                location => Update.GetRemoteFile($"{GetChannelLocation(location, allowAlpha: true)}/PRSSetup.exe").RawBytes,
-                BeforeUpdate,
-                true,
-                location,
-                "PRSSetup.exe",
-                progress);
+            bOK = Update.RunInstaller(installerName, BeforeUpdate, true, progress);
         });
         if (bOK is null || bOK == true)
         {
@@ -151,7 +164,6 @@ public partial class Configuration : ThemableWindow
         }
         else
         {
-            MessageWindow.ShowMessage("Unable to retrieve installer!", "Error");
             return false;
         }
     }

+ 0 - 51
prs.server/Forms/Version9Update/UpdateConsole.cs

@@ -1,51 +0,0 @@
-using InABox.Core;
-using InABox.Logging;
-using InABox.Wpf.Console;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Console = InABox.Wpf.Console.Console;
-
-namespace PRSServer.Forms.Version9Update;
-
-public class UpdateConsole : Console
-{
-    private EventLogger? logger;
-
-    public UpdateConsole(string description): base(description)
-    {
-    }
-
-    protected override void OnLoaded()
-    {
-        base.OnLoaded();
-
-        ConsoleControl.Enabled = true;
-
-        logger = new EventLogger(OnLog);
-        MainLogger.AddLogger(logger);
-    }
-
-    private void OnLog(string message)
-    {
-        Dispatcher.BeginInvoke(() =>
-        {
-            ConsoleControl.LoadLogEntry(message);
-        });
-    }
-
-    protected override void OnClosing()
-    {
-        base.OnClosing();
-        if(logger is not null)
-        {
-            MainLogger.RemoveLogger(logger);
-        }
-    }
-
-    protected override string GetLogDirectory()
-    {
-        return CoreUtils.GetPath();
-    }
-}

+ 108 - 0
prs.server/Forms/Version9Update/UpdateDatabaseFiles.cs

@@ -0,0 +1,108 @@
+using Comal.Stores;
+using InABox.Configuration;
+using InABox.Core;
+using InABox.Database;
+using InABox.Database.SQLite;
+using InABox.Logging;
+using InABox.Wpf;
+using InABox.WPF;
+using PRSServices;
+using System;
+using System.Collections.Generic;
+using System.Data.Common;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PRSServer.Forms.Version9Update;
+
+public static class UpdateDatabaseFiles
+{
+    public static bool UpdateDatabases(int targetMajorVersion)
+    {
+        try
+        {
+            Progress.ShowModal("Loading Data", progress =>
+            {
+                var sections = PRSService.GetConfiguration().LoadAll();
+
+                var stores = CoreUtils.TypeList(
+                    AppDomain.CurrentDomain.GetAssemblies(),
+                    myType =>
+                        myType.IsClass
+                        && !myType.IsAbstract
+                        && !myType.IsGenericType
+                        && myType.GetInterfaces().Contains(typeof(IStore))
+                ).ToArray();
+
+                foreach (var section in sections.Where(x => x.Value.Type == ServerType.Database))
+                {
+                    var properties = section.Value.DeserializeServerProperties();
+                    if (properties is not DatabaseServerProperties databaseProperties) continue;
+
+                    var factory = new SQLiteProviderFactory(databaseProperties.FileName);
+                    // Not starting the factory, since otherwise that will do bad things to the database if it is already version 9.
+                    // Instead, we just want to call a single SQL statement, really.
+                    var originalVersion = DbFactory.GetDatabaseVersion(factory);
+                    if (originalVersion.MajorVersion >= targetMajorVersion) continue;
+
+                    progress.Report($"Backing up database '{databaseProperties.Name}'");
+                    if (File.Exists(databaseProperties.FileName))
+                    {
+                        var directory = Path.GetDirectoryName(databaseProperties.FileName) ?? "";
+                        var originalFileName = Path.GetFileNameWithoutExtension(databaseProperties.FileName);
+
+                        var newFileName = Path.Combine(directory, $"{originalFileName}_{originalVersion}_backup.dbs");
+                        var i = 0;
+                        while (File.Exists(newFileName))
+                        {
+                            newFileName = Path.Combine(directory, $"{originalFileName}_{originalVersion}_backup_{i}.dbs");
+                            ++i;
+                        }
+
+                        File.Copy(databaseProperties.FileName, newFileName);
+                    }
+
+                    progress.Report($"Updating database '{databaseProperties.Name}' from version {originalVersion}");
+
+                    DbFactory.Stores = stores;
+                    DbFactory.DefaultStore = typeof(BaseStore<>);
+
+                    // Adding extra logger so that the SQL transactions are put in the right place.
+                    var logger = new LogFileLogger(DatabaseEngine.GetPath(section.Key), true);
+                    MainLogger.AddLogger(logger);
+
+                    factory = new SQLiteProviderFactory(databaseProperties.FileName);
+                    factory.Types = DbFactory.Entities.Where(x =>
+                        x.IsClass
+                        && !x.IsGenericType
+                        && x.IsSubclassOf(typeof(Entity))
+                    ).ToArray();
+
+                    DbFactory.ProviderFactory = factory;
+                    DbFactory.Start(); // Doing this will run all the update scripts.
+                    factory.InitializeNulls();
+
+                    var dbVersion = DbFactory.GetVersionSettings(factory);
+                    dbVersion.Version = new VersionNumber(9, 0).ToString();
+
+                    var result = factory.NewProvider(Logger.Main)
+                        .Query(Filter<GlobalSettings>.Where(x => x.Section).IsEqualTo(nameof(DatabaseVersion)))
+                        .Rows.FirstOrDefault()?.ToObject<GlobalSettings>() ?? new GlobalSettings() { Section = nameof(DatabaseVersion), Key = "" };
+                    result.OriginalValueList["Contents"] = result.Contents;
+                    result.Contents = Serialization.Serialize(dbVersion);
+                    factory.NewProvider(Logger.Main).Save(result);
+
+                    MainLogger.RemoveLogger(logger);
+                }
+            });
+            return true;
+        }
+        catch(Exception e)
+        {
+            MessageWindow.ShowError($"An error occurred while updating database files.", e);
+            return false;
+        }
+    }
+}

+ 0 - 29
prs.server/Forms/Version9Update/UpdateDatabaseFiles.xaml

@@ -1,29 +0,0 @@
-<Window x:Class="PRSServer.Forms.Version9Update.UpdateDatabaseFiles"
-        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
-        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-        xmlns:local="clr-namespace:PRSServer.Forms.Version9Update"
-        mc:Ignorable="d"
-        Title="Update Database to Version 9" Height="500" Width="350"
-        Loaded="Window_Loaded">
-    <Grid>
-        <Grid.RowDefinitions>
-            <RowDefinition Height="*"/>
-            <RowDefinition Height="Auto"/>
-        </Grid.RowDefinitions>
-        <local:UpdateDatabaseFilesGrid x:Name="Grid" Margin="5"
-                                       Grid.Row="0"/>
-        <DockPanel Grid.Row="1" LastChildFill="False"
-                   Margin="5,0,5,5">
-            <Button Content="Cancel"
-                    DockPanel.Dock="Right"
-                    Padding="5"
-                    Click="Cancel_Click"/>
-            <Button Content="Proceed"
-                    DockPanel.Dock="Right"
-                    Padding="5" Margin="0,0,5,0"
-                    Click="Proceed_Click"/>
-        </DockPanel>
-    </Grid>
-</Window>

+ 0 - 246
prs.server/Forms/Version9Update/UpdateDatabaseFiles.xaml.cs

@@ -1,246 +0,0 @@
-using Comal.Stores;
-using InABox.Clients;
-using InABox.Core;
-using InABox.Database;
-using InABox.Database.SQLite;
-using InABox.DynamicGrid;
-using InABox.Wpf;
-using InABox.WPF;
-using PRSServices;
-using Stripe.Treasury;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Data;
-using System.Windows.Documents;
-using System.Windows.Input;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using System.Windows.Shapes;
-
-namespace PRSServer.Forms.Version9Update;
-
-public class UpdateDatabaseFilesGridItem : BaseObject
-{
-    public string Name { get; set; }
-
-    public string FileName { get; set; }
-
-    public IProviderFactory ProviderFactory { get; set; }
-
-    public VersionNumber Version { get; set; }
-
-    public string VersionText => Version?.ToString() ?? "";
-
-    public bool NeedsUpdate => Version is not null && Version.MajorVersion < 9;
-}
-
-public class UpdateDatabaseFilesGrid : DynamicItemsListGrid<UpdateDatabaseFilesGridItem>
-{
-    private static readonly BitmapImage? _warning = Properties.Resources.warning.AsBitmapImage();
-    private static readonly BitmapImage? _tick = Properties.Resources.tick.AsBitmapImage();
-
-    protected override void Init()
-    {
-        base.Init();
-
-        ActionColumns.Add(new DynamicImageColumn(GetImage, UpdateClick)
-        {
-            ToolTip = UpdateToolTip
-        });
-    }
-
-    private FrameworkElement? UpdateToolTip(DynamicActionColumn column, CoreRow? row)
-    {
-        if(row is null)
-        {
-            return null;
-        }
-
-        var item = LoadItem(row);
-        if (item.NeedsUpdate)
-        {
-            return column.TextToolTip($"This database is currently version {item.VersionText} and needs updating to version 9.");
-        }
-        else
-        {
-            return null;
-        }
-    }
-
-    protected override DynamicGridColumns LoadColumns()
-    {
-        var columns = new DynamicGridColumns();
-        columns.Add<UpdateDatabaseFilesGridItem>(x => x.Name);
-        columns.Add<UpdateDatabaseFilesGridItem>(x => x.VersionText, caption: "Current Version");
-        return columns;
-    }
-
-    protected override void DoReconfigure(DynamicGridOptions options)
-    {
-        options.Clear();
-    }
-
-    private bool UpdateClick(CoreRow? row)
-    {
-        if (row is null) return false;
-
-        var item = LoadItem(row);
-        if (!item.NeedsUpdate) return false;
-
-        var currentVersion = DbFactory.GetDatabaseVersion(item.ProviderFactory);
-        if(currentVersion.MajorVersion >= 9)
-        {
-            item.Version = currentVersion;
-            return true;
-        }
-
-        var task = Task.Run(() =>
-        {
-            DbFactory.Stores = CoreUtils.TypeList(
-                AppDomain.CurrentDomain.GetAssemblies(),
-                myType =>
-                    myType.IsClass
-                    && !myType.IsAbstract
-                    && !myType.IsGenericType
-                    && myType.GetInterfaces().Contains(typeof(IStore))
-            ).ToArray();
-            DbFactory.DefaultStore = typeof(BaseStore<>);
-
-            var factory = new SQLiteProviderFactory(item.FileName);
-            DbFactory.ProviderFactory = factory;
-            DbFactory.Start(); // Doing this will run all the update scripts.
-            factory.InitializeNulls();
-        });
-
-        var window = Window.GetWindow(this);
-
-        var console = new UpdateConsole("Console");
-
-        console.Top = window.Top;
-        console.Left = window.Left + window.Width + 2;
-        console.Height = window.Height;
-        console.Loaded += (_, args) =>
-        {
-            var worker = new BackgroundWorker();
-
-            window.IsEnabled = false;
-
-            worker.DoWork += (o, e) =>
-            {
-                task.Wait();
-            };
-            worker.RunWorkerCompleted +=
-                (o, e) => console.Close();
-            worker.RunWorkerAsync();
-        };
-        console.ShowDialog();
-
-        try
-        {
-            task.Wait();
-        }
-        catch(Exception e)
-        {
-            MessageWindow.ShowError("An error occurred while trying to update this database.", e);
-        }
-
-        window.IsEnabled = true;
-
-        currentVersion = DbFactory.GetDatabaseVersion(item.ProviderFactory);
-        item.Version = currentVersion;
-        return true;
-    }
-
-    private BitmapImage? GetImage(CoreRow? row)
-    {
-        if (row is null) return _tick;
-
-        return LoadItem(row).NeedsUpdate ? _warning : _tick;
-    }
-}
-
-/// <summary>
-/// Interaction logic for UpdateDatabaseFiles.xaml
-/// </summary>
-public partial class UpdateDatabaseFiles : Window
-{
-    public UpdateDatabaseFiles()
-    {
-        InitializeComponent();
-    }
-
-    private void Window_Loaded(object sender, RoutedEventArgs e)
-    {
-        LoadData();
-    }
-
-    private void LoadData()
-    {
-        Progress.ShowModal("Loading Data", progress =>
-        {
-            var table = new CoreTable();
-            table.LoadColumns(typeof(Server));
-
-            var sections = PRSService.GetConfiguration().LoadAll();
-
-            foreach (var section in sections.Where(x => x.Value.Type == ServerType.Database))
-            {
-                var properties = section.Value.DeserializeServerProperties();
-                if (properties is not DatabaseServerProperties databaseProperties) continue;
-
-                Grid.Items.Add(new()
-                {
-                    Name = databaseProperties.Name,
-                    FileName = databaseProperties.FileName,
-                });
-            }
-
-            var tasks = Grid.Items.ToArray(LoadProvider);
-            Task.WaitAll(tasks);
-
-            foreach(var (item, task) in Grid.Items.Zip(tasks))
-            {
-                item.ProviderFactory = task.Result;
-                item.Version = DbFactory.GetDatabaseVersion(item.ProviderFactory);
-            }
-        });
-
-        Grid.Refresh(true, true);
-    }
-
-    private Task<IProviderFactory> LoadProvider(UpdateDatabaseFilesGridItem item)
-    {
-        return Task.Run(() =>
-        {
-            IProviderFactory factory = new SQLiteProviderFactory(item.FileName);
-            factory.Start();
-            return factory;
-        });
-    }
-
-    private void Proceed_Click(object sender, RoutedEventArgs e)
-    {
-        if(Grid.Items.Any(x => x.NeedsUpdate))
-        {
-            if(!MessageWindow.ShowYesNo("Some database files have not been updated to version 9! If you proceed, these " +
-                "databases will not work anymore. Are you sure you wish to proceed?", "Unupdated files"))
-            {
-                return;
-            }
-        }
-
-        DialogResult = true;
-    }
-
-    private void Cancel_Click(object sender, RoutedEventArgs e)
-    {
-        DialogResult = false;
-    }
-}

+ 39 - 20
prs.shared/Update.cs

@@ -137,10 +137,8 @@ namespace PRS.Shared
             return false;
         }
 
-        public static bool DownloadAndRunInstaller(Func<string, byte[]?> getInstaller, Action? beforeUpdate, bool elevated, string location,
-            string tempName, IProgress<string> progress)
+        public static string? DownloadInstaller(Func<string, byte[]?> getInstaller, string location, string tempName)
         {
-            bool bOK = false;
             var tempinstall = Path.Combine(CoreUtils.GetPath(), tempName);
             try
             {
@@ -148,31 +146,52 @@ namespace PRS.Shared
                 if (installer?.Any() == true)
                 {
                     File.WriteAllBytes(tempinstall, installer);
-
-                    var scriptFile = Path.Combine(CoreUtils.GetPath(), "install.bat");
-                    File.WriteAllText(scriptFile, GenerateInstallerScript(tempinstall));
-
-                    bOK = true;
-
-                    beforeUpdate?.Invoke();
-                    progress.Report("Launching Installer");
-                    /*var startInfo = new ProcessStartInfo(tempinstall,
-                                " /SILENT /SUPPRESSMSGBOXES /FORCECLOSEAPPLICATIONS /RESTARTAPPLICATIONS");*/
-                    var startInfo = new ProcessStartInfo(scriptFile);
-                    startInfo.Verb = elevated ? "runas" : "open";
-                    startInfo.UseShellExecute = true;
-                    startInfo.WindowStyle = ProcessWindowStyle.Hidden;
-                    var p = Process.Start(startInfo);
-                    p.WaitForExit();
+                    return tempinstall;
                 }
             }
             catch (Exception e)
             {
                 Logger.Send(LogType.Error, "", CoreUtils.FormatException(e));
-                MessageBox.Show("Error during installation!");
+                MessageBox.Show("Error downloading installer!");
             }
 
+            return null;
+        }
+
+        public static bool RunInstaller(string fileName, Action? beforeUpdate, bool elevated, IProgress<string> progress)
+        {
+            bool bOK = false;
+            try
+            {
+                var scriptFile = Path.Combine(CoreUtils.GetPath(), "install.bat");
+                File.WriteAllText(scriptFile, GenerateInstallerScript(fileName));
+
+                bOK = true;
+
+                beforeUpdate?.Invoke();
+                progress.Report("Launching Installer");
+                /*var startInfo = new ProcessStartInfo(tempinstall,
+                            " /SILENT /SUPPRESSMSGBOXES /FORCECLOSEAPPLICATIONS /RESTARTAPPLICATIONS");*/
+                var startInfo = new ProcessStartInfo(scriptFile);
+                startInfo.Verb = elevated ? "runas" : "open";
+                startInfo.UseShellExecute = true;
+                startInfo.WindowStyle = ProcessWindowStyle.Hidden;
+                var p = Process.Start(startInfo);
+                p.WaitForExit();
+            }
+            catch (Exception e)
+            {
+                Logger.Send(LogType.Error, "", CoreUtils.FormatException(e));
+                MessageBox.Show("Error during installation!");
+            }
             return bOK;
         }
+
+        public static bool DownloadAndRunInstaller(Func<string, byte[]?> getInstaller, Action? beforeUpdate, bool elevated, string location,
+            string tempName, IProgress<string> progress)
+        {
+            if (DownloadInstaller(getInstaller, location, tempName) is not string installerName) return false;
+            return RunInstaller(installerName, beforeUpdate, elevated, progress);
+        }
     }
 }