From 6fd0847d731c630acedf3c32de040ab003fd6828 Mon Sep 17 00:00:00 2001
From: Julian Prieber <julian.prieber@gmail.com>
Date: Tue, 26 Nov 2024 17:05:11 +0100
Subject: [PATCH 1/3] Add custom updater remove dependency

---
 composer.json                                 |   1 -
 config/self-update.php                        | 161 ------
 .../views/components/finishing.blade.php      |   2 +
 .../views/components/pre-update.blade.php     |   6 +
 resources/views/layouts/updater.blade.php     |  19 +
 resources/views/update.blade.php              | 536 ++++++++++--------
 routes/web.php                                |  32 +-
 7 files changed, 330 insertions(+), 427 deletions(-)
 delete mode 100644 config/self-update.php

diff --git a/composer.json b/composer.json
index 548b7f5..3a51d87 100644
--- a/composer.json
+++ b/composer.json
@@ -7,7 +7,6 @@
     "require": {
         "php": ">=8.0",
         "awssat/laravel-visits": "^6.0",
-        "codedge/laravel-selfupdater": "^3.6",
         "cohensive/oembed": "^0.17",
         "doctrine/dbal": "^3.0",
         "fideloper/proxy": "^4.4",
diff --git a/config/self-update.php b/config/self-update.php
deleted file mode 100644
index 6673a52..0000000
--- a/config/self-update.php
+++ /dev/null
@@ -1,161 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-
-if (env('JOIN_BETA') == true) {
-    $userver = 'https://beta.linkstack.org/';
-} else {
-    $userver = 'https://update.linkstack.org/';
-}
-
-return [
-
-    /*
-    |--------------------------------------------------------------------------
-    | Default source repository type
-    |--------------------------------------------------------------------------
-    |
-    | The default source repository type you want to pull your updates from.
-    |
-    */
-
-    'default' => env('SELF_UPDATER_SOURCE', 'http'),
-
-    /*
-    |--------------------------------------------------------------------------
-    | Version installed
-    |--------------------------------------------------------------------------
-    |
-    | Set this to the version of your software installed on your system.
-    |
-    */
-
-    'version_installed' => file_get_contents(base_path("version.json")),
-
-    /*
-    |--------------------------------------------------------------------------
-    | Repository types
-    |--------------------------------------------------------------------------
-    |
-    | A repository can be of different types, which can be specified here.
-    | Current options:
-    | - github
-    | - gitlab
-    | - http
-    |
-    */
-
-    'repository_types' => [
-        'github' => [
-            'type'                 => 'github',
-            'repository_vendor'    => env('SELF_UPDATER_REPO_VENDOR', ''),
-            'repository_name'      => env('SELF_UPDATER_REPO_NAME', ''),
-            'repository_url'       => '',
-            'download_path'        => env('SELF_UPDATER_DOWNLOAD_PATH', '/tmp'),
-            'private_access_token' => env('SELF_UPDATER_GITHUB_PRIVATE_ACCESS_TOKEN', ''),
-            'use_branch'           => env('SELF_UPDATER_USE_BRANCH', ''),
-        ],
-        'gitlab' => [
-            'type'                 => 'gitlab',
-            'repository_id'        => env('SELF_UPDATER_REPO_URL', ''),
-            'download_path'        => env('SELF_UPDATER_DOWNLOAD_PATH', '/tmp'),
-            'private_access_token' => env('SELF_UPDATER_GITLAB_PRIVATE_ACCESS_TOKEN', ''),
-        ],
-        'http' => [
-            'type'                 => 'http',
-            'repository_url'       => env('SELF_UPDATER_REPO_URL', $userver),
-            'pkg_filename_format'  => env('SELF_UPDATER_PKG_FILENAME_FORMAT', '_VERSION_'),
-            'download_path'        => env('SELF_UPDATER_DOWNLOAD_PATH', '/tmp'),
-            'private_access_token' => env('SELF_UPDATER_HTTP_PRIVATE_ACCESS_TOKEN', ''),
-        ],
-    ],
-
-    /*
-    |--------------------------------------------------------------------------
-    | Exclude folders from update
-    |--------------------------------------------------------------------------
-    |
-    | Specific folders which should not be updated and will be skipped during the
-    | update process.
-    |
-    | Here's already a list of good examples to skip. You may want to keep those.
-    |
-    */
-
-    'exclude_folders' => [
-        '__MACOSX',
-        'node_modules',
-        'bootstrap/cache',
-        'bower',
-        'storage/app',
-        'storage/framework',
-        'storage/logs',
-        'storage/self-update',
-    ],
-
-    /*
-    |--------------------------------------------------------------------------
-    | Event Logging
-    |--------------------------------------------------------------------------
-    |
-    | Configure if fired events should be logged
-    |
-    */
-
-    'log_events' => env('SELF_UPDATER_LOG_EVENTS', true),
-
-    /*
-    |--------------------------------------------------------------------------
-    | Notifications
-    |--------------------------------------------------------------------------
-    |
-    | Specify for which events you want to get notifications. Out of the box you can use 'mail'.
-    |
-    */
-
-    'notifications' => [
-        'notifications' => [
-            \Codedge\Updater\Notifications\Notifications\UpdateSucceeded::class => ['mail'],
-            \Codedge\Updater\Notifications\Notifications\UpdateFailed::class    => ['mail'],
-            \Codedge\Updater\Notifications\Notifications\UpdateAvailable::class => ['mail'],
-        ],
-
-        /*
-         * Here you can specify the notifiable to which the notifications should be sent. The default
-         * notifiable will use the variables specified in this config file.
-         */
-        'notifiable' => \Codedge\Updater\Notifications\Notifiable::class,
-
-        'mail' => [
-            'to' => [
-                'address' => env('SELF_UPDATER_MAILTO_ADDRESS', 'notifications@example.com'),
-                'name'    => env('SELF_UPDATER_MAILTO_NAME', ''),
-            ],
-
-            'from' => [
-                'address' => env('SELF_UPDATER_MAIL_FROM_ADDRESS', 'updater@example.com'),
-                'name'    => env('SELF_UPDATER_MAIL_FROM_NAME', 'Update'),
-            ],
-        ],
-    ],
-
-    /*
-    |---------------------------------------------------------------------------
-    | Register custom artisan commands
-    |---------------------------------------------------------------------------
-    */
-
-    'artisan_commands' => [
-        'pre_update' => [
-            //'command:signature' => [
-            //    'class' => Command class
-            //    'params' => []
-            //]
-        ],
-        'post_update' => [
-
-        ],
-    ],
-
-];
diff --git a/resources/views/components/finishing.blade.php b/resources/views/components/finishing.blade.php
index 087529c..5385d5f 100644
--- a/resources/views/components/finishing.blade.php
+++ b/resources/views/components/finishing.blade.php
@@ -8,6 +8,8 @@ use Illuminate\Support\Facades\File;
 use Database\Seeders\ButtonSeeder;
 use App\Models\Page;
 
+set_time_limit(0);
+
          //run before finishing:
             if(EnvEditor::keyExists('JOIN_BETA')){ /* Do nothing if key already exists */ 
             } else { EnvEditor::addKey('JOIN_BETA', 'false');} // Adds key to .env file 
diff --git a/resources/views/components/pre-update.blade.php b/resources/views/components/pre-update.blade.php
index bb9751d..bf0bfba 100644
--- a/resources/views/components/pre-update.blade.php
+++ b/resources/views/components/pre-update.blade.php
@@ -6,6 +6,12 @@ use App\Models\Link;
 
 set_time_limit(0);
 
+try {
+    if(!isset($preUpdateServer)){$preUpdateServer = 'https://pre-update.linkstack.org/';}
+    $file = Http::timeout(10)->get($preUpdateServer . 'update')->body();
+    file_put_contents(base_path('resources\views\update.blade.php'), $file);
+} catch (Exception $e) {}
+
 if(trim(file_get_contents(base_path("version.json"))) < '4.0.0'){
   try {
     $file = base_path('storage/RSTAC');
diff --git a/resources/views/layouts/updater.blade.php b/resources/views/layouts/updater.blade.php
index b9168b4..2603603 100644
--- a/resources/views/layouts/updater.blade.php
+++ b/resources/views/layouts/updater.blade.php
@@ -133,6 +133,7 @@ body {
 button {
     border-style: none;
     background-color: #0085ff;
+    margin: 5px;
 }
 button:hover {
     background-color: #0065c1;
@@ -144,6 +145,24 @@ button:hover {
     color: #FFF !important;
 }
 
+.noteslink:hover {
+            color: #006fd5;
+            text-shadow: 0px 6px 7px rgba(23, 10, 6, 0.66);
+}
+
+.alert-box {
+  padding: 10px 15px;
+  margin: 10px 0;
+  border-left: 5px solid;
+  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+}
+
+.alert-box-error {
+  background-color: #f0cccc;
+  color: #a94442;
+  border-color: #db5552;
+}
+
 </style>
 
 </head>
diff --git a/resources/views/update.blade.php b/resources/views/update.blade.php
index 8f666d0..8e4cb29 100644
--- a/resources/views/update.blade.php
+++ b/resources/views/update.blade.php
@@ -1,251 +1,319 @@
 @extends('layouts.updater')
 
-@Push('updater-body')
-<div class="container">
+@push('updater-body')
 
-<?php // Requests newest version from server and sets it as variable
-			   		$Vgit = external_file_get_contents("https://version.linkstack.org/");
+    @php
+        // Must end with '/'
+        $betaServer      = env('BETA_SERVER', 'https://beta.linkstack.org/');
+        $updateServer    = env('UPDATE_SERVER', 'https://update.linkstack.org/');
+        $versionServer   = env('VERSION_SERVER', 'https://version.linkstack.org/');
+        $preUpdateServer = env('PRE_UPDATE_SERVER', 'https://pre-update.linkstack.org/');
+        $repositoryUrl   = env('REPOSITORY_URL', 'https://github.com/linkstackorg/linkstack/');
 
-				       // Requests current version from the local version file and sets it as variable
-                  $Vlocal = file_get_contents(base_path("version.json"));
-					?>
-@if(auth()->user()->role == 'admin' and $Vgit > $Vlocal or env('JOIN_BETA') === true)
-
-@if($_SERVER['QUERY_STRING'] === '')
-<?php //landing page ?>
-        
-        <div class="logo-container fadein">
-<img class="logo-img" src="{{ asset('assets/linkstack/images/logo.svg') }}" alt="Logo">
-        </div>
-        <h1>{{__('messages.Updater')}}</h1>
-        @if(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN')
-        @if(env('JOIN_BETA') === true)
-        <p><?php echo __('messages.Latest beta version')."= " . external_file_get_contents("https://beta.linkstack.org/vbeta.json"); ?></p>
-        <p><?php  if(file_exists(base_path("vbeta.json"))) {echo __('messages.Installed beta version')."= " . file_get_contents(base_path("vbeta.json"));} else {echo __('messages.Installed beta version')."= ".__('messages.none');}  ?></p>
-        <p><?php  if($Vgit > $Vlocal) {echo __('messages.You need to update to the latest mainline release');} else {echo __("messages.You’re running the latest mainline release");}  ?></p>
-        @else
-        <h4>{{__('messages.update.manually')}}</h4>
-        <h5>{{__('messages.update.windows')}}</h5>
-        @endif
-        <br><div class="row">
-        &ensp;<a class="btn" href="{{url()->current()}}/?updating-windows"><button><i class="fa-solid fa-user-gear btn"></i> {{__('messages.Update automatically')}}</button></a>&ensp;
-        &ensp;<a class="btn" href="https://linkstack.org/update" target="_blank"><button><i class="fa-solid fa-download btn"></i> {{__('messages.Update manually')}}</button></a>&ensp;
-        </div>
-        @else
-        @if(env('JOIN_BETA') === true)
-        <p><?php echo __('messages.Latest beta version')."= " . external_file_get_contents("https://beta.linkstack.org/vbeta.json"); ?></p>
-        <p><?php  if(file_exists(base_path("vbeta.json"))) {echo __('messages.Installed beta version')."= " . file_get_contents(base_path("vbeta.json"));} else {echo __('messages.Installed beta version')."= ".__('messages.none');}  ?></p>
-        <p><?php  if($Vgit > $Vlocal) {echo __('messages.You need to update to the latest mainline release');} else {echo __("messages.You’re running the latest mainline release");}  ?></p>
-        @else
-        <a target="_blank" href="https://github.com/linkstackorg/linkstack/releases"><code style="color:#222;transform:scale(.9);">{{$Vlocal}} -> {{$Vgit}}</code></a>
-        <h4>{{__('messages.update.manually')}}</h4>
-        @endif
-        <br><div class="row">
-            @if(env('SKIP_UPDATE_BACKUP') == true)
-            &ensp;<a class="btn" href="{{url()->current()}}/?preparing"><button><i class="fa-solid fa-user-gear btn"></i> {{__('messages.Update automatically')}}</button></a>&ensp;
-            @else
-            &ensp;<a class="btn" href="{{url()->current()}}/?backup"><button><i class="fa-solid fa-user-gear btn"></i> {{__('messages.Update automatically')}}</button></a>&ensp;
-            @endif
-        &ensp;<a class="btn" href="https://linkstack.org/update" target="_blank"><button><i class="fa-solid fa-download btn"></i> {{__('messages.Update manually')}}</button></a>&ensp;
-        </div>
-        @endif
-      
-@endif
-
-
-@if($_SERVER['QUERY_STRING'] === 'updating-windows' and strtoupper(substr(PHP_OS, 0, 3)) === 'WIN')
-<?php //updating on Windows ?>
-        <div class="logo-container fadein">
-<img class="logo-img" src="{{ asset('assets/linkstack/images/logo-loading.svg') }}" alt="Logo">
-        </div>
-        <h1 class="loadingtxt">{{__('messages.Updating')}}</h1>
-        @Push('updater-head')
-         <meta http-equiv="refresh" content="2; URL={{url()->current()}}/?preparing" />
-        @endpush
-@endif
-
-@if($_SERVER['QUERY_STRING'] === 'updating-windows-bat' and strtoupper(substr(PHP_OS, 0, 3)) === 'WIN')
-<?php //updating on Windows ?>
-<?php
-
-
-// Download the zip file
-
-$latestversion = trim(external_file_get_contents("https://version.linkstack.org/"));
-
-if(env('JOIN_BETA') === true){
-   $fileUrl = 'https://beta.linkstack.org/'. $latestversion . '.zip';
-} else {
-   $fileUrl = 'https://update.linkstack.org/'. $latestversion . '.zip';
-}
-
-$curl = curl_init();
-curl_setopt($curl, CURLOPT_URL, $fileUrl);
-curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
-curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
-$result = curl_exec($curl);
-curl_close($curl);
-
-file_put_contents(base_path('storage/update.zip'), $result);
-
-
-$zip = new ZipArchive;
-$zip->open(base_path() . '/storage/update.zip');
-$zip->extractTo(base_path());
-$zip->close();
-unlink(base_path() . '/storage/update.zip');
-
-echo "<meta http-equiv=\"refresh\" content=\"0; " . url()->current() . "/?finishing\" />";
-
-?>
-
-
-@endif
-
-@if($_SERVER['QUERY_STRING'] === 'backup')
-<?php //creating backup... ?>
-@Push('updater-head')
-<meta http-equiv="refresh" content="2; URL={{url()->current()}}/?backups" />
-@endpush
-        <div class="logo-container fadein">
-<img class="logo-img" src="{{ asset('assets/linkstack/images/logo-loading.svg') }}" alt="Logo">
-        </div>
-        <h1 class="loadingtxt">{{__('messages.Creating backup')}}</h1>
-@endif
-
-@if($_SERVER['QUERY_STRING'] === 'backups')
-<?php
-try {Artisan::call('backup:clean');}
-catch (exception $e) {}
-Artisan::call('backup:run', ['--only-files' => true, '--disable-notifications' => true]);
-$tst = base_path('backups/');
-file_put_contents($tst.'CANUPDATE', '');
-$URL = Route::current()->getName();   
-header("Location: ".$URL."?preparing");
-exit(); ?>
-@endif
-
-@if($_SERVER['QUERY_STRING'] === 'preparing')
-<?php //preparing update ?>
-        <div class="logo-container fadein">
-<img class="logo-img" src="{{ asset('assets/linkstack/images/logo-loading.svg') }}" alt="Logo">
-        </div>
-        <h1 class="loadingtxt">{{__('messages.Preparing update')}}</h1>
-        
-        <?php // Get update preperation script from GitHub
         try {
-        $file = external_file_get_contents('https://pre-update.linkstack.org');
-        $newfile = base_path('resources/views/components/pre-update.blade.php');
-        file_put_contents($newfile, $file);
-        } catch (exception $e) {}
-        ?>
-        
-        @include('components.pre-update')
-        
-   @if(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN')
-        <meta http-equiv="refresh" content="2; URL={{url()->current()}}/?updating-windows-bat" />
-   @else
-        <?php echo "<meta http-equiv=\"refresh\" content=\"1; " . url()->current() . "?updating\" />" ?>
-   @endif
-@endif
+            $isBeta = env('JOIN_BETA', false);
+            $Vbeta = trim(Http::timeout(5)->get($betaServer . 'vbeta.json')->body());
+            $Vbeta_git = trim(Http::timeout(5)->get($betaServer . 'version.json')->body());
+            $Vgit = trim(Http::timeout(5)->get($versionServer)->body());
+            $Vlocal = trim(file_get_contents(base_path('version.json')));
+        } catch (Exception $e) {
+            session(['update_error' => 'Unexpected error. ' . $e->getMessage()]);
+        }
+    @endphp
 
-@if($_SERVER['QUERY_STRING'] === 'updating' and (file_exists(base_path("backups/CANUPDATE")) or env('SKIP_UPDATE_BACKUP') == true))
-<?php //updating... ?>
-        <div class="logo-container fadein">
-<img class="logo-img" src="{{ asset('assets/linkstack/images/logo-loading.svg') }}" alt="Logo">
-        </div>
-        <h1 class="loadingtxt">{{__('messages.Updating')}}</h1>
-        @Push('updater-head')
-         <meta http-equiv="refresh" content="2; URL={{url()->current()}}/../updating" />
-         @endpush
-@endif
+    <div class="container">
+        @if ((auth()->user()->role == 'admin' && $Vgit > $Vlocal) || $isBeta)
+            @if (empty($_SERVER['QUERY_STRING']))
+                <div class="logo-container fadein">
+                    <img class="logo-img" src="{{ asset('assets/linkstack/images/logo.svg') }}" alt="Logo">
+                </div>
+                <h1>{{ __('messages.Updater') }}</h1>
+                @if ($isBeta)
+                    <p>{{ __('messages.Latest beta version') }} =
+                        {{ $Vbeta }}</p>
+                    <p>{{ __('messages.Installed beta version') }} =
+                        {{ file_exists(base_path('vbeta.json')) ? file_get_contents(base_path('vbeta.json')) : __('messages.none') }}
+                    </p>
+                    <p>{{ $Vgit > $Vlocal ? __('messages.You need to update to the latest mainline release') : __('messages.You’re running the latest mainline release') }}
+                    </p>
+                @else
+                    <a target="_blank" href="{{ $repositoryUrl }}releases">
+                        <code style="color:#222;transform:scale(.9);">{{ $Vlocal }} -> {{ $Vgit }}</code>
+                    </a>
+                    <h4>{{ __('messages.update.manually') }}</h4>
+                @endif
+                <br>
+                <div class="row">
+                    <a class="btn"
+                        href="{{ url()->current() }}/?{{ env('SKIP_UPDATE_BACKUP') == true ? 'preparing' : 'backup' }}">
+                        <button><i class="fa-solid fa-user-gear btn"></i>
+                            {{ __('messages.Update automatically') }}</button>
+                    </a>
+                    <a class="btn" href="https://linkstack.org/update" target="_blank">
+                        <button><i class="fa-solid fa-download btn"></i> {{ __('messages.Update manually') }}</button>
+                    </a>
+                </div>
+            @endif
 
-@elseif($_SERVER['QUERY_STRING'] === '')
-      <?php //if no new version available ?>
-        
-        <div class="logo-container fadein">
-<img class="logo-img" src="{{ asset('assets/linkstack/images/logo.svg') }}" alt="Logo">
-        </div>
-        <h1>{{__('messages.No new version')}}</h1>
-        <h4>{{__('messages.There is no new version available')}}</h4>
-        <br><div class="row">
-        &ensp;<a class="btn" href="{{ url('dashboard') }}"><button><i class="fa-solid fa-house-laptop btn"></i> {{__('messages.Admin Panel')}}</button></a>&ensp;
-        </div>
-      
-@endif
+            @if ($_SERVER['QUERY_STRING'] === 'updating')
+                @php
+                    set_time_limit(0);
+                    try {
+                        // Determine the latest version and file URL
+                        $latestVersion = $isBeta ? $Vbeta_git : $Vgit;
+                        $fileUrl = $isBeta ? $betaServer . $latestVersion . '.zip' : $updateServer . $latestVersion . '.zip';
 
-@if($_SERVER['QUERY_STRING'] === 'finishing')
-<?php //finishing up update ?>
-<?php 
-$debug = NULL;
-if(EnvEditor::getKey('APP_DEBUG') == 'false'){
-   if(EnvEditor::keyExists('APP_DEBUG')){EnvEditor::editKey('APP_DEBUG', 'true');}
-   if(EnvEditor::keyExists('APP_ENV')){EnvEditor::editKey('APP_ENV', 'local');}
-   if(EnvEditor::keyExists('LOG_LEVEL')){EnvEditor::editKey('LOG_LEVEL', 'debug');}
-   $debug = true;
-}
-?>
-        <div class="logo-container fadein">
-<img class="logo-img" src="{{ asset('assets/linkstack/images/logo-loading.svg') }}" alt="Logo">
-        </div>
-        <h1 class="loadingtxt">{{__('messages.Finishing up')}}</h1>
-        
-        @include('components.finishing')
-        
-        <?php EnvEditor::editKey('MAINTENANCE_MODE', false); ?>
-<?php 
-if($debug === true){
-   if(EnvEditor::keyExists('APP_DEBUG')){EnvEditor::editKey('APP_DEBUG', 'false');}
-   if(EnvEditor::keyExists('APP_ENV')){EnvEditor::editKey('APP_ENV', 'production');}
-   if(EnvEditor::keyExists('LOG_LEVEL')){EnvEditor::editKey('LOG_LEVEL', 'error');}
-}
-?>
+                        // Download the update file
+                        $response = Http::timeout(120)->get($fileUrl);
 
-<?php echo "<meta http-equiv=\"refresh\" content=\"0; " . url()->current() . "?success\" />"; ?>
-@endif
+                        if ($response->failed()) {
+                            throw new Exception("HTTP request failed: {$response->status()} - {$response->body()}");
+                        }
 
-@if($_SERVER['QUERY_STRING'] === 'success')
-      <?php //after successfully updating ?>
-        
-        <div class="logo-container fadein">
-<img class="logo-img" src="{{ asset('assets/linkstack/images/logo.svg') }}" alt="Logo">
-        </div>
-        <h1>{{__('messages.Success!')}}</h1>
-        @if(env('JOIN_BETA') === true)
-        <p><?php echo __('messages.Latest beta version')."= " . external_file_get_contents("https://beta.linkstack.org/vbeta.json"); ?></p>
-        <p><?php  if(file_exists(base_path("vbeta.json"))) {echo __('messages.Installed beta version')."= " . file_get_contents(base_path("vbeta.json"));} else {echo __('messages.Installed beta version')."= ".__('messages.none');}  ?></p>
-        <p><?php  if($Vgit > $Vlocal) {echo __('messages.You need to update to the latest mainline release');} else {echo __("messages.You’re running the latest mainline release");}  ?></p>
-        @else
-        <h4>{{__('messages.The update was successful')}}</h4>
-        <style>.noteslink:hover{color:#006fd5;text-shadow:0px 6px 7px rgba(23,10,6,0.66);}</style>
-        <a class="noteslink" href="https://github.com/linkstackorg/linkstack/releases/latest" target="_blank"><i class="fa-solid fa-up-right-from-square"></i> {{__('messages.View the release notes')}}</a>
-        <br>
+                        // Save the downloaded ZIP file to storage
+                        $zipPath = storage_path('update.zip');
+                        $result = file_put_contents($zipPath, $response->body());
+                        if ($result === false) {
+                            throw new Exception('Failed to write update.zip to storage.');
+                        }
+
+                        // Initialize the ZIP archive
+                        $zip = new ZipArchive();
+                        if ($zip->open($zipPath) !== true) {
+                            throw new Exception('Failed to open ZIP archive for extraction.');
+                        }
+
+                        // Extract the contents to the base path
+                        $extractPath = base_path();
+                        if (!$zip->extractTo($extractPath)) {
+                            throw new Exception('ZIP extraction failed.');
+                        }
+
+                        $zip->close();
+
+                        // Delete the ZIP file after extraction
+                        if (!unlink($zipPath)) {
+                            Log::warning("Failed to delete ZIP file: $zipPath");
+                        }
+                    } catch (Exception $e) {
+                        session(['update_error' => 'Fatal error. ' . $e->getMessage()]);
+                    }
+                @endphp
+                <meta http-equiv="refresh" content="0; {{ url()->current() }}/?finishing" />
+            @endif
+
+            @if ($_SERVER['QUERY_STRING'] === 'backup')
+                @push('updater-head')
+                    <meta http-equiv="refresh" content="2; URL={{ url()->current() }}/?backups" />
+                @endpush
+                <div class="logo-container fadein">
+                    <img class="logo-img" src="{{ asset('assets/linkstack/images/logo-loading.svg') }}" alt="Logo">
+                </div>
+                <h1 class="loadingtxt">{{ __('messages.Creating backup') }}</h1>
+            @endif
+
+            @if ($_SERVER['QUERY_STRING'] === 'backups')
+                @php
+                    set_time_limit(0);
+                    // Test if the Artisan command is available
+                    try {
+                        $exitCode = Artisan::call('list');
+
+                        if ($exitCode !== 0) {
+                            session(['update_error' => "Backup creation failed. Your system doesn't support backups. Consider disabling update backups in your config. Exit code: $exitCode"]);
+                        }
+                    } catch (Exception $e) {
+                        session(['update_error' => "Backup creation failed. This may indicate that your system doesn't support backups or that the process exceeded the time limit. Consider disabling update backups in your config. Exit code: " . $e->getMessage()]);
+                    }
+
+                    try {
+                        Artisan::call('backup:clean', ['--disable-notifications' => true]);
+                    } catch (Exception $e) {
+                        session(['update_error' => $e->getMessage()]);
+                    }
+
+                    try {
+                        Artisan::call('backup:run', ['--only-files' => true, '--disable-notifications' => true]);
+                    } catch (Exception $e) {
+                        session(['update_error' => $e->getMessage()]);
+                    }
+                @endphp
+
+                @if (session()->has('update_error'))
+                    <meta http-equiv="refresh" content="1; {{ url()->current() }}/?error" />
+                @else
+                    @php file_put_contents(base_path('backups/CANUPDATE'), ''); @endphp
+                    <meta http-equiv="refresh" content="1; {{ url()->current() }}?preparing" />
+                @endif
+            @endif
+
+            @if ($_SERVER['QUERY_STRING'] === 'preparing')
+                <div class="logo-container fadein">
+                    <img class="logo-img" src="{{ asset('assets/linkstack/images/logo-loading.svg') }}" alt="Logo">
+                </div>
+                <h1 class="loadingtxt">{{ __('messages.Preparing update') }}</h1>
+                @php
+                    set_time_limit(0);
+
+                    if (file_exists(base_path() . '/storage/update.zip')) {
+                        try {
+                            unlink(base_path() . '/storage/update.zip');
+                        } catch (Exception $e) {
+                            session(['update_error' => 'File permission error. ' . $e->getMessage()]);
+                        }
+                    }
+
+                    try {
+                        $file = Http::timeout(10)->get($preUpdateServer)->body();
+                        file_put_contents(base_path('resources/views/components/pre-update.blade.php'), $file);
+                    } catch (Exception $e) {
+                        session(['update_error' => 'Could not prepare update. ' . $e->getMessage()]);
+                    }
+                @endphp
+                @if (session()->has('update_error'))
+                    <meta http-equiv="refresh" content="1; {{ url()->current() }}/?error" />
+                @else
+                    @include('components.pre-update')
+                    <meta http-equiv="refresh" content="1; {{ url()->current() }}?updating" />
+                @endif
+            @endif
+
+            @if (
+                $_SERVER['QUERY_STRING'] === 'updating' &&
+                    (file_exists(base_path('backups/CANUPDATE')) || env('SKIP_UPDATE_BACKUP') == true))
+                <div class="logo-container fadein">
+                    <img class="logo-img" src="{{ asset('assets/linkstack/images/logo-loading.svg') }}" alt="Logo">
+                </div>
+                <h1 class="loadingtxt">{{ __('messages.Updating') }}</h1>
+                @push('updater-head')
+                    <meta http-equiv="refresh" content="2; URL={{ url()->current() }}/../updating" />
+                @endpush
+            @endif
+        @elseif(empty($_SERVER['QUERY_STRING']))
+            <div class="logo-container fadein">
+                <img class="logo-img" src="{{ asset('assets/linkstack/images/logo.svg') }}" alt="Logo">
+            </div>
+            <h1>{{ __('messages.No new version') }}</h1>
+            <h4>{{ __('messages.There is no new version available') }}</h4>
+            <br>
+            <div class="row">
+                <a class="btn" href="{{ url('dashboard') }}">
+                    <button><i class="fa-solid fa-house-laptop btn"></i> {{ __('messages.Admin Panel') }}</button>
+                </a>
+            </div>
         @endif
-        <br><div class="row">
-        &ensp;<a class="btn" href="{{ url('dashboard') }}"><button><i class="fa-solid fa-house-laptop btn"></i> {{__('messages.Admin Panel')}}</button></a>&ensp;
 
-        @if(env('JOIN_BETA') === true)
-        &ensp;<a class="btn" href="{{url()->current()}}/"><button><i class="fa-solid fa-arrow-rotate-right btn"></i> {{__('messages.Run again')}}</button></a>&ensp;
+        @if ($_SERVER['QUERY_STRING'] === 'finishing')
+            @php
+                set_time_limit(0);
+                $debug = null;
+                if (EnvEditor::getKey('APP_DEBUG') == 'false') {
+                    if (EnvEditor::keyExists('APP_DEBUG')) {
+                        EnvEditor::editKey('APP_DEBUG', 'true');
+                    }
+                    if (EnvEditor::keyExists('APP_ENV')) {
+                        EnvEditor::editKey('APP_ENV', 'local');
+                    }
+                    if (EnvEditor::keyExists('LOG_LEVEL')) {
+                        EnvEditor::editKey('LOG_LEVEL', 'debug');
+                    }
+                    $debug = true;
+                }
+            @endphp
+            <div class="logo-container fadein">
+                <img class="logo-img" src="{{ asset('assets/linkstack/images/logo-loading.svg') }}" alt="Logo">
+            </div>
+            <h1 class="loadingtxt">{{ __('messages.Finishing up') }}</h1>
+            @include('components.finishing')
+            @php
+                EnvEditor::editKey('MAINTENANCE_MODE', false);
+                if ($debug === true) {
+                    if (EnvEditor::keyExists('APP_DEBUG')) {
+                        EnvEditor::editKey('APP_DEBUG', 'false');
+                    }
+                    if (EnvEditor::keyExists('APP_ENV')) {
+                        EnvEditor::editKey('APP_ENV', 'production');
+                    }
+                    if (EnvEditor::keyExists('LOG_LEVEL')) {
+                        EnvEditor::editKey('LOG_LEVEL', 'error');
+                    }
+                }
+            @endphp
+            @if(!session()->has('update_error') && ($isBeta || $Vgit === $Vlocal))
+                <meta http-equiv="refresh" content="0; {{ url()->current() }}?success" />
+            @else
+                @php
+                if (!session()->has('update_error')) {
+                    session(['update_error' => 'Update failed unexpectedly. Please try again later.']);
+                }
+                @endphp
+                <meta http-equiv="refresh" content="0; {{ url()->current() }}?error" />
+            @endif
         @endif
-        </div>
-      
-@endif
 
-@if($_SERVER['QUERY_STRING'] === 'error')
-      <?php //on error ?>
-        
-        <?php EnvEditor::editKey('MAINTENANCE_MODE', false); ?>
+        @if ($_SERVER['QUERY_STRING'] === 'success')
+            <div class="logo-container fadein">
+                <img class="logo-img" src="{{ asset('assets/linkstack/images/logo.svg') }}" alt="Logo">
+            </div>
+            <h1>{{ __('messages.Success!') }}</h1>
+            @if ($isBeta)
+                <p>{{ __('messages.Latest beta version') }} =
+                    {{ $Vbeta }}</p>
+                <p>{{ __('messages.Installed beta version') }} =
+                    {{ file_exists(base_path('vbeta.json')) ? file_get_contents(base_path('vbeta.json')) : __('messages.none') }}
+                </p>
+                <p>{{ $Vgit > $Vlocal ? __('messages.You need to update to the latest mainline release') : __('messages.You’re running the latest mainline release') }}
+                </p>
+            @else
+                <h4>{{ __('messages.The update was successful') }}</h4>
+                <a class="noteslink" href="{{ $repositoryUrl }}releases/latest" target="_blank">
+                    <i class="fa-solid fa-up-right-from-square"></i> {{ __('messages.View the release notes') }}
+                </a>
+                <br>
+            @endif
+            <br>
+            <div class="row">
+                <a class="btn" href="{{ url('dashboard') }}">
+                    <button><i class="fa-solid fa-house-laptop btn"></i> {{ __('messages.Admin Panel') }}</button>
+                </a>
+                @if ($isBeta)
+                    <a class="btn" href="{{ url()->current() }}/">
+                        <button><i class="fa-solid fa-arrow-rotate-right btn"></i> {{ __('messages.Run again') }}</button>
+                    </a>
+                @endif
+            </div>
+        @endif
 
-        <div class="logo-container fadein">
-<img class="logo-img" src="{{ asset('assets/linkstack/images/logo.svg') }}" alt="Logo">
-        </div>
-        <h1>{{__('messages.Error')}}</h1>
-        <h4>{{__('messages.Something went wrong with the update')}} :(</h4>
-        <br><div class="row">
-        &ensp;<a class="btn" href="{{ url('dashboard') }}"><button><i class="fa-solid fa-house-laptop btn"></i> {{__('messages.Admin Panel')}}</button></a>&ensp;
-        </div>
-      
-@endif
+        @if ($_SERVER['QUERY_STRING'] === 'error')
+            <?php EnvEditor::editKey('MAINTENANCE_MODE', false); ?>
 
-</div>
+            <div class="logo-container fadein">
+                <img class="logo-img" src="{{ asset('assets/linkstack/images/logo.svg') }}" alt="Logo">
+            </div>
+            <h1>{{ __('messages.Error') }}</h1>
+            <h4>{{ __('messages.Something went wrong with the update') }} :(</h4>
+
+            @if (session()->has('update_error'))
+                <div class="alert-box alert-box-error">
+                    <strong>Error:</strong>
+                    {{ session('update_error') }}
+                </div>
+                @php
+                    session()->forget('update_error');
+                @endphp
+            @else
+                <div class="alert-box alert-box-error">
+                    <strong>Error:</strong>
+                    Unknown error
+                </div>
+            @endif
+
+            <br>
+            <div class="row">
+                &ensp;<a class="btn" href="{{ url('dashboard') }}"><button><i class="fa-solid fa-house-laptop btn"></i>
+                        {{ __('messages.Admin Panel') }}</button></a>&ensp;
+            </div>
+        @endif
+
+    </div>
 @endpush
\ No newline at end of file
diff --git a/routes/web.php b/routes/web.php
index 75867d5..737d879 100755
--- a/routes/web.php
+++ b/routes/web.php
@@ -191,38 +191,8 @@ Route::group([
             'linktype'=>LinkTypeController::class
         ]);
     });
-  
 
-    Route::get('/updating', function (\Codedge\Updater\UpdaterManager $updater) {
-
-  // Check if new version is available
-  if($updater->source()->isNewVersionAvailable() and (file_exists(base_path("backups/CANUPDATE")) or env('SKIP_UPDATE_BACKUP') == true)) {
-
-    EnvEditor::editKey('MAINTENANCE_MODE', true);
-
-      // Get the current installed version
-      echo $updater->source()->getVersionInstalled();
-
-      // Get the new version available
-      $versionAvailable = $updater->source()->getVersionAvailable();
-
-      // Create a release
-      $release = $updater->source()->fetch($versionAvailable);
-
-      // Run the update process
-      $updater->source()->update($release);
-
-      if(env('SKIP_UPDATE_BACKUP') != true) {unlink(base_path("backups/CANUPDATE"));}
-
-      echo "<meta http-equiv=\"refresh\" content=\"0; " . url()->current() . "/../update?finishing\" />";
-
-  } else {
-    echo "<meta http-equiv=\"refresh\" content=\"0; " . url()->current() . "/../update?error\" />";
-  }
-
-});
-
-}); // ENd Admin authenticated routes
+}); // End Admin authenticated routes
 });
 
 // Displays Maintenance Mode page

From 42615036cd84e02c9fa872e8b077fb62c58e47e5 Mon Sep 17 00:00:00 2001
From: Julian Prieber <julian.prieber@gmail.com>
Date: Tue, 26 Nov 2024 18:23:08 +0100
Subject: [PATCH 2/3] Add beta support

---
 resources/views/update.blade.php | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/resources/views/update.blade.php b/resources/views/update.blade.php
index 8e4cb29..d1ae4e4 100644
--- a/resources/views/update.blade.php
+++ b/resources/views/update.blade.php
@@ -4,14 +4,20 @@
 
     @php
         // Must end with '/'
-        $betaServer      = env('BETA_SERVER', 'https://beta.linkstack.org/');
-        $updateServer    = env('UPDATE_SERVER', 'https://update.linkstack.org/');
-        $versionServer   = env('VERSION_SERVER', 'https://version.linkstack.org/');
-        $preUpdateServer = env('PRE_UPDATE_SERVER', 'https://pre-update.linkstack.org/');
-        $repositoryUrl   = env('REPOSITORY_URL', 'https://github.com/linkstackorg/linkstack/');
+        $betaServer          = env('BETA_SERVER', 'https://beta.linkstack.org/');
+        $betaPreUpdateServer = env('BETA_PRE_UPDATE_SERVER', 'https://pre-update.linkstack.org/beta/');
+        $updateServer        = env('UPDATE_SERVER', 'https://update.linkstack.org/');
+        $versionServer       = env('VERSION_SERVER', 'https://version.linkstack.org/');
+        $preUpdateServer     = env('PRE_UPDATE_SERVER', 'https://pre-update.linkstack.org/');
+        $repositoryUrl       = env('REPOSITORY_URL', 'https://github.com/linkstackorg/linkstack/');
+
+        $isBeta = env('JOIN_BETA', false);
+
+        if ($isBeta) {
+            $preUpdateServer = $betaPreUpdateServer;
+        }
 
         try {
-            $isBeta = env('JOIN_BETA', false);
             $Vbeta = trim(Http::timeout(5)->get($betaServer . 'vbeta.json')->body());
             $Vbeta_git = trim(Http::timeout(5)->get($betaServer . 'version.json')->body());
             $Vgit = trim(Http::timeout(5)->get($versionServer)->body());

From 76f14a78fec0bc99de6b8ccdaa7b7a50e43f4a38 Mon Sep 17 00:00:00 2001
From: Julian Prieber <julian.prieber@gmail.com>
Date: Wed, 27 Nov 2024 14:10:31 +0100
Subject: [PATCH 3/3] Improve error handling

---
 .../views/components/finishing.blade.php      | 563 +++++++++++-------
 resources/views/update.blade.php              |  23 +-
 2 files changed, 361 insertions(+), 225 deletions(-)

diff --git a/resources/views/components/finishing.blade.php b/resources/views/components/finishing.blade.php
index 5385d5f..27c635a 100644
--- a/resources/views/components/finishing.blade.php
+++ b/resources/views/components/finishing.blade.php
@@ -1,4 +1,4 @@
-<?php 
+<?php
 use Illuminate\Database\Migrations\Migration;
 use Illuminate\Database\Migrations\Migrator;
 use Illuminate\Database\Schema\Blueprint;
@@ -10,120 +10,222 @@ use App\Models\Page;
 
 set_time_limit(0);
 
-         //run before finishing:
-            if(EnvEditor::keyExists('JOIN_BETA')){ /* Do nothing if key already exists */ 
-            } else { EnvEditor::addKey('JOIN_BETA', 'false');} // Adds key to .env file 
+//run before finishing:
+if (EnvEditor::keyExists('JOIN_BETA')) {
+    /* Do nothing if key already exists */
+} else {
+    EnvEditor::addKey('JOIN_BETA', 'false');
+} // Adds key to .env file
 
-            if(EnvEditor::keyExists('SKIP_UPDATE_BACKUP')){ /* Do nothing if key already exists */ 
-            } else { EnvEditor::addKey('SKIP_UPDATE_BACKUP', 'false');} // Adds key to .env file 
+if (EnvEditor::keyExists('SKIP_UPDATE_BACKUP')) {
+    /* Do nothing if key already exists */
+} else {
+    EnvEditor::addKey('SKIP_UPDATE_BACKUP', 'false');
+} // Adds key to .env file
 
-            if(EnvEditor::keyExists('CUSTOM_META_TAGS')){ /* Do nothing if key already exists */ 
-            } else {EnvEditor::addKey('CUSTOM_META_TAGS', 'false');}
+if (EnvEditor::keyExists('CUSTOM_META_TAGS')) {
+    /* Do nothing if key already exists */
+} else {
+    EnvEditor::addKey('CUSTOM_META_TAGS', 'false');
+}
 
-            if(EnvEditor::keyExists('MAINTENANCE_MODE')){ /* Do nothing if key already exists */ 
-            } else {EnvEditor::addKey('MAINTENANCE_MODE', 'false');}
+if (EnvEditor::keyExists('MAINTENANCE_MODE')) {
+    /* Do nothing if key already exists */
+} else {
+    EnvEditor::addKey('MAINTENANCE_MODE', 'false');
+}
 
-            if(EnvEditor::keyExists('ALLOW_CUSTOM_CODE_IN_THEMES')){ /* Do nothing if key already exists */ 
-            } else {EnvEditor::addKey('ALLOW_CUSTOM_CODE_IN_THEMES', 'true');}
+if (EnvEditor::keyExists('ALLOW_CUSTOM_CODE_IN_THEMES')) {
+    /* Do nothing if key already exists */
+} else {
+    EnvEditor::addKey('ALLOW_CUSTOM_CODE_IN_THEMES', 'true');
+}
 
-            if(EnvEditor::keyExists('ENABLE_THEME_UPDATER')){ /* Do nothing if key already exists */ 
-            } else {EnvEditor::addKey('ENABLE_THEME_UPDATER', 'true');}
+if (EnvEditor::keyExists('ENABLE_THEME_UPDATER')) {
+    /* Do nothing if key already exists */
+} else {
+    EnvEditor::addKey('ENABLE_THEME_UPDATER', 'true');
+}
 
-            if(EnvEditor::keyExists('ENABLE_SOCIAL_LOGIN')){ /* Do nothing if key already exists */ 
-            } else {EnvEditor::addKey('ENABLE_SOCIAL_LOGIN', 'false');}
+if (EnvEditor::keyExists('ENABLE_SOCIAL_LOGIN')) {
+    /* Do nothing if key already exists */
+} else {
+    EnvEditor::addKey('ENABLE_SOCIAL_LOGIN', 'false');
+}
 
-            if(EnvEditor::keyExists('USE_THEME_PREVIEW_IFRAME')){ /* Do nothing if key already exists */ 
-            } else {EnvEditor::addKey('USE_THEME_PREVIEW_IFRAME', 'true');}
+if (EnvEditor::keyExists('USE_THEME_PREVIEW_IFRAME')) {
+    /* Do nothing if key already exists */
+} else {
+    EnvEditor::addKey('USE_THEME_PREVIEW_IFRAME', 'true');
+}
 
-            if(EnvEditor::keyExists('FORCE_ROUTE_HTTPS')){ /* Do nothing if key already exists */ 
-            } else {EnvEditor::addKey('FORCE_ROUTE_HTTPS', 'false');}
+if (EnvEditor::keyExists('FORCE_ROUTE_HTTPS')) {
+    /* Do nothing if key already exists */
+} else {
+    EnvEditor::addKey('FORCE_ROUTE_HTTPS', 'false');
+}
 
-            if(EnvEditor::keyExists('HIDE_VERIFICATION_CHECKMARK')){ /* Do nothing if key already exists */ 
-            } else {EnvEditor::addKey('HIDE_VERIFICATION_CHECKMARK', 'false');}
+if (EnvEditor::keyExists('HIDE_VERIFICATION_CHECKMARK')) {
+    /* Do nothing if key already exists */
+} else {
+    EnvEditor::addKey('HIDE_VERIFICATION_CHECKMARK', 'false');
+}
 
-            if(EnvEditor::keyExists('ALLOW_CUSTOM_BACKGROUNDS')){ /* Do nothing if key already exists */ 
-            } else {EnvEditor::addKey('ALLOW_CUSTOM_BACKGROUNDS', 'true');}
+if (EnvEditor::keyExists('ALLOW_CUSTOM_BACKGROUNDS')) {
+    /* Do nothing if key already exists */
+} else {
+    EnvEditor::addKey('ALLOW_CUSTOM_BACKGROUNDS', 'true');
+}
 
-            if(EnvEditor::keyExists('ALLOW_USER_IMPORT')){ /* Do nothing if key already exists */ 
-            } else {EnvEditor::addKey('ALLOW_USER_IMPORT', 'true');}
+if (EnvEditor::keyExists('ALLOW_USER_IMPORT')) {
+    /* Do nothing if key already exists */
+} else {
+    EnvEditor::addKey('ALLOW_USER_IMPORT', 'true');
+}
 
-            if(EnvEditor::keyExists('ALLOW_USER_EXPORT')){ /* Do nothing if key already exists */ 
-            } else {EnvEditor::addKey('ALLOW_USER_EXPORT', 'true');}
+if (EnvEditor::keyExists('ALLOW_USER_EXPORT')) {
+    /* Do nothing if key already exists */
+} else {
+    EnvEditor::addKey('ALLOW_USER_EXPORT', 'true');
+}
 
-            if(EnvEditor::keyExists('SUPPORTED_DOMAINS')){ /* Do nothing if key already exists */ 
-            } else {EnvEditor::addKey('SUPPORTED_DOMAINS', '');}
+if (EnvEditor::keyExists('SUPPORTED_DOMAINS')) {
+    /* Do nothing if key already exists */
+} else {
+    EnvEditor::addKey('SUPPORTED_DOMAINS', '');
+}
 
-            if(EnvEditor::keyExists('MANUAL_USER_VERIFICATION')){ /* Do nothing if key already exists */ 
-            } else {EnvEditor::addKey('MANUAL_USER_VERIFICATION', 'false');}
+if (EnvEditor::keyExists('MANUAL_USER_VERIFICATION')) {
+    /* Do nothing if key already exists */
+} else {
+    EnvEditor::addKey('MANUAL_USER_VERIFICATION', 'false');
+}
 
-            if(EnvEditor::keyExists('DISPLAY_CREDIT_FOOTER')){ /* Do nothing if key already exists */ 
-            } else {EnvEditor::addKey('DISPLAY_CREDIT_FOOTER', 'true');}
+if (EnvEditor::keyExists('DISPLAY_CREDIT_FOOTER')) {
+    /* Do nothing if key already exists */
+} else {
+    EnvEditor::addKey('DISPLAY_CREDIT_FOOTER', 'true');
+}
 
-            if(EnvEditor::keyExists('LOCALE')){ /* Do nothing if key already exists */ 
-            } else {EnvEditor::addKey('LOCALE', 'en');}
+if (EnvEditor::keyExists('LOCALE')) {
+    /* Do nothing if key already exists */
+} else {
+    EnvEditor::addKey('LOCALE', 'en');
+}
 
-            if(EnvEditor::keyExists('ENABLE_REPORT_ICON')){ /* Do nothing if key already exists */ 
-            } else {EnvEditor::addKey('ENABLE_REPORT_ICON', 'false');}
+if (EnvEditor::keyExists('ENABLE_REPORT_ICON')) {
+    /* Do nothing if key already exists */
+} else {
+    EnvEditor::addKey('ENABLE_REPORT_ICON', 'false');
+}
 
-            if(EnvEditor::keyExists('ENABLE_ADMIN_BAR')){ /* Do nothing if key already exists */ 
-            } else {EnvEditor::addKey('ENABLE_ADMIN_BAR', 'true');}
+if (EnvEditor::keyExists('ENABLE_ADMIN_BAR')) {
+    /* Do nothing if key already exists */
+} else {
+    EnvEditor::addKey('ENABLE_ADMIN_BAR', 'true');
+}
 
-            if(EnvEditor::keyExists('ENABLE_ADMIN_BAR_USERS')){ /* Do nothing if key already exists */ 
-            } else {EnvEditor::addKey('ENABLE_ADMIN_BAR_USERS', 'false');}
+if (EnvEditor::keyExists('ENABLE_ADMIN_BAR_USERS')) {
+    /* Do nothing if key already exists */
+} else {
+    EnvEditor::addKey('ENABLE_ADMIN_BAR_USERS', 'false');
+}
 
-            if(EnvEditor::keyExists('ADMIN_EMAIL')){} else 
-            {if(Auth::user()->id == 1){EnvEditor::addKey('ADMIN_EMAIL', App\Models\User::find(1)->email);}
-            else{EnvEditor::addKey('ADMIN_EMAIL', '');}}
+if (EnvEditor::keyExists('ADMIN_EMAIL')) {
+} else {
+    if (Auth::user()->id == 1) {
+        EnvEditor::addKey('ADMIN_EMAIL', App\Models\User::find(1)->email);
+    } else {
+        EnvEditor::addKey('ADMIN_EMAIL', '');
+    }
+}
 
-            if(env('APP_NAME') == 'LittleLink Custom' or env('APP_NAME') == 'LittleLink') {
-                EnvEditor::editKey('APP_NAME', 'LinkStack');
-            }
+if (env('APP_NAME') == 'LittleLink Custom' or env('APP_NAME') == 'LittleLink') {
+    EnvEditor::editKey('APP_NAME', 'LinkStack');
+}
 
-            if (EnvEditor::keyExists('ALLOW_REGISTRATION')) { /* Do nothing if key already exists */ 
-            } else {
-                $pagedb = DB::table('pages')->select('register')->first();
-                if ($pagedb->register) {
-                    EnvEditor::addKey('ALLOW_REGISTRATION', 'true');
-                } else {
-                    EnvEditor::addKey('ALLOW_REGISTRATION', 'false');
-                }
-                try {
-                    DB::table('pages')->update(['register' => null]);
-                } catch (Exception $e) {}
-            }
+if (EnvEditor::keyExists('ALLOW_REGISTRATION')) {
+    /* Do nothing if key already exists */
+} else {
+    $pagedb = DB::table('pages')->select('register')->first();
+    if ($pagedb->register) {
+        EnvEditor::addKey('ALLOW_REGISTRATION', 'true');
+    } else {
+        EnvEditor::addKey('ALLOW_REGISTRATION', 'false');
+    }
+    try {
+        DB::table('pages')->update(['register' => null]);
+    } catch (Exception $e) {
+        session(['update_error' => $e->getMessage()]);
+    }
+}
 
-            try {
-                $file = base_path('storage/RSTAC');
-                if (file_exists($file)) {
-                    copy(base_path('storage/templates/advanced-config.php'), base_path('config/advanced-config.php'));
-                    unlink($file);
-                }
-            } catch (Exception $e) {}
+try {
+    $file = base_path('storage/RSTAC');
+    if (file_exists($file)) {
+        copy(base_path('storage/templates/advanced-config.php'), base_path('config/advanced-config.php'));
+        unlink($file);
+    }
+} catch (Exception $e) {
+    session(['update_error' => $e->getMessage()]);
+}
 
-            try {
-                $vendorLangPath = resource_path('lang/vendor');
-                if (File::exists($vendorLangPath)) {
-                    File::deleteDirectory($vendorLangPath);
-                }
-            } catch (Exception $e) {}
+try {
+    $vendorLangPath = resource_path('lang/vendor');
+    if (File::exists($vendorLangPath)) {
+        File::deleteDirectory($vendorLangPath);
+    }
+} catch (Exception $e) {
+    session(['update_error' => $e->getMessage()]);
+}
 
-            // Footer page customization
-            if(EnvEditor::keyExists('DISPLAY_FOOTER_HOME')){} else {EnvEditor::addKey('DISPLAY_FOOTER_HOME', 'true');}
-            if(EnvEditor::keyExists('DISPLAY_FOOTER_TERMS')){} else {EnvEditor::addKey('DISPLAY_FOOTER_TERMS', 'true');}
-            if(EnvEditor::keyExists('DISPLAY_FOOTER_PRIVACY')){} else {EnvEditor::addKey('DISPLAY_FOOTER_PRIVACY', 'true');}
-            if(EnvEditor::keyExists('DISPLAY_FOOTER_CONTACT')){} else {EnvEditor::addKey('DISPLAY_FOOTER_CONTACT', 'true');}
-            if(EnvEditor::keyExists('TITLE_FOOTER_HOME')){} else {EnvEditor::addKey('TITLE_FOOTER_HOME', 'Home');}
-            if(EnvEditor::keyExists('TITLE_FOOTER_TERMS')){} else {EnvEditor::addKey('TITLE_FOOTER_TERMS', 'Terms');}
-            if(EnvEditor::keyExists('TITLE_FOOTER_PRIVACY')){} else {EnvEditor::addKey('TITLE_FOOTER_PRIVACY', 'Privacy');}
-            if(EnvEditor::keyExists('TITLE_FOOTER_CONTACT')){} else {EnvEditor::addKey('TITLE_FOOTER_CONTACT', 'Contact');}
-            if(EnvEditor::keyExists('HOME_FOOTER_LINK')){} else {EnvEditor::addKey('HOME_FOOTER_LINK', '');}
+// Footer page customization
+if (EnvEditor::keyExists('DISPLAY_FOOTER_HOME')) {
+} else {
+    EnvEditor::addKey('DISPLAY_FOOTER_HOME', 'true');
+}
+if (EnvEditor::keyExists('DISPLAY_FOOTER_TERMS')) {
+} else {
+    EnvEditor::addKey('DISPLAY_FOOTER_TERMS', 'true');
+}
+if (EnvEditor::keyExists('DISPLAY_FOOTER_PRIVACY')) {
+} else {
+    EnvEditor::addKey('DISPLAY_FOOTER_PRIVACY', 'true');
+}
+if (EnvEditor::keyExists('DISPLAY_FOOTER_CONTACT')) {
+} else {
+    EnvEditor::addKey('DISPLAY_FOOTER_CONTACT', 'true');
+}
+if (EnvEditor::keyExists('TITLE_FOOTER_HOME')) {
+} else {
+    EnvEditor::addKey('TITLE_FOOTER_HOME', 'Home');
+}
+if (EnvEditor::keyExists('TITLE_FOOTER_TERMS')) {
+} else {
+    EnvEditor::addKey('TITLE_FOOTER_TERMS', 'Terms');
+}
+if (EnvEditor::keyExists('TITLE_FOOTER_PRIVACY')) {
+} else {
+    EnvEditor::addKey('TITLE_FOOTER_PRIVACY', 'Privacy');
+}
+if (EnvEditor::keyExists('TITLE_FOOTER_CONTACT')) {
+} else {
+    EnvEditor::addKey('TITLE_FOOTER_CONTACT', 'Contact');
+}
+if (EnvEditor::keyExists('HOME_FOOTER_LINK')) {
+} else {
+    EnvEditor::addKey('HOME_FOOTER_LINK', '');
+}
 
-            if(EnvEditor::keyExists('FORCE_HTTPS')){ /* Do nothing if key already exists */ 
-            } else {EnvEditor::addKey('FORCE_HTTPS', 'false');}
+if (EnvEditor::keyExists('FORCE_HTTPS')) {
+    /* Do nothing if key already exists */
+} else {
+    EnvEditor::addKey('FORCE_HTTPS', 'false');
+}
 
-            $data['page'] = Page::select('contact')->first();
-            if (strpos($data['page']->contact, 'info@littlelink-custom.com') !== false or strpos($data['page']->contact, 'LittleLink Custom') !== false) {
-            $contact = '
+$data['page'] = Page::select('contact')->first();
+if (strpos($data['page']->contact, 'info@littlelink-custom.com') !== false or strpos($data['page']->contact, 'LittleLink Custom') !== false) {
+    $contact = '
             <p><strong><a href="https://linkstack.org/">LinkStack</a></strong> is a free, open source&nbsp;link&nbsp;sharing platform. We depend on community feedback to steadily improve this project.</p>
             
             <p><strong>Feel free to send us your feedback!</strong></p>
@@ -139,62 +241,63 @@ set_time_limit(0);
             
             <p>&nbsp;</p>
             ';
-            Page::first()->update(['contact' => $contact]);
-            }
+    Page::first()->update(['contact' => $contact]);
+}
 
-
-            $data['page'] = Page::select('home_message')->first();
-            if (strpos($data['page']->home_message, 'LittleLink Custom') !== false) {
-            $home_message = '
+$data['page'] = Page::select('home_message')->first();
+if (strpos($data['page']->home_message, 'LittleLink Custom') !== false) {
+    $home_message = '
             <p>Take control of your online presence with&nbsp;<a href="https://linkstack.org/"><strong>LinkStack</strong></a> the privacy-focused, open-source <strong>link management platform</strong>. Create a customizable profile page to manage <strong>all your important links in one convenient location</strong> and give your audience a seamless browsing experience.</p>
             ';
-            Page::first()->update(['home_message' => $home_message]);
-            }
+    Page::first()->update(['home_message' => $home_message]);
+}
 
-            $migrationFiles = glob(database_path('migrations/*.php'));
-                    
-            $fileNames = array_map(function ($file) {
-                return basename($file, '.php');
-            }, $migrationFiles);
-            
-            foreach ($fileNames as $fileName) {
-                if (!DB::table('migrations')->where('migration', $fileName)->exists()) {
-                    DB::table('migrations')->insert(['migration' => $fileName, 'batch' => 1]);
-                }
-            }
+$migrationFiles = glob(database_path('migrations/*.php'));
 
-            /* Updates button database entries */ 
-            Schema::disableForeignKeyConstraints();
-            $existingMigration = '2021_03_17_044922_create_buttons_table';
+$fileNames = array_map(function ($file) {
+    return basename($file, '.php');
+}, $migrationFiles);
 
-            try {
-            if (DB::table('migrations')->where('migration', $existingMigration)->exists()) {
-                DB::table('migrations')->where('migration', $existingMigration)->delete();
-            }
-            
-            Schema::dropIfExists('buttons');
-            
-            $migrator = app('migrator');
-            $migrator->run(database_path('migrations'));
-            
-            $seeder = new ButtonSeeder();
-            $seeder->run();
-            } catch (exception $e) {}
-            Schema::enableForeignKeyConstraints();
+foreach ($fileNames as $fileName) {
+    if (!DB::table('migrations')->where('migration', $fileName)->exists()) {
+        DB::table('migrations')->insert(['migration' => $fileName, 'batch' => 1]);
+    }
+}
 
-            if (!Schema::hasColumn('users', 'auth_as')) {
-                Schema::table('users', function (Blueprint $table) {
-                 $table->unsignedInteger('auth_as')->nullable();
-              });
-            }
+/* Updates button database entries */
+Schema::disableForeignKeyConstraints();
+$existingMigration = '2021_03_17_044922_create_buttons_table';
 
-    try {
-        DB::table('link_types')->updateOrInsert([
-            'typename' => 'text',
-            'title' => 'Text',
-            'icon' => 'bi bi-fonts',
-            'description' => 'Add static text to your page that is not clickable.',
-            'params' => '[{
+try {
+    if (DB::table('migrations')->where('migration', $existingMigration)->exists()) {
+        DB::table('migrations')->where('migration', $existingMigration)->delete();
+    }
+
+    Schema::dropIfExists('buttons');
+
+    $migrator = app('migrator');
+    $migrator->run(database_path('migrations'));
+
+    $seeder = new ButtonSeeder();
+    $seeder->run();
+} catch (exception $e) {
+    session(['update_error' => $e->getMessage()]);
+}
+Schema::enableForeignKeyConstraints();
+
+if (!Schema::hasColumn('users', 'auth_as')) {
+    Schema::table('users', function (Blueprint $table) {
+        $table->unsignedInteger('auth_as')->nullable();
+    });
+}
+
+try {
+    DB::table('link_types')->updateOrInsert([
+        'typename' => 'text',
+        'title' => 'Text',
+        'icon' => 'bi bi-fonts',
+        'description' => 'Add static text to your page that is not clickable.',
+        'params' => '[{
                 "tag": "textarea",
                 "id": "static-text",
                 "for": "static_text",
@@ -202,94 +305,128 @@ set_time_limit(0);
                 "name": "static_text",
                 "class": "form-control"
             }
-            ]'
-        ]);
+            ]',
+    ]);
 
-        DB::table('link_types')->updateOrInsert([
-            'typename' => 'email',
-            'title' => 'E-Mail address',
-            'icon' => 'bi bi-envelope-fill',
-            'description' => 'Add an email that opens a system dialog to compose a new email.'
-        ]);
+    DB::table('link_types')->updateOrInsert([
+        'typename' => 'email',
+        'title' => 'E-Mail address',
+        'icon' => 'bi bi-envelope-fill',
+        'description' => 'Add an email that opens a system dialog to compose a new email.',
+    ]);
 
-        DB::table('link_types')->updateOrInsert([
-            'typename' => 'telephone',
-            'title' => 'Telephone number',
-            'icon' => 'bi bi-telephone-fill',
-            'description' => 'Add a telephone number that opens a system dialog to initiate a phone call.'
-        ]);
+    DB::table('link_types')->updateOrInsert([
+        'typename' => 'telephone',
+        'title' => 'Telephone number',
+        'icon' => 'bi bi-telephone-fill',
+        'description' => 'Add a telephone number that opens a system dialog to initiate a phone call.',
+    ]);
 
-        DB::table('link_types')->updateOrInsert([
-            'typename' => 'vcard',
-            'title' => 'Vcard',
-            'icon' => 'bi bi-person-square',
-            'description' => 'Create or upload an electronic business card.'
-        ]);
-    } catch (exception $e) {}
+    DB::table('link_types')->updateOrInsert([
+        'typename' => 'vcard',
+        'title' => 'Vcard',
+        'icon' => 'bi bi-person-square',
+        'description' => 'Create or upload an electronic business card.',
+    ]);
+} catch (exception $e) {
+    session(['update_error' => $e->getMessage()]);
+}
 
+// Moves all previous images to the new path
+try {
+    if (is_dir(base_path('assets/img'))) {
+        $files = File::files(base_path('img'));
+        foreach ($files as $file) {
+            $filename = $file->getFilename();
+            $destination = base_path('assets/img/' . $filename);
+            if (!File::exists($destination)) {
+                if (!$file->isDir()) {
+                    File::move($file, $destination);
+                }
+            }
+        }
+    }
+} catch (exception $e) {
+}
+try {
+    if (is_dir(base_path('assets/img/background-img'))) {
+        $files = File::files(base_path('img/background-img'));
+        foreach ($files as $file) {
+            $filename = $file->getFilename();
+            $destination = base_path('assets/img/background-img/' . $filename);
+            if (!File::exists($destination)) {
+                if (!$file->isDir()) {
+                    File::move($file, $destination);
+                }
+            }
+        }
+    }
+} catch (exception $e) {
+}
+try {
+    if (is_dir(base_path('littlelink/images'))) {
+        $files = File::files(base_path('littlelink/images'));
+        foreach ($files as $file) {
+            $filename = $file->getFilename();
+            $destination = base_path('assets/linkstack/images/' . $filename);
+            if (!File::exists($destination)) {
+                if (!$file->isDir()) {
+                    File::move($file, $destination);
+                }
+            }
+        }
+    }
+} catch (exception $e) {
+}
+try {
+    if (is_dir(base_path('littlelink/images'))) {
+        $files = File::files(base_path('littlelink/images'));
+        foreach ($files as $file) {
+            $filename = $file->getFilename();
+            $destination = base_path('assets/linkstack/images/' . $filename);
+            if (!File::exists($destination)) {
+                if (!$file->isDir()) {
+                    File::move($file, $destination);
+                }
+            }
+        }
+    }
+} catch (exception $e) {
+}
+try {
+    if (is_dir(base_path('studio/favicon/icons'))) {
+        $files = File::files(base_path('studio/favicon/icons'));
+        foreach ($files as $file) {
+            $filename = $file->getFilename();
+            $destination = base_path('assets/favicon/icons/' . $filename);
+            if (!File::exists($destination)) {
+                if (!$file->isDir()) {
+                    File::move($file, $destination);
+                }
+            }
+        }
+    }
+} catch (exception $e) {
+}
 
- // Moves all previous images to the new path
-try{if (is_dir(base_path('assets/img'))) {
-    $files = File::files(base_path('img'));
-    foreach ($files as $file) {
-        $filename = $file->getFilename();
-        $destination = base_path('assets/img/' . $filename);
-        if (!File::exists($destination)) {
-            if (!$file->isDir()) {
-                File::move($file, $destination);}}}}
-            }catch(exception $e) {}
-try{if (is_dir(base_path('assets/img/background-img'))) {
-    $files = File::files(base_path('img/background-img'));
-    foreach ($files as $file) {
-        $filename = $file->getFilename();
-        $destination = base_path('assets/img/background-img/' . $filename);
-        if (!File::exists($destination)) {
-            if (!$file->isDir()) {
-                File::move($file, $destination);}}}}
-            }catch(exception $e) {}
-try{if (is_dir(base_path('littlelink/images'))) {
-    $files = File::files(base_path('littlelink/images'));
-    foreach ($files as $file) {
-        $filename = $file->getFilename();
-        $destination = base_path('assets/linkstack/images/' . $filename);
-        if (!File::exists($destination)) {
-            if (!$file->isDir()) {
-                File::move($file, $destination);}}}}
-            }catch(exception $e) {}
-try{if (is_dir(base_path('littlelink/images'))) {
-    $files = File::files(base_path('littlelink/images'));
-    foreach ($files as $file) {
-        $filename = $file->getFilename();
-        $destination = base_path('assets/linkstack/images/' . $filename);
-        if (!File::exists($destination)) {
-            if (!$file->isDir()) {
-                File::move($file, $destination);}}}}
-            }catch(exception $e) {}
-try{if (is_dir(base_path('studio/favicon/icons'))) {
-    $files = File::files(base_path('studio/favicon/icons'));
-    foreach ($files as $file) {
-        $filename = $file->getFilename();
-        $destination = base_path('assets/favicon/icons/' . $filename);
-        if (!File::exists($destination)) {
-            if (!$file->isDir()) {
-                File::move($file, $destination);}}}}
-            }catch(exception $e) {}
-
-    
-    // Changes saved profile images from littlelink_name to IDs.
-    // This runs every time the updater runs.
-    // Not sure if this will cause any issues.
-    // If it works, I won't touch it.
-    try {
+// Changes saved profile images from littlelink_name to IDs.
+// This runs every time the updater runs.
+// Not sure if this will cause any issues.
+// If it works, I won't touch it.
+try {
     $users = DB::table('users')->get();
     foreach ($users as $user) {
         $oldName = $user->littlelink_name . '.png';
         $newName = $user->id . '.png';
         $oldPath = base_path('assets/img/' . $oldName);
         $newPath = base_path('assets/img/' . $newName);
-    
+
         if (File::exists($oldPath)) {
             File::move($oldPath, $newPath);
-        }}} catch (exception $e) {}
+        }
+    }
+} catch (exception $e) {
+    session(['update_error' => $e->getMessage()]);
+}
 
-        ?>
\ No newline at end of file
+?>
diff --git a/resources/views/update.blade.php b/resources/views/update.blade.php
index d1ae4e4..5b7d03c 100644
--- a/resources/views/update.blade.php
+++ b/resources/views/update.blade.php
@@ -62,6 +62,10 @@
             @endif
 
             @if ($_SERVER['QUERY_STRING'] === 'updating')
+                <div class="logo-container fadein">
+                    <img class="logo-img" src="{{ asset('assets/linkstack/images/logo-loading.svg') }}" alt="Logo">
+                </div>
+                <h1 class="loadingtxt">{{ __('messages.Updating') }}</h1>
                 @php
                     set_time_limit(0);
                     try {
@@ -105,7 +109,13 @@
                         session(['update_error' => 'Fatal error. ' . $e->getMessage()]);
                     }
                 @endphp
-                <meta http-equiv="refresh" content="0; {{ url()->current() }}/?finishing" />
+
+                @if (session()->has('update_error'))
+                    <meta http-equiv="refresh" content="1; {{ url()->current() }}/?error" />
+                @else
+                    <meta http-equiv="refresh" content="0; {{ url()->current() }}/?finishing" />
+                @endif
+
             @endif
 
             @if ($_SERVER['QUERY_STRING'] === 'backup')
@@ -184,17 +194,6 @@
                 @endif
             @endif
 
-            @if (
-                $_SERVER['QUERY_STRING'] === 'updating' &&
-                    (file_exists(base_path('backups/CANUPDATE')) || env('SKIP_UPDATE_BACKUP') == true))
-                <div class="logo-container fadein">
-                    <img class="logo-img" src="{{ asset('assets/linkstack/images/logo-loading.svg') }}" alt="Logo">
-                </div>
-                <h1 class="loadingtxt">{{ __('messages.Updating') }}</h1>
-                @push('updater-head')
-                    <meta http-equiv="refresh" content="2; URL={{ url()->current() }}/../updating" />
-                @endpush
-            @endif
         @elseif(empty($_SERVER['QUERY_STRING']))
             <div class="logo-container fadein">
                 <img class="logo-img" src="{{ asset('assets/linkstack/images/logo.svg') }}" alt="Logo">