diff --git a/.docker/php/Dockerfile b/.docker/php/Dockerfile index 18de9a08..a1ce506c 100644 --- a/.docker/php/Dockerfile +++ b/.docker/php/Dockerfile @@ -1,70 +1,79 @@ # 👷‍♀️ 第一阶段:构建阶段,包含所有开发依赖 -FROM php:8.2-fpm-alpine AS builder +FROM php:8.2-fpm AS builder -RUN apk add --no-cache \ - $PHPIZE_DEPS \ +# 安装依赖 +RUN apt-get update && apt-get install -y \ + git \ + unzip \ libzip-dev \ libpng-dev \ - libjpeg-turbo-dev \ - freetype-dev \ - icu-dev \ - libxml2-dev \ + libjpeg-dev \ libwebp-dev \ - gmp-dev \ - oniguruma-dev \ - linux-headers - -RUN docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp - -RUN docker-php-ext-install -j$(nproc) \ - bcmath \ - pdo_mysql \ - mysqli \ - gd \ - pcntl \ - sockets \ - gmp \ - zip \ - intl \ - opcache + libfreetype6-dev \ + libicu-dev \ + libxml2-dev \ + libgmp-dev \ + libonig-dev \ + curl \ + wget \ + rsync \ + pkg-config \ + build-essential \ + && docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \ + && docker-php-ext-install -j$(nproc) \ + bcmath \ + pdo_mysql \ + mysqli \ + gd \ + pcntl \ + sockets \ + gmp \ + zip \ + intl \ + opcache # 检查 pecl.php.net 可用性 RUN curl -Is https://pecl.php.net | head -n 1 | grep "200" \ || (echo "❌ pecl.php.net unreachable, aborting build." && exit 1) - # 安装 redis 扩展 RUN pecl channel-update pecl.php.net \ && pecl install redis \ && docker-php-ext-enable redis # 👨‍🍳 第二阶段:运行阶段,仅包含必要运行环境 -FROM php:8.2-fpm-alpine +FROM php:8.2-fpm -# 复制已构建的扩展 -COPY --from=builder /usr/local/lib/php/extensions /usr/local/lib/php/extensions -COPY --from=builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/ - -# 安装运行时所需依赖 -RUN apk add --no-cache \ +# 安装运行所需依赖(无需 dev 工具) +RUN apt-get update && apt-get install -y \ + libzip4 \ + libpng-dev \ + libjpeg-dev \ + libwebp-dev \ + libfreetype6 \ + libicu-dev \ + libxml2 \ + libgmp-dev \ + libonig-dev \ + git \ + curl \ wget \ rsync \ - libzip \ - libpng \ - libjpeg-turbo \ - libwebp \ - freetype \ - icu \ - libxml2 \ - gmp \ - oniguruma \ - git + unzip \ + bash \ + netcat-openbsd \ + default-mysql-client \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + +# 复制构建好的 PHP 扩展配置 +COPY --from=builder /usr/local/lib/php/extensions /usr/local/lib/php/extensions +COPY --from=builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/ # 配置 www.conf RUN sed -i \ -e 's/;catch_workers_output = .*/catch_workers_output = yes/g' \ - -e 's/;php_admin_flag\[log_errors\] = .*/php_admin_flag\[log_errors\] = on/g' \ - -e 's/;php_admin_value\[error_log\] = .*/php_admin_value\[error_log\] = \/dev\/stderr/g' \ + -e 's/;php_admin_flag\[log_errors\] = .*/php_admin_flag[log_errors] = on/g' \ + -e 's/;php_admin_value\[error_log\] = .*/php_admin_value[error_log] = \/dev\/stderr/g' \ /usr/local/etc/php-fpm.d/www.conf # 配置 PHP 错误日志输出到 stderr @@ -75,6 +84,7 @@ RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local ENV LOG_CHANNEL=stderr +# 复制入口脚本 COPY entrypoint.sh /usr/local/bin/entrypoint.sh RUN chmod +x /usr/local/bin/entrypoint.sh diff --git a/.docker/php/entrypoint.sh b/.docker/php/entrypoint.sh index 6753811a..165e9780 100644 --- a/.docker/php/entrypoint.sh +++ b/.docker/php/entrypoint.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # 定义颜色 COLOR_RED='\033[0;31m' @@ -36,15 +36,27 @@ VENDOR_DIR="${ROOT_PATH}/vendor" chown -R www-data:www-data $ROOT_PATH +mysql_timeout=30 until nc -z mysql 3306; do echo_info "Waiting for MySQL to be ready..." sleep 2 + ((mysql_timeout--)) + if [ $mysql_timeout -le 0 ]; then + echo "❌ MySQL connection timeout." + exit 1 + fi done echo_success "MySQL is ready." +redis_timeout=30 until nc -z redis 6379; do echo_info "Waiting for Redis to be ready..." sleep 2 + ((redis_timeout--)) + if [ $redis_timeout -le 0 ]; then + echo "❌ Redis connection timeout." + exit 1 + fi done echo_success "Redis is ready." @@ -107,7 +119,3 @@ else echo_error "Unknown SERVICE_NAME: $SERVICE_NAME, exiting." exit 1 fi - - - - diff --git a/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php b/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php index d4838b97..7a7ecdfc 100644 --- a/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php +++ b/app/Filament/Resources/System/SettingResource/Pages/EditSetting.php @@ -10,6 +10,7 @@ use App\Models\SearchBox; use App\Models\Setting; use App\Models\User; use App\Repositories\TokenRepository; +use App\Repositories\ToolRepository; use Filament\Facades\Filament; use Filament\Resources\Pages\Page; use Filament\Forms; @@ -122,7 +123,7 @@ class EditSetting extends Page implements Forms\Contracts\HasForms // Forms\Components\TextInput::make('backup.google_drive_client_secret')->label(__('label.setting.backup.google_drive_client_secret')), // Forms\Components\TextInput::make('backup.google_drive_refresh_token')->label(__('label.setting.backup.google_drive_refresh_token')), // Forms\Components\TextInput::make('backup.google_drive_folder_id')->label(__('label.setting.backup.google_drive_folder_id')), - Forms\Components\TextInput::make('backup.export_path')->label(__('label.setting.backup.export_path'))->helperText(new HtmlString(__('label.setting.backup.export_path_help', ['default_path' => sys_get_temp_dir()]))), + Forms\Components\TextInput::make('backup.export_path')->label(__('label.setting.backup.export_path'))->helperText(new HtmlString(__('label.setting.backup.export_path_help', ['default_path' => ToolRepository::getBackupExportPathDefault()]))), Forms\Components\Radio::make('backup.via_ftp')->options(self::$yesOrNo)->inline(true)->label(__('label.setting.backup.via_ftp'))->helperText(new HtmlString(__('label.setting.backup.via_ftp_help'))), Forms\Components\Radio::make('backup.via_sftp')->options(self::$yesOrNo)->inline(true)->label(__('label.setting.backup.via_sftp'))->helperText(new HtmlString(__('label.setting.backup.via_sftp_help'))), ])->columns(2); diff --git a/app/Repositories/ToolRepository.php b/app/Repositories/ToolRepository.php index e4ca4079..6e251d56 100644 --- a/app/Repositories/ToolRepository.php +++ b/app/Repositories/ToolRepository.php @@ -32,7 +32,7 @@ class ToolRepository extends BaseRepository $webRoot = base_path(); $dirName = basename($webRoot); $excludes = self::BACKUP_EXCLUDES; - $baseFilename = sprintf('%s/%s.web.%s', Setting::getBackupExportPath() ?: sys_get_temp_dir(), $dirName, date('Ymd.His')); + $baseFilename = sprintf('%s/%s.web.%s', $this->getBackupExportPath(), $dirName, date('Ymd.His')); if (command_exists('tar') && ($method === 'tar' || $method === null)) { $filename = $baseFilename . ".tar.gz"; $command = "tar"; @@ -89,9 +89,9 @@ class ToolRepository extends BaseRepository { $connectionName = config('database.default'); $config = config("database.connections.$connectionName"); - $filename = sprintf('%s/%s.database.%s.sql', Setting::getBackupExportPath() ?: sys_get_temp_dir(), basename(base_path()), date('Ymd.His')); + $filename = sprintf('%s/%s.database.%s.sql', $this->getBackupExportPath(), basename(base_path()), date('Ymd.His')); $command = sprintf( - 'mysqldump --user=%s --password=%s --host=%s --port=%s --single-transaction --no-create-db %s >> %s 2>&1', + 'mysqldump --user=%s --password=%s --host=%s --port=%s --single-transaction --no-create-db --no-tablespaces %s >> %s 2>&1', $config['username'], $config['password'], $config['host'], $config['port'], $config['database'], $filename, ); $result = exec($command, $output, $result_code); @@ -115,7 +115,7 @@ class ToolRepository extends BaseRepository if ($backupDatabase['result_code'] != 0) { throw new \RuntimeException("backup database fail: " . json_encode($backupDatabase)); } - $baseFilename = sprintf('%s/%s.%s', Setting::getBackupExportPath() ?: sys_get_temp_dir(), basename(base_path()), date('Ymd.His')); + $baseFilename = sprintf('%s/%s.%s', $this->getBackupExportPath(), basename(base_path()), date('Ymd.His')); if (command_exists('tar') && ($method === 'tar' || $method === null)) { $filename = $baseFilename . ".tar.gz"; $command = sprintf( @@ -149,6 +149,20 @@ class ToolRepository extends BaseRepository return $this->transfer($filename, $result_code); } + private function getBackupExportPath(): string + { + $path = Setting::getBackupExportPath(); + if (empty($path)) { + $path = self::getBackupExportPathDefault(); + } + return $path; + } + + public static function getBackupExportPathDefault(): string + { + return sys_get_temp_dir() . "/nexusphp_backup"; + } + /** * do backup cronjob * diff --git a/docker-compose.yml b/docker-compose.yml index 8a002ce9..cf294a92 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,6 +8,7 @@ services: SERVICE_NAME: php volumes: - .:/var/www/html + - ${NP_BACKUP_EXPORT_PATH:-/tmp/nexusphp_backup}:${NP_BACKUP_EXPORT_PATH:-/tmp/nexusphp_backup} depends_on: - mysql - redis diff --git a/include/constants.php b/include/constants.php index 318c3ee3..5bf8b292 100644 --- a/include/constants.php +++ b/include/constants.php @@ -1,6 +1,6 @@