Compare commits
29 Commits
f167e64d00
...
stable
| Author | SHA1 | Date | |
|---|---|---|---|
| d178b8da91 | |||
| 7ecb6378fe | |||
| 9496078644 | |||
| 3aab864c34 | |||
| 5eb5404061 | |||
| d9040f1e6c | |||
| 8270e635d6 | |||
| d724f80cad | |||
| 1f5f340ce4 | |||
| 40e111b3a6 | |||
| 506011f933 | |||
| 80a8b86a08 | |||
| c1cb3f394a | |||
| 31c8491aaf | |||
| 0ad5916504 | |||
| bac70c3927 | |||
| bf23e46e2d | |||
| 55b9a69c42 | |||
| b6ce5160f9 | |||
| d279e7f36f | |||
| a0d914ea24 | |||
| ce3b89d54e | |||
| 5cd8a1a9d6 | |||
| 6f9d9f9e7a | |||
| db7f088b36 | |||
| 54d4cd7f99 | |||
| af03c23c9f | |||
| 68dd17f895 | |||
| 8249df15df |
@@ -37,3 +37,24 @@ jobs:
|
||||
echo "$ANSIBLE_VAULT_PASSWORD" > .vault_pass.txt
|
||||
ansible-playbook --vault-password-file .vault_pass.txt deploy-to-prod.yaml
|
||||
rm .vault_pass.txt
|
||||
|
||||
promote_stable:
|
||||
runs-on: self-hosted
|
||||
needs: deploy
|
||||
steps:
|
||||
- name: Promote master to stable
|
||||
env:
|
||||
SPEEDBB_REPO: ${{ vars.SPEEDBB_REPO }}
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
GITEA_ACTOR: ${{ gitea.actor }}
|
||||
run: |
|
||||
set -e
|
||||
REPO="$SPEEDBB_REPO"
|
||||
if [ -n "$GITEA_TOKEN" ]; then
|
||||
REPO=$(echo "$SPEEDBB_REPO" | sed "s#https://#https://${GITEA_ACTOR}:${GITEA_TOKEN}@#")
|
||||
fi
|
||||
git clone --quiet --depth=1 --branch=stable "$REPO" repo
|
||||
cd repo
|
||||
git fetch origin master
|
||||
git merge --ff-only FETCH_HEAD
|
||||
git push origin stable
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -22,12 +22,14 @@
|
||||
/public/build
|
||||
/public/hot
|
||||
/public/storage
|
||||
/public/custom
|
||||
/storage/app
|
||||
/storage/framework
|
||||
/storage/*.key
|
||||
/storage/pail
|
||||
/storage/framework/views/*.php
|
||||
/bootstrap/cache/*.php
|
||||
/custom
|
||||
/vendor
|
||||
Homestead.json
|
||||
Homestead.yaml
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-02-12
|
||||
- Refined ACP System tab with left navigation, section-specific requirements, and CLI PHP selector.
|
||||
- Added CLI PHP interpreter options (php, keyhelp-php-domain, custom) with KeyHelp guidance.
|
||||
- Updated CLI update tooling and automation notes (KeyHelp PHP handling, CI runner requirements).
|
||||
- Adjusted ACP layout and tab styling for better dark-mode readability and auto-sizing sidebars.
|
||||
- Added Custom top-level ACP tab and preserved /custom paths during in-app updates.
|
||||
|
||||
## 2026-02-10
|
||||
- Reshaped ACP System tab with left navigation and dedicated views (Overview, Live Update, CLI, CI/CD).
|
||||
- Moved system requirements table into the CI/CD view with refresh controls.
|
||||
|
||||
5
NOTES.md
5
NOTES.md
@@ -9,4 +9,7 @@ Progress (last 2 days):
|
||||
- Added coverage scripts and cleanup (tests for update/version flows, system update/status, attachments, forums, roles, ranks, settings, portal, etc.).
|
||||
- Hardened tests with fakes/mocks to cover error paths and edge cases.
|
||||
|
||||
TODO: Make PHP binary path configurable for updates if default PHP is outdated (ACP -> System).
|
||||
TODO: Make the PHP binary path configurable for updates if the default PHP is outdated (ACP -> System).
|
||||
CI/CD: Runner must have PHP 8.4+ as the default CLI interpreter.
|
||||
KeyHelp: `keyhelp-php-domain` can select the PHP version based on the domain of the script location.
|
||||
KeyHelp: `keyhelp-php-domain` is a Pro feature; on non-Pro setups we must fake the command.
|
||||
|
||||
@@ -21,12 +21,31 @@
|
||||
path: "{{ prod_base_dir }}/.env"
|
||||
register: env_file
|
||||
|
||||
- name: Ensure bootstrap cache directory exists
|
||||
file:
|
||||
path: "{{ prod_base_dir }}/bootstrap/cache"
|
||||
state: directory
|
||||
mode: "0775"
|
||||
|
||||
- name: Download and installs all libs and dependencies
|
||||
block:
|
||||
- name: Composer install
|
||||
community.general.composer:
|
||||
command: install
|
||||
arguments: --no-dev --optimize-autoloader
|
||||
working_dir: "{{ prod_base_dir }}"
|
||||
php_path: /usr/bin/keyhelp-php84
|
||||
rescue:
|
||||
- name: Debug package discovery
|
||||
shell: |
|
||||
keyhelp-php84 artisan package:discover -v --ansi 2>&1 | tail -n 200
|
||||
args:
|
||||
chdir: "{{ prod_base_dir }}"
|
||||
register: package_discover_debug
|
||||
- debug:
|
||||
var: package_discover_debug.stdout_lines
|
||||
- fail:
|
||||
msg: "Composer install failed; see package:discover output above."
|
||||
|
||||
- name: Install node_modules
|
||||
npm:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\Process\Process;
|
||||
@@ -16,9 +17,12 @@ class SystemStatusController extends Controller
|
||||
}
|
||||
|
||||
$phpDefaultPath = $this->resolveBinary('php');
|
||||
$phpSelectedPath = PHP_BINARY ?: $phpDefaultPath;
|
||||
$phpConfiguredPath = trim((string) Setting::where('key', 'system.php_binary')->value('value'));
|
||||
$phpSelectedPath = $phpConfiguredPath ?: (PHP_BINARY ?: $phpDefaultPath);
|
||||
$phpSelectedOk = (bool) $phpSelectedPath;
|
||||
$phpSelectedVersion = PHP_VERSION;
|
||||
$phpSelectedVersion = $phpSelectedPath
|
||||
? ($this->resolvePhpVersion($phpSelectedPath) ?? PHP_VERSION)
|
||||
: PHP_VERSION;
|
||||
$minVersions = $this->resolveMinVersions();
|
||||
$composerPath = $this->resolveBinary('composer');
|
||||
$nodePath = $this->resolveBinary('node');
|
||||
@@ -40,6 +44,7 @@ class SystemStatusController extends Controller
|
||||
return response()->json([
|
||||
'php' => PHP_VERSION,
|
||||
'php_default' => $phpDefaultPath,
|
||||
'php_configured' => $phpConfiguredPath ?: null,
|
||||
'php_selected_path' => $phpSelectedPath,
|
||||
'php_selected_ok' => $phpSelectedOk,
|
||||
'php_selected_version' => $phpSelectedVersion,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Setting;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\File;
|
||||
@@ -112,6 +113,7 @@ class SystemUpdateController extends Controller
|
||||
$append('Syncing files...');
|
||||
$usedRsync = false;
|
||||
$rsyncPath = trim((string) shell_exec('command -v rsync'));
|
||||
$protectedPaths = ['custom', 'public/custom'];
|
||||
if ($rsyncPath !== '') {
|
||||
$usedRsync = true;
|
||||
$rsync = new Process([
|
||||
@@ -121,6 +123,8 @@ class SystemUpdateController extends Controller
|
||||
'--exclude=.env',
|
||||
'--exclude=storage',
|
||||
'--exclude=public/storage',
|
||||
'--exclude=custom',
|
||||
'--exclude=public/custom',
|
||||
$sourceDir . '/',
|
||||
base_path() . '/',
|
||||
]);
|
||||
@@ -133,6 +137,15 @@ class SystemUpdateController extends Controller
|
||||
], 500);
|
||||
}
|
||||
} else {
|
||||
foreach ($protectedPaths as $path) {
|
||||
$sourcePath = $sourceDir . DIRECTORY_SEPARATOR . $path;
|
||||
if (File::exists($sourcePath)) {
|
||||
File::deleteDirectory($sourcePath);
|
||||
if (File::exists($sourcePath)) {
|
||||
File::delete($sourcePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
File::copyDirectory($sourceDir, base_path());
|
||||
}
|
||||
|
||||
@@ -169,7 +182,10 @@ class SystemUpdateController extends Controller
|
||||
], 500);
|
||||
}
|
||||
|
||||
$phpBinary = trim((string) Setting::where('key', 'system.php_binary')->value('value'));
|
||||
if ($phpBinary === '') {
|
||||
$phpBinary = env('SYSTEM_UPDATE_PHP_BINARY') ?: (PHP_BINARY ?: 'php');
|
||||
}
|
||||
$append("Running migrations (using {$phpBinary})...");
|
||||
$migrate = new Process([$phpBinary, 'artisan', 'migrate', '--force'], base_path());
|
||||
$migrate->setTimeout(600);
|
||||
|
||||
0
bootstrap/cache/.gitkeep
vendored
Normal file
0
bootstrap/cache/.gitkeep
vendored
Normal file
72
bootstrap/cache/packages.php
vendored
72
bootstrap/cache/packages.php
vendored
@@ -1,72 +0,0 @@
|
||||
<?php return array (
|
||||
'barryvdh/laravel-ide-helper' =>
|
||||
array (
|
||||
'providers' =>
|
||||
array (
|
||||
0 => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider',
|
||||
),
|
||||
),
|
||||
'laravel/fortify' =>
|
||||
array (
|
||||
'providers' =>
|
||||
array (
|
||||
0 => 'Laravel\\Fortify\\FortifyServiceProvider',
|
||||
),
|
||||
),
|
||||
'laravel/pail' =>
|
||||
array (
|
||||
'providers' =>
|
||||
array (
|
||||
0 => 'Laravel\\Pail\\PailServiceProvider',
|
||||
),
|
||||
),
|
||||
'laravel/sail' =>
|
||||
array (
|
||||
'providers' =>
|
||||
array (
|
||||
0 => 'Laravel\\Sail\\SailServiceProvider',
|
||||
),
|
||||
),
|
||||
'laravel/sanctum' =>
|
||||
array (
|
||||
'providers' =>
|
||||
array (
|
||||
0 => 'Laravel\\Sanctum\\SanctumServiceProvider',
|
||||
),
|
||||
),
|
||||
'laravel/tinker' =>
|
||||
array (
|
||||
'providers' =>
|
||||
array (
|
||||
0 => 'Laravel\\Tinker\\TinkerServiceProvider',
|
||||
),
|
||||
),
|
||||
'nesbot/carbon' =>
|
||||
array (
|
||||
'providers' =>
|
||||
array (
|
||||
0 => 'Carbon\\Laravel\\ServiceProvider',
|
||||
),
|
||||
),
|
||||
'nunomaduro/collision' =>
|
||||
array (
|
||||
'providers' =>
|
||||
array (
|
||||
0 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
|
||||
),
|
||||
),
|
||||
'nunomaduro/termwind' =>
|
||||
array (
|
||||
'providers' =>
|
||||
array (
|
||||
0 => 'Termwind\\Laravel\\TermwindServiceProvider',
|
||||
),
|
||||
),
|
||||
'pestphp/pest-plugin-laravel' =>
|
||||
array (
|
||||
'providers' =>
|
||||
array (
|
||||
0 => 'Pest\\Laravel\\PestServiceProvider',
|
||||
),
|
||||
),
|
||||
);
|
||||
277
bootstrap/cache/services.php
vendored
277
bootstrap/cache/services.php
vendored
@@ -1,277 +0,0 @@
|
||||
<?php return array (
|
||||
'providers' =>
|
||||
array (
|
||||
0 => 'Illuminate\\Auth\\AuthServiceProvider',
|
||||
1 => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
|
||||
2 => 'Illuminate\\Bus\\BusServiceProvider',
|
||||
3 => 'Illuminate\\Cache\\CacheServiceProvider',
|
||||
4 => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
5 => 'Illuminate\\Concurrency\\ConcurrencyServiceProvider',
|
||||
6 => 'Illuminate\\Cookie\\CookieServiceProvider',
|
||||
7 => 'Illuminate\\Database\\DatabaseServiceProvider',
|
||||
8 => 'Illuminate\\Encryption\\EncryptionServiceProvider',
|
||||
9 => 'Illuminate\\Filesystem\\FilesystemServiceProvider',
|
||||
10 => 'Illuminate\\Foundation\\Providers\\FoundationServiceProvider',
|
||||
11 => 'Illuminate\\Hashing\\HashServiceProvider',
|
||||
12 => 'Illuminate\\Mail\\MailServiceProvider',
|
||||
13 => 'Illuminate\\Notifications\\NotificationServiceProvider',
|
||||
14 => 'Illuminate\\Pagination\\PaginationServiceProvider',
|
||||
15 => 'Illuminate\\Auth\\Passwords\\PasswordResetServiceProvider',
|
||||
16 => 'Illuminate\\Pipeline\\PipelineServiceProvider',
|
||||
17 => 'Illuminate\\Queue\\QueueServiceProvider',
|
||||
18 => 'Illuminate\\Redis\\RedisServiceProvider',
|
||||
19 => 'Illuminate\\Session\\SessionServiceProvider',
|
||||
20 => 'Illuminate\\Translation\\TranslationServiceProvider',
|
||||
21 => 'Illuminate\\Validation\\ValidationServiceProvider',
|
||||
22 => 'Illuminate\\View\\ViewServiceProvider',
|
||||
23 => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider',
|
||||
24 => 'Laravel\\Fortify\\FortifyServiceProvider',
|
||||
25 => 'Laravel\\Pail\\PailServiceProvider',
|
||||
26 => 'Laravel\\Sail\\SailServiceProvider',
|
||||
27 => 'Laravel\\Sanctum\\SanctumServiceProvider',
|
||||
28 => 'Laravel\\Tinker\\TinkerServiceProvider',
|
||||
29 => 'Carbon\\Laravel\\ServiceProvider',
|
||||
30 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
|
||||
31 => 'Termwind\\Laravel\\TermwindServiceProvider',
|
||||
32 => 'Pest\\Laravel\\PestServiceProvider',
|
||||
33 => 'App\\Providers\\AppServiceProvider',
|
||||
34 => 'App\\Providers\\FortifyServiceProvider',
|
||||
),
|
||||
'eager' =>
|
||||
array (
|
||||
0 => 'Illuminate\\Auth\\AuthServiceProvider',
|
||||
1 => 'Illuminate\\Cookie\\CookieServiceProvider',
|
||||
2 => 'Illuminate\\Database\\DatabaseServiceProvider',
|
||||
3 => 'Illuminate\\Encryption\\EncryptionServiceProvider',
|
||||
4 => 'Illuminate\\Filesystem\\FilesystemServiceProvider',
|
||||
5 => 'Illuminate\\Foundation\\Providers\\FoundationServiceProvider',
|
||||
6 => 'Illuminate\\Notifications\\NotificationServiceProvider',
|
||||
7 => 'Illuminate\\Pagination\\PaginationServiceProvider',
|
||||
8 => 'Illuminate\\Session\\SessionServiceProvider',
|
||||
9 => 'Illuminate\\View\\ViewServiceProvider',
|
||||
10 => 'Laravel\\Fortify\\FortifyServiceProvider',
|
||||
11 => 'Laravel\\Pail\\PailServiceProvider',
|
||||
12 => 'Laravel\\Sanctum\\SanctumServiceProvider',
|
||||
13 => 'Carbon\\Laravel\\ServiceProvider',
|
||||
14 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
|
||||
15 => 'Termwind\\Laravel\\TermwindServiceProvider',
|
||||
16 => 'Pest\\Laravel\\PestServiceProvider',
|
||||
17 => 'App\\Providers\\AppServiceProvider',
|
||||
18 => 'App\\Providers\\FortifyServiceProvider',
|
||||
),
|
||||
'deferred' =>
|
||||
array (
|
||||
'Illuminate\\Broadcasting\\BroadcastManager' => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
|
||||
'Illuminate\\Contracts\\Broadcasting\\Factory' => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
|
||||
'Illuminate\\Contracts\\Broadcasting\\Broadcaster' => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
|
||||
'Illuminate\\Bus\\Dispatcher' => 'Illuminate\\Bus\\BusServiceProvider',
|
||||
'Illuminate\\Contracts\\Bus\\Dispatcher' => 'Illuminate\\Bus\\BusServiceProvider',
|
||||
'Illuminate\\Contracts\\Bus\\QueueingDispatcher' => 'Illuminate\\Bus\\BusServiceProvider',
|
||||
'Illuminate\\Bus\\BatchRepository' => 'Illuminate\\Bus\\BusServiceProvider',
|
||||
'Illuminate\\Bus\\DatabaseBatchRepository' => 'Illuminate\\Bus\\BusServiceProvider',
|
||||
'cache' => 'Illuminate\\Cache\\CacheServiceProvider',
|
||||
'cache.store' => 'Illuminate\\Cache\\CacheServiceProvider',
|
||||
'cache.psr6' => 'Illuminate\\Cache\\CacheServiceProvider',
|
||||
'memcached.connector' => 'Illuminate\\Cache\\CacheServiceProvider',
|
||||
'Illuminate\\Cache\\RateLimiter' => 'Illuminate\\Cache\\CacheServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\AboutCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Cache\\Console\\ClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Cache\\Console\\ForgetCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ClearCompiledCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Auth\\Console\\ClearResetsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ConfigCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ConfigClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ConfigShowCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Database\\Console\\DbCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Database\\Console\\MonitorCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Database\\Console\\PruneCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Database\\Console\\ShowCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Database\\Console\\TableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Database\\Console\\WipeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\DownCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\EnvironmentCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\EnvironmentDecryptCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\EnvironmentEncryptCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\EventCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\EventClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\EventListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Concurrency\\Console\\InvokeSerializedClosureCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\KeyGenerateCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\OptimizeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\OptimizeClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\PackageDiscoverCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Cache\\Console\\PruneStaleTagsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Queue\\Console\\ClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Queue\\Console\\ListFailedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Queue\\Console\\FlushFailedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Queue\\Console\\ForgetFailedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Queue\\Console\\ListenCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Queue\\Console\\MonitorCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Queue\\Console\\PauseCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Queue\\Console\\PruneBatchesCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Queue\\Console\\PruneFailedJobsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Queue\\Console\\RestartCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Queue\\Console\\ResumeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Queue\\Console\\RetryCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Queue\\Console\\RetryBatchCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Queue\\Console\\WorkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ReloadCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\RouteCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\RouteClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\RouteListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Database\\Console\\DumpCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Database\\Console\\Seeds\\SeedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Console\\Scheduling\\ScheduleFinishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Console\\Scheduling\\ScheduleListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Console\\Scheduling\\ScheduleRunCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Console\\Scheduling\\ScheduleClearCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Console\\Scheduling\\ScheduleTestCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Console\\Scheduling\\ScheduleWorkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Console\\Scheduling\\ScheduleInterruptCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Database\\Console\\ShowModelCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\StorageLinkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\StorageUnlinkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\UpCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ViewCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ViewClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ApiInstallCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\BroadcastingInstallCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Cache\\Console\\CacheTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\CastMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ChannelListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ChannelMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ClassMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ComponentMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ConfigMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ConfigPublishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ConsoleMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Routing\\Console\\ControllerMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\DocsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\EnumMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\EventGenerateCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\EventMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ExceptionMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Database\\Console\\Factories\\FactoryMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\InterfaceMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\JobMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\JobMiddlewareMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\LangPublishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ListenerMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\MailMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Routing\\Console\\MiddlewareMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ModelMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\NotificationMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Notifications\\Console\\NotificationTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ObserverMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\PolicyMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ProviderMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Queue\\Console\\FailedTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Queue\\Console\\TableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Queue\\Console\\BatchesTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\RequestMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ResourceMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\RuleMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ScopeMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Database\\Console\\Seeds\\SeederMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Session\\Console\\SessionTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ServeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\StubPublishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\TestMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\TraitMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\VendorPublishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Foundation\\Console\\ViewMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'migrator' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'migration.repository' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'migration.creator' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Database\\Migrations\\Migrator' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Database\\Console\\Migrations\\MigrateCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Database\\Console\\Migrations\\FreshCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Database\\Console\\Migrations\\InstallCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Database\\Console\\Migrations\\RefreshCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Database\\Console\\Migrations\\ResetCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Database\\Console\\Migrations\\RollbackCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Database\\Console\\Migrations\\StatusCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Database\\Console\\Migrations\\MigrateMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'composer' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
|
||||
'Illuminate\\Concurrency\\ConcurrencyManager' => 'Illuminate\\Concurrency\\ConcurrencyServiceProvider',
|
||||
'hash' => 'Illuminate\\Hashing\\HashServiceProvider',
|
||||
'hash.driver' => 'Illuminate\\Hashing\\HashServiceProvider',
|
||||
'mail.manager' => 'Illuminate\\Mail\\MailServiceProvider',
|
||||
'mailer' => 'Illuminate\\Mail\\MailServiceProvider',
|
||||
'Illuminate\\Mail\\Markdown' => 'Illuminate\\Mail\\MailServiceProvider',
|
||||
'auth.password' => 'Illuminate\\Auth\\Passwords\\PasswordResetServiceProvider',
|
||||
'auth.password.broker' => 'Illuminate\\Auth\\Passwords\\PasswordResetServiceProvider',
|
||||
'Illuminate\\Contracts\\Pipeline\\Hub' => 'Illuminate\\Pipeline\\PipelineServiceProvider',
|
||||
'pipeline' => 'Illuminate\\Pipeline\\PipelineServiceProvider',
|
||||
'queue' => 'Illuminate\\Queue\\QueueServiceProvider',
|
||||
'queue.connection' => 'Illuminate\\Queue\\QueueServiceProvider',
|
||||
'queue.failer' => 'Illuminate\\Queue\\QueueServiceProvider',
|
||||
'queue.listener' => 'Illuminate\\Queue\\QueueServiceProvider',
|
||||
'queue.worker' => 'Illuminate\\Queue\\QueueServiceProvider',
|
||||
'redis' => 'Illuminate\\Redis\\RedisServiceProvider',
|
||||
'redis.connection' => 'Illuminate\\Redis\\RedisServiceProvider',
|
||||
'translator' => 'Illuminate\\Translation\\TranslationServiceProvider',
|
||||
'translation.loader' => 'Illuminate\\Translation\\TranslationServiceProvider',
|
||||
'validator' => 'Illuminate\\Validation\\ValidationServiceProvider',
|
||||
'validation.presence' => 'Illuminate\\Validation\\ValidationServiceProvider',
|
||||
'Illuminate\\Contracts\\Validation\\UncompromisedVerifier' => 'Illuminate\\Validation\\ValidationServiceProvider',
|
||||
'Barryvdh\\LaravelIdeHelper\\Console\\GeneratorCommand' => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider',
|
||||
'Barryvdh\\LaravelIdeHelper\\Console\\ModelsCommand' => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider',
|
||||
'Barryvdh\\LaravelIdeHelper\\Console\\MetaCommand' => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider',
|
||||
'Barryvdh\\LaravelIdeHelper\\Console\\EloquentCommand' => 'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider',
|
||||
'Laravel\\Sail\\Console\\InstallCommand' => 'Laravel\\Sail\\SailServiceProvider',
|
||||
'Laravel\\Sail\\Console\\PublishCommand' => 'Laravel\\Sail\\SailServiceProvider',
|
||||
'command.tinker' => 'Laravel\\Tinker\\TinkerServiceProvider',
|
||||
),
|
||||
'when' =>
|
||||
array (
|
||||
'Illuminate\\Broadcasting\\BroadcastServiceProvider' =>
|
||||
array (
|
||||
),
|
||||
'Illuminate\\Bus\\BusServiceProvider' =>
|
||||
array (
|
||||
),
|
||||
'Illuminate\\Cache\\CacheServiceProvider' =>
|
||||
array (
|
||||
),
|
||||
'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider' =>
|
||||
array (
|
||||
),
|
||||
'Illuminate\\Concurrency\\ConcurrencyServiceProvider' =>
|
||||
array (
|
||||
),
|
||||
'Illuminate\\Hashing\\HashServiceProvider' =>
|
||||
array (
|
||||
),
|
||||
'Illuminate\\Mail\\MailServiceProvider' =>
|
||||
array (
|
||||
),
|
||||
'Illuminate\\Auth\\Passwords\\PasswordResetServiceProvider' =>
|
||||
array (
|
||||
),
|
||||
'Illuminate\\Pipeline\\PipelineServiceProvider' =>
|
||||
array (
|
||||
),
|
||||
'Illuminate\\Queue\\QueueServiceProvider' =>
|
||||
array (
|
||||
),
|
||||
'Illuminate\\Redis\\RedisServiceProvider' =>
|
||||
array (
|
||||
),
|
||||
'Illuminate\\Translation\\TranslationServiceProvider' =>
|
||||
array (
|
||||
),
|
||||
'Illuminate\\Validation\\ValidationServiceProvider' =>
|
||||
array (
|
||||
),
|
||||
'Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider' =>
|
||||
array (
|
||||
),
|
||||
'Laravel\\Sail\\SailServiceProvider' =>
|
||||
array (
|
||||
),
|
||||
'Laravel\\Tinker\\TinkerServiceProvider' =>
|
||||
array (
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -98,5 +98,5 @@
|
||||
"minimum-stability": "stable",
|
||||
"prefer-stable": true,
|
||||
"version": "26.0.2",
|
||||
"build": "45"
|
||||
"build": "72"
|
||||
}
|
||||
|
||||
2
composer.lock
generated
2
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "83b577a02e99a4e17696941851d13cc2",
|
||||
"content-hash": "e6076a6989b155fddbc675cab28fdd50",
|
||||
"packages": [
|
||||
{
|
||||
"name": "bacon/bacon-qr-code",
|
||||
|
||||
178
git_update.sh
178
git_update.sh
@@ -1,13 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
# shellcheck disable=SC2016
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
if [[ -n "$(git status --porcelain)" ]]; then
|
||||
git restore -q bootstrap/cache/packages.php bootstrap/cache/services.php 2>/dev/null || true
|
||||
DIRTY="$(git status --porcelain)"
|
||||
DIRTY_FILTERED="$(echo "$DIRTY" | grep -vE '^( M|M ) (bootstrap/cache/(packages|services)\.php|package-lock\.json)$' || true)"
|
||||
if [[ -n "$DIRTY_FILTERED" ]]; then
|
||||
echo "Working tree is dirty. Please commit or stash changes before updating."
|
||||
echo "$DIRTY_FILTERED"
|
||||
exit 1
|
||||
fi
|
||||
if echo "$DIRTY" | grep -qE 'package-lock\.json'; then
|
||||
echo "Warning: package-lock.json is modified. Continuing anyway."
|
||||
fi
|
||||
|
||||
echo "Fetching latest refs..."
|
||||
git fetch --prune --tags
|
||||
@@ -18,8 +26,111 @@ git checkout stable
|
||||
echo "Pulling latest stable..."
|
||||
git pull --ff-only
|
||||
|
||||
resolve_php_bin() {
|
||||
if [[ -n "${PHP_BIN:-}" ]]; then
|
||||
echo "$PHP_BIN"
|
||||
return
|
||||
fi
|
||||
if command -v keyhelp-php84 >/dev/null 2>&1; then
|
||||
echo "keyhelp-php84"
|
||||
return
|
||||
fi
|
||||
if command -v php >/dev/null 2>&1; then
|
||||
echo "php"
|
||||
return
|
||||
fi
|
||||
echo "php"
|
||||
}
|
||||
|
||||
resolve_configured_php_bin() {
|
||||
local configured="${1:-}"
|
||||
local current="${2:-php}"
|
||||
local trimmed="$configured"
|
||||
trimmed="${trimmed#"${trimmed%%[![:space:]]*}"}"
|
||||
trimmed="${trimmed%"${trimmed##*[![:space:]]}"}"
|
||||
|
||||
if [[ -z "$trimmed" ]]; then
|
||||
echo "$current"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$trimmed" == "keyhelp-php-domain" ]]; then
|
||||
if command -v keyhelp-php-domain >/dev/null 2>&1; then
|
||||
echo "keyhelp-php-domain"
|
||||
return
|
||||
fi
|
||||
if [[ -x "/usr/bin/keyhelp-php-domain" ]]; then
|
||||
echo "/usr/bin/keyhelp-php-domain"
|
||||
return
|
||||
fi
|
||||
if [[ -x "/usr/local/bin/keyhelp-php-domain" ]]; then
|
||||
echo "/usr/local/bin/keyhelp-php-domain"
|
||||
return
|
||||
fi
|
||||
echo "Configured PHP binary 'keyhelp-php-domain' was not found." >&2
|
||||
echo "Set ACP -> System -> CLI to a working custom binary (e.g. keyhelp-php84)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if command -v "$trimmed" >/dev/null 2>&1; then
|
||||
echo "$trimmed"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$trimmed" == */* && -x "$trimmed" ]]; then
|
||||
echo "$trimmed"
|
||||
return
|
||||
fi
|
||||
|
||||
echo "Configured PHP binary '$trimmed' is not executable/resolvable." >&2
|
||||
echo "Set ACP -> System -> CLI to a valid command or absolute executable path." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
read_setting_php_bin() {
|
||||
if [[ ! -f artisan ]]; then
|
||||
echo ""
|
||||
return 0
|
||||
fi
|
||||
echo "Running with PHP binary: $PHP_BIN -r <read system.php_binary>" >&2
|
||||
"$PHP_BIN" -r '
|
||||
require "vendor/autoload.php";
|
||||
$app = require "bootstrap/app.php";
|
||||
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
|
||||
$value = (string) \App\Models\Setting::where("key", "system.php_binary")->value("value");
|
||||
echo trim($value);
|
||||
'
|
||||
}
|
||||
|
||||
PHP_BIN="$(resolve_php_bin)"
|
||||
echo "Resolved PHP binary: $PHP_BIN"
|
||||
if command -v "$PHP_BIN" >/dev/null 2>&1; then
|
||||
echo "PHP version ($PHP_BIN): $($PHP_BIN -v | head -n 1)"
|
||||
else
|
||||
echo "PHP binary '$PHP_BIN' not found in PATH."
|
||||
fi
|
||||
|
||||
echo "Installing PHP dependencies..."
|
||||
composer install --no-dev --optimize-autoloader
|
||||
COMPOSER_BIN="$(command -v composer || true)"
|
||||
if [[ -z "$COMPOSER_BIN" ]]; then
|
||||
echo "Composer not found in PATH."
|
||||
exit 1
|
||||
fi
|
||||
echo "Running with PHP binary: $PHP_BIN $COMPOSER_BIN install --no-dev --optimize-autoloader"
|
||||
"$PHP_BIN" "$COMPOSER_BIN" install --no-dev --optimize-autoloader
|
||||
|
||||
if ! CONFIGURED_PHP="$(read_setting_php_bin)"; then
|
||||
echo "Failed to read configured PHP binary from settings." >&2
|
||||
echo "Aborting to avoid running update with the wrong PHP binary." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Configured PHP binary from settings: ${CONFIGURED_PHP:-<empty>}"
|
||||
PHP_BIN="$(resolve_configured_php_bin "$CONFIGURED_PHP" "$PHP_BIN")"
|
||||
|
||||
echo "Final PHP binary: $PHP_BIN"
|
||||
if command -v "$PHP_BIN" >/dev/null 2>&1; then
|
||||
echo "Final PHP version ($PHP_BIN): $($PHP_BIN -v | head -n 1)"
|
||||
fi
|
||||
|
||||
echo "Installing JS dependencies..."
|
||||
npm install
|
||||
@@ -27,21 +138,62 @@ npm install
|
||||
echo "Building assets..."
|
||||
npm run build
|
||||
|
||||
PHP_BIN="${PHP_BIN:-php}"
|
||||
|
||||
echo "Running migrations..."
|
||||
$PHP_BIN artisan migrate --force
|
||||
echo "Running with PHP binary: $PHP_BIN artisan migrate --force"
|
||||
"$PHP_BIN" artisan migrate --force
|
||||
|
||||
echo "Syncing version/build to settings..."
|
||||
VERSION="$($PHP_BIN -r '$c=json_decode(file_get_contents("composer.json"), true); echo $c["version"] ?? "";')"
|
||||
BUILD="$($PHP_BIN -r '$c=json_decode(file_get_contents("composer.json"), true); echo $c["build"] ?? "";')"
|
||||
echo "Running with PHP binary: $PHP_BIN -r <read composer.json version>"
|
||||
VERSION="$("$PHP_BIN" -r '$c=json_decode(file_get_contents("composer.json"), true); echo $c["version"] ?? "";')"
|
||||
echo "Running with PHP binary: $PHP_BIN -r <read composer.json build>"
|
||||
BUILD="$("$PHP_BIN" -r '$c=json_decode(file_get_contents("composer.json"), true); echo $c["build"] ?? "";')"
|
||||
echo "Computed from composer.json: VERSION=$VERSION, BUILD=$BUILD"
|
||||
|
||||
if [[ -n "$VERSION" ]]; then
|
||||
$PHP_BIN artisan tinker --execute="\\App\\Models\\Setting::updateOrCreate(['key'=>'version'], ['value'=>'$VERSION']);"
|
||||
fi
|
||||
|
||||
if [[ -n "$BUILD" ]]; then
|
||||
$PHP_BIN artisan tinker --execute="\\App\\Models\\Setting::updateOrCreate(['key'=>'build'], ['value'=>'$BUILD']);"
|
||||
if [[ -n "$VERSION" || -n "$BUILD" ]]; then
|
||||
echo "Updating settings version/build (VERSION=$VERSION, BUILD=$BUILD)..."
|
||||
echo "Running with PHP binary: $PHP_BIN -r <write settings version/build>"
|
||||
SPEEDBB_VERSION="$VERSION" SPEEDBB_BUILD="$BUILD" "$PHP_BIN" -r '
|
||||
require "vendor/autoload.php";
|
||||
$app = require "bootstrap/app.php";
|
||||
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
|
||||
$version = getenv("SPEEDBB_VERSION");
|
||||
$build = getenv("SPEEDBB_BUILD");
|
||||
if ($version !== false && $version !== "") {
|
||||
\Illuminate\Support\Facades\DB::table("settings")->upsert(
|
||||
[[
|
||||
"key" => "version",
|
||||
"value" => $version,
|
||||
"created_at" => now(),
|
||||
"updated_at" => now(),
|
||||
]],
|
||||
["key"],
|
||||
["value", "updated_at"]
|
||||
);
|
||||
echo "Upserted version setting.\n";
|
||||
}
|
||||
if ($build !== false && $build !== "") {
|
||||
\Illuminate\Support\Facades\DB::table("settings")->upsert(
|
||||
[[
|
||||
"key" => "build",
|
||||
"value" => $build,
|
||||
"created_at" => now(),
|
||||
"updated_at" => now(),
|
||||
]],
|
||||
["key"],
|
||||
["value", "updated_at"]
|
||||
);
|
||||
echo "Upserted build setting.\n";
|
||||
}
|
||||
' \
|
||||
&& echo "Running with PHP binary: $PHP_BIN -r <verify settings version/build>" \
|
||||
&& "$PHP_BIN" -r '
|
||||
require "vendor/autoload.php";
|
||||
$app = require "bootstrap/app.php";
|
||||
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
|
||||
$version = \App\Models\Setting::where("key", "version")->value("value");
|
||||
$build = \App\Models\Setting::where("key", "build")->value("value");
|
||||
echo "Settings now: version={$version}, build={$build}\n";
|
||||
'
|
||||
fi
|
||||
|
||||
echo "Update complete."
|
||||
|
||||
@@ -862,8 +862,8 @@ a {
|
||||
}
|
||||
|
||||
[data-bs-theme="dark"] {
|
||||
--bb-ink: #e6e8eb;
|
||||
--bb-ink-muted: #9aa4b2;
|
||||
--bb-ink: #aaaeb4;
|
||||
--bb-ink-muted: #6b7483;
|
||||
--bb-border: #2a2f3a;
|
||||
--bb-page-bg: radial-gradient(circle at 10% 20%, #141823 0%, #10131a 45%, #0b0e14 100%);
|
||||
}
|
||||
@@ -922,10 +922,18 @@ a {
|
||||
|
||||
.nav-tabs .nav-link {
|
||||
color: var(--bb-accent, #f29b3f);
|
||||
border: 1px solid var(--bb-border);
|
||||
border-bottom-color: transparent;
|
||||
border-radius: 10px 10px 0 0;
|
||||
margin-right: 0.35rem;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link.active {
|
||||
color: inherit;
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border-color: var(--bb-border);
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
|
||||
.bb-version {
|
||||
|
||||
@@ -201,6 +201,12 @@ function Acp({ isAdmin }) {
|
||||
favicon_128: '',
|
||||
favicon_256: '',
|
||||
})
|
||||
const [systemCliSettings, setSystemCliSettings] = useState({
|
||||
php_mode: 'php',
|
||||
php_custom: '',
|
||||
})
|
||||
const [systemCliSaving, setSystemCliSaving] = useState(false)
|
||||
const [systemCliError, setSystemCliError] = useState('')
|
||||
const settingsDetailMap = {
|
||||
forum_name: 'forumName',
|
||||
default_theme: 'defaultTheme',
|
||||
@@ -292,6 +298,16 @@ function Acp({ isAdmin }) {
|
||||
favicon_256: settingsMap.get('favicon_256') || '',
|
||||
}
|
||||
setGeneralSettings(next)
|
||||
const configuredPhp = settingsMap.get('system.php_binary') || ''
|
||||
const phpMode = configuredPhp === 'keyhelp-php-domain'
|
||||
? 'keyhelp'
|
||||
: configuredPhp === '' || configuredPhp === 'php'
|
||||
? 'php'
|
||||
: 'custom'
|
||||
setSystemCliSettings({
|
||||
php_mode: phpMode,
|
||||
php_custom: phpMode === 'custom' ? configuredPhp : '',
|
||||
})
|
||||
setAttachmentSettings({
|
||||
display_images_inline: settingsMap.get('attachments.display_images_inline') || 'true',
|
||||
create_thumbnails: settingsMap.get('attachments.create_thumbnails') || 'true',
|
||||
@@ -373,6 +389,135 @@ function Acp({ isAdmin }) {
|
||||
}
|
||||
}
|
||||
|
||||
const handleSystemCliSave = async (event) => {
|
||||
event.preventDefault()
|
||||
setSystemCliSaving(true)
|
||||
setSystemCliError('')
|
||||
try {
|
||||
let value = ''
|
||||
if (systemCliSettings.php_mode === 'custom') {
|
||||
value = typeof systemCliSettings.php_custom === 'string'
|
||||
? systemCliSettings.php_custom.trim()
|
||||
: String(systemCliSettings.php_custom ?? '')
|
||||
} else if (systemCliSettings.php_mode === 'keyhelp') {
|
||||
value = 'keyhelp-php-domain'
|
||||
} else {
|
||||
value = 'php'
|
||||
}
|
||||
await saveSetting('system.php_binary', value)
|
||||
setSystemCliSettings((prev) => ({
|
||||
...prev,
|
||||
php_mode: systemCliSettings.php_mode,
|
||||
php_custom: systemCliSettings.php_mode === 'custom' ? value : '',
|
||||
}))
|
||||
} catch (err) {
|
||||
setSystemCliError(err.message)
|
||||
} finally {
|
||||
setSystemCliSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
const systemChecks = useMemo(() => {
|
||||
if (!systemStatus) return []
|
||||
return [
|
||||
{
|
||||
id: 'php',
|
||||
label: 'PHP',
|
||||
path: systemStatus.php_selected_path || '—',
|
||||
min: systemStatus.min_versions?.php || '—',
|
||||
current: systemStatus.php_selected_version || '—',
|
||||
status: systemStatus.php_selected_ok ? 'ok' : 'bad',
|
||||
},
|
||||
{
|
||||
id: 'composer',
|
||||
label: 'Composer',
|
||||
path: systemStatus.composer || t('system.not_found'),
|
||||
min: systemStatus.min_versions?.composer || '—',
|
||||
current: systemStatus.composer_version || '—',
|
||||
status: systemStatus.composer ? 'ok' : 'bad',
|
||||
},
|
||||
{
|
||||
id: 'node',
|
||||
label: 'Node',
|
||||
path: systemStatus.node || t('system.not_found'),
|
||||
min: systemStatus.min_versions?.node || '—',
|
||||
current: systemStatus.node_version || '—',
|
||||
status: systemStatus.node ? 'ok' : 'bad',
|
||||
},
|
||||
{
|
||||
id: 'npm',
|
||||
label: 'npm',
|
||||
path: systemStatus.npm || t('system.not_found'),
|
||||
min: systemStatus.min_versions?.npm || '—',
|
||||
current: systemStatus.npm_version || '—',
|
||||
status: systemStatus.npm ? 'ok' : 'bad',
|
||||
},
|
||||
{
|
||||
id: 'tar',
|
||||
label: 'tar',
|
||||
path: systemStatus.tar || t('system.not_found'),
|
||||
min: '—',
|
||||
current: systemStatus.tar_version || '—',
|
||||
status: systemStatus.tar ? 'ok' : 'bad',
|
||||
},
|
||||
{
|
||||
id: 'rsync',
|
||||
label: 'rsync',
|
||||
path: systemStatus.rsync || t('system.not_found'),
|
||||
min: '—',
|
||||
current: systemStatus.rsync_version || '—',
|
||||
status: systemStatus.rsync ? 'ok' : 'bad',
|
||||
},
|
||||
{
|
||||
id: 'proc',
|
||||
label: 'proc_* functions',
|
||||
path: systemStatus.proc_functions
|
||||
? Object.entries(systemStatus.proc_functions)
|
||||
.filter(([, ok]) => !ok)
|
||||
.map(([name]) => name)
|
||||
.join(', ')
|
||||
: '—',
|
||||
min: '—',
|
||||
current: '—',
|
||||
note: 'Optional. Needed for automated version checks.',
|
||||
status:
|
||||
Boolean(systemStatus.proc_functions) &&
|
||||
Object.values(systemStatus.proc_functions).every(Boolean)
|
||||
? 'ok'
|
||||
: 'bad',
|
||||
pathColSpan: 3,
|
||||
},
|
||||
{
|
||||
id: 'storage',
|
||||
label: t('system.storage_writable'),
|
||||
path: 'storage/',
|
||||
min: '—',
|
||||
current: '—',
|
||||
status: systemStatus.storage_writable ? 'ok' : 'bad',
|
||||
},
|
||||
{
|
||||
id: 'updates',
|
||||
label: t('system.updates_writable'),
|
||||
path: 'storage/app/updates',
|
||||
min: '—',
|
||||
current: '—',
|
||||
status: systemStatus.updates_writable ? 'ok' : 'bad',
|
||||
},
|
||||
]
|
||||
}, [systemStatus, t])
|
||||
|
||||
const visibleSystemChecks = useMemo(() => {
|
||||
const visibilityBySection = {
|
||||
insite: ['php', 'proc', 'storage', 'updates'],
|
||||
cli: ['php', 'composer', 'node', 'npm', 'proc', 'storage', 'updates'],
|
||||
ci: ['php', 'composer', 'node', 'npm', 'tar', 'rsync', 'proc', 'storage', 'updates'],
|
||||
info: [],
|
||||
}
|
||||
const allowed = new Set(visibilityBySection[systemSection] || [])
|
||||
return systemChecks.filter((check) => allowed.has(check.id))
|
||||
}, [systemChecks, systemSection])
|
||||
|
||||
|
||||
const handleLogoUpload = async (file, settingKey) => {
|
||||
if (!file) return
|
||||
setGeneralUploading(true)
|
||||
@@ -823,6 +968,74 @@ function Acp({ isAdmin }) {
|
||||
}
|
||||
}
|
||||
|
||||
function renderSystemRequirementsPanel() {
|
||||
return (
|
||||
<div className="bb-acp-panel">
|
||||
<div className="bb-acp-panel-header">
|
||||
<div className="d-flex align-items-center justify-content-between">
|
||||
<h5 className="mb-0">{t('system.requirements')}</h5>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="dark"
|
||||
onClick={loadSystemStatus}
|
||||
disabled={systemLoading}
|
||||
>
|
||||
{t('acp.refresh')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bb-acp-panel-body">
|
||||
{!systemStatus && <p className="bb-muted mb-0">{t('system.not_found')}</p>}
|
||||
{systemStatus && (
|
||||
<table className="bb-acp-stats-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{t('system.check')}</th>
|
||||
<th>{t('system.path')}</th>
|
||||
<th>{t('system.min_version')}</th>
|
||||
<th>{t('system.current_version')}</th>
|
||||
<th>{t('system.status')}</th>
|
||||
<th>{t('system.recheck')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{visibleSystemChecks.map((check) => (
|
||||
<tr key={check.id}>
|
||||
<td>{check.label}</td>
|
||||
<td className="bb-acp-stats-value text-start" colSpan={check.pathColSpan || 1}>
|
||||
{check.path}
|
||||
{check.note && <div className="bb-muted mt-1 text-center">{check.note}</div>}
|
||||
</td>
|
||||
{!check.pathColSpan && (
|
||||
<>
|
||||
<td className="bb-acp-stats-value">{check.min}</td>
|
||||
<td className="bb-acp-stats-value">{check.current}</td>
|
||||
</>
|
||||
)}
|
||||
<td className="bb-acp-stats-value">
|
||||
<StatusIcon status={check.status} />
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="dark"
|
||||
onClick={loadSystemStatus}
|
||||
disabled={systemLoading}
|
||||
>
|
||||
{t('system.recheck')}
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isAdmin) {
|
||||
handleVersionCheck()
|
||||
@@ -2517,7 +2730,7 @@ function Acp({ isAdmin }) {
|
||||
<Tabs defaultActiveKey="general" className="mb-3">
|
||||
<Tab eventKey="general" title={t('acp.general')}>
|
||||
<Row className="g-4">
|
||||
<Col lg={3} xl={2}>
|
||||
<Col xs={12} lg="auto">
|
||||
<div className="bb-acp-sidebar">
|
||||
<div className="bb-acp-sidebar-section">
|
||||
<div className="bb-acp-sidebar-title">{t('acp.quick_access')}</div>
|
||||
@@ -2577,13 +2790,7 @@ function Acp({ isAdmin }) {
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
<Col lg={9} xl={10}>
|
||||
<div className="bb-acp-panel mb-4">
|
||||
<div className="bb-acp-panel-header">
|
||||
<h5 className="mb-1">{t('acp.welcome_title')}</h5>
|
||||
<p className="bb-muted mb-0">{t('acp.general_hint')}</p>
|
||||
</div>
|
||||
</div>
|
||||
<Col xs={12} lg>
|
||||
<div className="bb-acp-panel mb-4">
|
||||
<div className="bb-acp-panel-header">
|
||||
<div className="d-flex align-items-center justify-content-between">
|
||||
@@ -3510,7 +3717,7 @@ function Acp({ isAdmin }) {
|
||||
{systemLoading && <p className="bb-muted">{t('acp.loading')}</p>}
|
||||
{!systemLoading && (
|
||||
<Row className="g-4">
|
||||
<Col lg={3} xl={2}>
|
||||
<Col xs={12} lg="auto">
|
||||
<div className="bb-acp-sidebar">
|
||||
<div className="bb-acp-sidebar-section">
|
||||
<div className="bb-acp-sidebar-title">{t('acp.system')}</div>
|
||||
@@ -3555,7 +3762,7 @@ function Acp({ isAdmin }) {
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
<Col lg={9} xl={10}>
|
||||
<Col xs={12} lg>
|
||||
{systemSection === 'info' && (
|
||||
<div className="bb-acp-panel">
|
||||
<div className="bb-acp-panel-header">
|
||||
@@ -3570,17 +3777,14 @@ function Acp({ isAdmin }) {
|
||||
</div>
|
||||
)}
|
||||
{systemSection === 'insite' && (
|
||||
<div className="bb-acp-panel">
|
||||
<div className="bb-acp-panel-header">
|
||||
<h5 className="mb-0">Live Update</h5>
|
||||
</div>
|
||||
<>
|
||||
<div className="bb-acp-panel mb-3">
|
||||
<div className="bb-acp-panel-body">
|
||||
<p className="bb-muted mb-0">
|
||||
Placeholder: run a live update from inside the forum, with safety checks
|
||||
and status details.
|
||||
</p>
|
||||
<p className="bb-muted mb-0">Live update controls will appear here.</p>
|
||||
</div>
|
||||
</div>
|
||||
{renderSystemRequirementsPanel()}
|
||||
</>
|
||||
)}
|
||||
{systemSection === 'cli' && (
|
||||
<div className="bb-acp-panel">
|
||||
@@ -3588,274 +3792,87 @@ function Acp({ isAdmin }) {
|
||||
<h5 className="mb-0">CLI</h5>
|
||||
</div>
|
||||
<div className="bb-acp-panel-body">
|
||||
<p className="bb-muted mb-0">
|
||||
Placeholder: CLI upgrade commands and automation helpers will live here.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{systemSection === 'ci' && (
|
||||
<div className="bb-acp-panel">
|
||||
<div className="bb-acp-panel-header">
|
||||
<div className="d-flex align-items-center justify-content-between">
|
||||
<h5 className="mb-0">{t('system.requirements')}</h5>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="dark"
|
||||
onClick={loadSystemStatus}
|
||||
disabled={systemLoading}
|
||||
{systemCliError && <p className="text-danger">{systemCliError}</p>}
|
||||
<Form onSubmit={handleSystemCliSave}>
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>PHP interpreter</Form.Label>
|
||||
<Form.Select
|
||||
className="mb-2"
|
||||
value={systemCliSettings.php_mode}
|
||||
onChange={(event) =>
|
||||
setSystemCliSettings((prev) => ({
|
||||
...prev,
|
||||
php_mode: event.target.value,
|
||||
}))
|
||||
}
|
||||
>
|
||||
{t('acp.refresh')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bb-acp-panel-body">
|
||||
{!systemStatus && (
|
||||
<p className="bb-muted mb-0">
|
||||
{t('system.not_found')}
|
||||
</p>
|
||||
)}
|
||||
{systemStatus && (
|
||||
<table className="bb-acp-stats-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{t('system.check')}</th>
|
||||
<th>{t('system.path')}</th>
|
||||
<th>{t('system.min_version')}</th>
|
||||
<th>{t('system.current_version')}</th>
|
||||
<th>{t('system.status')}</th>
|
||||
<th>{t('system.recheck')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>PHP</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
{systemStatus.php_selected_path || '—'}
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
{systemStatus.min_versions?.php || '—'}
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
{systemStatus.php_selected_version || '—'}
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
<StatusIcon
|
||||
status={systemStatus.php_selected_ok ? 'ok' : 'bad'}
|
||||
/>
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="dark"
|
||||
onClick={loadSystemStatus}
|
||||
disabled={systemLoading}
|
||||
>
|
||||
{t('system.recheck')}
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Composer</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
{systemStatus.composer || t('system.not_found')}
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
{systemStatus.min_versions?.composer || '—'}
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
{systemStatus.composer_version || '—'}
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
<StatusIcon status={systemStatus.composer ? 'ok' : 'bad'} />
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="dark"
|
||||
onClick={loadSystemStatus}
|
||||
disabled={systemLoading}
|
||||
>
|
||||
{t('system.recheck')}
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Node</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
{systemStatus.node || t('system.not_found')}
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
{systemStatus.min_versions?.node || '—'}
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
{systemStatus.node_version || '—'}
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
<StatusIcon status={systemStatus.node ? 'ok' : 'bad'} />
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="dark"
|
||||
onClick={loadSystemStatus}
|
||||
disabled={systemLoading}
|
||||
>
|
||||
{t('system.recheck')}
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>npm</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
{systemStatus.npm || t('system.not_found')}
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
{systemStatus.min_versions?.npm || '—'}
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
{systemStatus.npm_version || '—'}
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
<StatusIcon status={systemStatus.npm ? 'ok' : 'bad'} />
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="dark"
|
||||
onClick={loadSystemStatus}
|
||||
disabled={systemLoading}
|
||||
>
|
||||
{t('system.recheck')}
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>tar</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
{systemStatus.tar || t('system.not_found')}
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">—</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
{systemStatus.tar_version || '—'}
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
<StatusIcon status={systemStatus.tar ? 'ok' : 'bad'} />
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="dark"
|
||||
onClick={loadSystemStatus}
|
||||
disabled={systemLoading}
|
||||
>
|
||||
{t('system.recheck')}
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>rsync</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
{systemStatus.rsync || t('system.not_found')}
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">—</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
{systemStatus.rsync_version || '—'}
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
<StatusIcon status={systemStatus.rsync ? 'ok' : 'bad'} />
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="dark"
|
||||
onClick={loadSystemStatus}
|
||||
disabled={systemLoading}
|
||||
>
|
||||
{t('system.recheck')}
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>proc_* functions</td>
|
||||
<td className="bb-acp-stats-value" colSpan={3}>
|
||||
{systemStatus.proc_functions
|
||||
? Object.entries(systemStatus.proc_functions)
|
||||
.filter(([, ok]) => !ok)
|
||||
.map(([name]) => name)
|
||||
.join(', ')
|
||||
: '—'}
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
<StatusIcon
|
||||
status={
|
||||
Boolean(systemStatus.proc_functions) &&
|
||||
Object.values(systemStatus.proc_functions).every(Boolean)
|
||||
? 'ok'
|
||||
: 'bad'
|
||||
<option value="php">php (system default)</option>
|
||||
<option value="keyhelp">keyhelp-php-domain</option>
|
||||
<option value="custom">Custom binary (e.g. keyhelp-php84)</option>
|
||||
</Form.Select>
|
||||
{systemCliSettings.php_mode === 'custom' && (
|
||||
<Form.Control
|
||||
type="text"
|
||||
placeholder="keyhelp-php84"
|
||||
value={systemCliSettings.php_custom}
|
||||
onChange={(event) =>
|
||||
setSystemCliSettings((prev) => ({
|
||||
...prev,
|
||||
php_custom: event.target.value,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="dark"
|
||||
onClick={loadSystemStatus}
|
||||
disabled={systemLoading}
|
||||
>
|
||||
{t('system.recheck')}
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t('system.storage_writable')}</td>
|
||||
<td className="bb-acp-stats-value">storage/</td>
|
||||
<td className="bb-acp-stats-value">—</td>
|
||||
<td className="bb-acp-stats-value">—</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
<StatusIcon status={systemStatus.storage_writable ? 'ok' : 'bad'} />
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="dark"
|
||||
onClick={loadSystemStatus}
|
||||
disabled={systemLoading}
|
||||
>
|
||||
{t('system.recheck')}
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{t('system.updates_writable')}</td>
|
||||
<td className="bb-acp-stats-value">storage/app/updates</td>
|
||||
<td className="bb-acp-stats-value">—</td>
|
||||
<td className="bb-acp-stats-value">—</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
<StatusIcon status={systemStatus.updates_writable ? 'ok' : 'bad'} />
|
||||
</td>
|
||||
<td className="bb-acp-stats-value">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="dark"
|
||||
onClick={loadSystemStatus}
|
||||
disabled={systemLoading}
|
||||
>
|
||||
{t('system.recheck')}
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
<Form.Text className="bb-muted">
|
||||
Used for CLI-based updates and maintenance tasks. `keyhelp-php-domain`
|
||||
is available on KeyHelp Pro; on non-Pro setups use a custom binary.
|
||||
</Form.Text>
|
||||
</Form.Group>
|
||||
<Button type="submit" variant="dark" disabled={systemCliSaving}>
|
||||
{t('acp.save')}
|
||||
</Button>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{systemSection === 'cli' && renderSystemRequirementsPanel()}
|
||||
{systemSection === 'ci' && (
|
||||
<>
|
||||
<div className="bb-acp-panel">
|
||||
<div className="bb-acp-panel-header">
|
||||
<h5 className="mb-0">CI/CD</h5>
|
||||
</div>
|
||||
<div className="bb-acp-panel-body">
|
||||
<p className="bb-muted mb-0">
|
||||
Placeholder: CI/CD pipelines, runner requirements, and deployment logs will
|
||||
live here.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{renderSystemRequirementsPanel()}
|
||||
</>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
</Tab>
|
||||
<Tab eventKey="custom" title="Custom">
|
||||
<Row className="g-4">
|
||||
<Col xs={12}>
|
||||
<div className="bb-acp-panel">
|
||||
<div className="bb-acp-panel-header">
|
||||
<h5 className="mb-0">Custom</h5>
|
||||
</div>
|
||||
<div className="bb-acp-panel-body">
|
||||
<p className="bb-muted mb-0">
|
||||
Place site-specific assets or overrides in `/custom` and `public/custom`.
|
||||
These paths are preserved during in-place updates.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
<Modal show={showModal} onHide={handleReset} centered size="lg">
|
||||
<Modal.Header closeButton closeVariant="white">
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Fail fast if the database is unreachable.
|
||||
php artisan version:fetch >/dev/null
|
||||
# Keep commits possible when local DB is offline.
|
||||
if ! php artisan version:fetch >/dev/null 2>&1; then
|
||||
echo "pre-commit: skipped 'php artisan version:fetch' (database unreachable)." >&2
|
||||
echo "pre-commit: start MySQL and run it manually when needed." >&2
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user