diff --git a/.docker/supervisor/supervisord.conf b/.docker/supervisor/supervisord.conf new file mode 100644 index 0000000..768921d --- /dev/null +++ b/.docker/supervisor/supervisord.conf @@ -0,0 +1,64 @@ +[supervisord] +nodaemon=true +user=root +logfile=/dev/stdout +logfile_maxbytes=0 +pidfil=/www/storage/logs/supervisor/supervisord.pid +loglevel=info + +[program:octane] +process_name=%(program_name)s_%(process_num)02d +command=php /www/artisan octane:start --host=0.0.0.0 --port=7001 +autostart=%(ENV_ENABLE_WEB)s +autorestart=true +user=www +redirect_stderr=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stdout_logfile_backups=0 +numprocs=1 +stopwaitsecs=10 +stopsignal=QUIT +stopasgroup=true +killasgroup=true +priority=100 + +[program:horizon] +process_name=%(program_name)s_%(process_num)02d +command=php /www/artisan horizon +autostart=%(ENV_ENABLE_HORIZON)s +autorestart=true +user=www +redirect_stderr=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stdout_logfile_backups=0 +numprocs=1 +stopwaitsecs=3 +stopsignal=SIGINT +stopasgroup=true +killasgroup=true +priority=200 + +[program:redis] +process_name=%(program_name)s_%(process_num)02d +command=redis-server --dir /data + --dbfilename dump.rdb + --save 900 1 + --save 300 10 + --save 60 10000 + --unixsocket /data/redis.sock + --unixsocketperm 777 +autostart=%(ENV_ENABLE_REDIS)s +autorestart=true +user=redis +redirect_stderr=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stdout_logfile_backups=0 +numprocs=1 +stopwaitsecs=3 +stopsignal=TERM +stopasgroup=true +killasgroup=true +priority=300 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 1c2683a..9c202e6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,23 +1,25 @@ FROM phpswoole/swoole:php8.1-alpine COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ - RUN install-php-extensions pcntl bcmath \ - && apk --no-cache add shadow sqlite mysql-client git patch \ - && addgroup -S -g 1000 www && adduser -S -G www -u 1000 www -#复制项目文件以及配置文件 + && apk --no-cache add shadow sqlite mysql-client git patch supervisor redis \ + && addgroup -S -g 1000 www && adduser -S -G www -u 1000 www \ + && (getent group redis || addgroup -S redis) \ + && (getent passwd redis || adduser -S -G redis -H -h /data redis) + WORKDIR /www COPY .docker / COPY . /www +COPY .docker/supervisor/supervisord.conf /etc/supervisor/conf.d/supervisord.conf + RUN composer install --optimize-autoloader --no-cache --no-dev \ && php artisan storage:link \ && chown -R www:www /www \ - && chmod -R 775 /www - -CMD php artisan octane:start \ - --server="swoole" \ - --host=0.0.0.0 \ - --port=${OCTANE_PORT:-7001} \ - --workers=${OCTANE_WORKERS:-auto} \ - --task-workers=${OCTANE_TASK_WORKERS:-auto} \ - --max-requests=${OCTANE_MAX_REQUESTS:-500} \ No newline at end of file + && chmod -R 775 /www \ + && mkdir -p /data \ + && chown redis:redis /data + +ENV ENABLE_WEB=false \ + ENABLE_HORIZON=false \ + ENABLE_REDIS=false +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] \ No newline at end of file diff --git a/README.md b/README.md index 5ae4d44..c85d11d 100644 --- a/README.md +++ b/README.md @@ -31,14 +31,13 @@ Xboard New是基于Xboard二次开发,重写后台管理并优化系统架构 git clone -b compose-new --depth 1 https://github.com/cedar2025/Xboard && \ cd Xboard && \ docker compose run -it --rm \ - -e enable_sqlite=true \ - -e enable_redis=true \ - -e admin_account=admin@demo.com \ + -e ENABLE_SQLITE=true \ + -e ENABLE_REDIS=true \ + -e ADMIN_ACCOUNT=admin@demo.com \ web php artisan xboard:install && \ docker compose up -d - -# 安装完成后访问 http://服务器IP:7001 ``` +安装完成后访问 http://服务器IP:7001 > 提示:安装过程中会显示管理员账号密码,请务必保存。 diff --git a/app/Console/Commands/XboardInstall.php b/app/Console/Commands/XboardInstall.php index 434dd10..c111fbb 100644 --- a/app/Console/Commands/XboardInstall.php +++ b/app/Console/Commands/XboardInstall.php @@ -45,10 +45,10 @@ class XboardInstall extends Command public function handle() { try { - $isDocker = env('docker', false); - $enableSqlite = env('enable_sqlite', false); - $enableRedis = env('enable_redis', false); - $adminAccount = env('admin_account', ''); + $isDocker = file_exists('/.dockerenv'); + $enableSqlite = env('ENABLE_SQLITE', false); + $enableRedis = env('ENABLE_REDIS', false); + $adminAccount = env('ADMIN_ACCOUNT', ''); $this->info("__ __ ____ _ "); $this->info("\ \ / /| __ ) ___ __ _ _ __ __| | "); $this->info(" \ \/ / | __ \ / _ \ / _` | '__/ _` | "); @@ -146,7 +146,7 @@ class XboardInstall extends Command while (!$isReidsValid) { // 判断是否为Docker环境 if ($isDocker == 'true' && ($enableRedis || confirm(label: '是否启用Docker内置的Redis', default: true, yes: '启用', no: '不启用'))) { - $envConfig['REDIS_HOST'] = '/run/redis-socket/redis.sock'; + $envConfig['REDIS_HOST'] = '/data/redis.sock'; $envConfig['REDIS_PORT'] = 0; $envConfig['REDIS_PASSWORD'] = null; } else { @@ -171,6 +171,7 @@ class XboardInstall extends Command // 连接失败,输出错误消息 $this->error("redis连接失败:" . $e->getMessage()); $this->info("请重新输入REDIS配置"); + sleep(1); } } diff --git a/bin/fswatch b/bin/fswatch deleted file mode 100755 index dd60014..0000000 --- a/bin/fswatch +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash -WORK_DIR=$1 -if [ ! -n "${WORK_DIR}" ] ;then - WORK_DIR="." -fi - -echo "Restarting LaravelS..." -./bin/laravels restart -d -i - -echo "Starting fswatch..." -LOCKING=0 -fswatch -e ".*" -i "\\.php$" -r ${WORK_DIR} | while read file -do - if [[ ! ${file} =~ .php$ ]] ;then - continue - fi - if [ ${LOCKING} -eq 1 ] ;then - echo "Reloading, skipped." - continue - fi - echo "File ${file} has been modified." - LOCKING=1 - ./bin/laravels reload - LOCKING=0 -done -exit 0 \ No newline at end of file diff --git a/bin/inotify b/bin/inotify deleted file mode 100755 index ea3a3bd..0000000 --- a/bin/inotify +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash -WORK_DIR=$1 -if [ ! -n "${WORK_DIR}" ] ;then - WORK_DIR="." -fi - -echo "Restarting LaravelS..." -./bin/laravels restart -d -i - -echo "Starting inotifywait..." -LOCKING=0 - -inotifywait --event modify --event create --event move --event delete -mrq ${WORK_DIR} | while read file - -do - if [[ ! ${file} =~ .php$ ]] ;then - continue - fi - if [ ${LOCKING} -eq 1 ] ;then - echo "Reloading, skipped." - continue - fi - echo "File ${file} has been modified." - LOCKING=1 - ./bin/laravels reload - LOCKING=0 -done -exit 0 \ No newline at end of file diff --git a/bin/laravels b/bin/laravels deleted file mode 100755 index c17313e..0000000 --- a/bin/laravels +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env php -prefixes[$prefix]) === false) { - $this->prefixes[$prefix] = []; - } - - // retain the base directory for the namespace prefix - if ($prepend) { - array_unshift($this->prefixes[$prefix], $base_dir); - } else { - $this->prefixes[$prefix][] = $base_dir; - } - } - - /** - * Loads the class file for a given class name. - * - * @param string $class The fully-qualified class name. - * @return mixed The mapped file name on success, or boolean false on - * failure. - */ - public function loadClass($class) - { - // the current namespace prefix - $prefix = $class; - - // work backwards through the namespace names of the fully-qualified - // class name to find a mapped file name - while (false !== $pos = strrpos($prefix, '\\')) { - // retain the trailing namespace separator in the prefix - $prefix = substr($class, 0, $pos + 1); - - // the rest is the relative class name - $relative_class = substr($class, $pos + 1); - - // try to load a mapped file for the prefix and relative class - $mapped_file = $this->loadMappedFile($prefix, $relative_class); - if ($mapped_file) { - return $mapped_file; - } - - // remove the trailing namespace separator for the next iteration - // of strrpos() - $prefix = rtrim($prefix, '\\'); - } - - // never found a mapped file - return false; - } - - /** - * Load the mapped file for a namespace prefix and relative class. - * - * @param string $prefix The namespace prefix. - * @param string $relative_class The relative class name. - * @return mixed Boolean false if no mapped file can be loaded, or the - * name of the mapped file that was loaded. - */ - protected function loadMappedFile($prefix, $relative_class) - { - // are there any base directories for this namespace prefix? - if (isset($this->prefixes[$prefix]) === false) { - return false; - } - - // look through base directories for this namespace prefix - foreach ($this->prefixes[$prefix] as $base_dir) { - // replace the namespace prefix with the base directory, - // replace namespace separators with directory separators - // in the relative class name, append with .php - $file = $base_dir - . str_replace('\\', '/', $relative_class) - . '.php'; - - // if the mapped file exists, require it - if ($this->requireFile($file)) { - // yes, we're done - return $file; - } - } - - // never found it - return false; - } - - /** - * If a file exists, require it from the file system. - * - * @param string $file The file to require. - * @return bool True if the file exists, false if not. - */ - public function requireFile($file) - { - if (file_exists($file)) { - require $file; - return true; - } - return false; - } -} - -$basePath = dirname(__DIR__) . '/'; -$loader = new Psr4Autoloader(); -$loader->register(); - -// Register laravel-s -$loader->addNamespace('Hhxsv5\LaravelS', $basePath . '/vendor/hhxsv5/laravel-s/src'); - -// Register laravel-s dependencies - -// To fix issue #364 https://github.com/hhxsv5/laravel-s/issues/364 -$loader->addNamespace('Symfony\Polyfill\Php80', $basePath . '/vendor/symfony/polyfill-php80'); -$loader->requireFile($basePath . '/vendor/symfony/polyfill-php80/bootstrap.php'); - -$loader->addNamespace('Symfony\Component\Console', $basePath . '/vendor/symfony/console'); -$loader->addNamespace('Symfony\Contracts\Service', $basePath . '/vendor/symfony/service-contracts'); -$loader->addNamespace('Symfony\Contracts', $basePath . '/vendor/symfony/contracts'); - -$command = new Hhxsv5\LaravelS\Console\Portal($basePath); -$input = new Symfony\Component\Console\Input\ArgvInput(); -$output = new Symfony\Component\Console\Output\ConsoleOutput(); -$code = $command->run($input, $output); -exit($code); \ No newline at end of file diff --git a/compose.sample.yaml b/compose.sample.yaml index 0a7690d..17ed6c3 100644 --- a/compose.sample.yaml +++ b/compose.sample.yaml @@ -2,19 +2,19 @@ services: web: image: ghcr.io/cedar2025/xboard:new volumes: - - ./.docker/.data/redis/:/run/redis-socket + - ./.docker/.data/redis/:/data/ - ./:/www/ environment: - docker=true depends_on: - redis network_mode: host - command: php artisan octane:start --server="swoole" --port=7001 --host=0.0.0.0 + command: php artisan octane:start --port=7001 --host=0.0.0.0 restart: on-failure horizon: image: ghcr.io/cedar2025/xboard:new volumes: - - ./.docker/.data/redis/:/run/redis-socket + - ./.docker/.data/redis/:/data/ - ./:/www/ restart: on-failure network_mode: host diff --git a/config/octane.php b/config/octane.php index 818ee70..f2cc571 100644 --- a/config/octane.php +++ b/config/octane.php @@ -36,7 +36,7 @@ return [ | */ - 'server' => env('OCTANE_SERVER', 'roadrunner'), + 'server' => env('OCTANE_SERVER', 'swoole'), /* |-------------------------------------------------------------------------- diff --git a/config/swoole_http.php b/config/swoole_http.php deleted file mode 100644 index a73b139..0000000 --- a/config/swoole_http.php +++ /dev/null @@ -1,137 +0,0 @@ - [ - 'host' => env('SWOOLE_HTTP_HOST', '0.0.0.0'), - 'port' => env('SWOOLE_HTTP_PORT', '1215'), - 'public_path' => base_path('public'), - // Determine if to use swoole to respond request for static files - 'handle_static_files' => env('SWOOLE_HANDLE_STATIC', true), - 'access_log' => env('SWOOLE_HTTP_ACCESS_LOG', false), - // You must add --enable-openssl while compiling Swoole - // Put `SWOOLE_SOCK_TCP | SWOOLE_SSL` if you want to enable SSL - 'socket_type' => SWOOLE_SOCK_TCP, - 'process_type' => SWOOLE_PROCESS, - 'options' => [ - 'pid_file' => env('SWOOLE_HTTP_PID_FILE', base_path('storage/logs/swoole_http.pid')), - 'log_file' => env('SWOOLE_HTTP_LOG_FILE', base_path('storage/logs/swoole_http.log')), - 'daemonize' => env('SWOOLE_HTTP_DAEMONIZE', false), - // Normally this value should be 1~4 times larger according to your cpu cores. - 'reactor_num' => env('SWOOLE_HTTP_REACTOR_NUM', swoole_cpu_num()), - 'worker_num' => env('SWOOLE_HTTP_WORKER_NUM', swoole_cpu_num()), - 'task_worker_num' => env('SWOOLE_HTTP_TASK_WORKER_NUM', swoole_cpu_num()), - // The data to receive can't be larger than buffer_output_size. - 'package_max_length' => 20 * 1024 * 1024, - // The data to send can't be larger than buffer_output_size. - 'buffer_output_size' => 10 * 1024 * 1024, - // Max buffer size for socket connections - 'socket_buffer_size' => 128 * 1024 * 1024, - // Worker will restart after processing this number of requests - 'max_request' => 3000, - // Enable coroutine send - 'send_yield' => true, - // You must add --enable-openssl while compiling Swoole - 'ssl_cert_file' => null, - 'ssl_key_file' => null, - ], - ], - - /* - |-------------------------------------------------------------------------- - | Enable to turn on websocket server. - |-------------------------------------------------------------------------- - */ - 'websocket' => [ - 'enabled' => env('SWOOLE_HTTP_WEBSOCKET', false), - ], - - /* - |-------------------------------------------------------------------------- - | Hot reload configuration - |-------------------------------------------------------------------------- - */ - 'hot_reload' => [ - 'enabled' => env('SWOOLE_HOT_RELOAD_ENABLE', false), - 'recursively' => env('SWOOLE_HOT_RELOAD_RECURSIVELY', true), - 'directory' => env('SWOOLE_HOT_RELOAD_DIRECTORY', base_path()), - 'log' => env('SWOOLE_HOT_RELOAD_LOG', true), - 'filter' => env('SWOOLE_HOT_RELOAD_FILTER', '.php'), - ], - - /* - |-------------------------------------------------------------------------- - | Console output will be transferred to response content if enabled. - |-------------------------------------------------------------------------- - */ - 'ob_output' => env('SWOOLE_OB_OUTPUT', true), - - /* - |-------------------------------------------------------------------------- - | Pre-resolved instances here will be resolved when sandbox created. - |-------------------------------------------------------------------------- - */ - 'pre_resolved' => [ - 'view', 'files', 'session', 'session.store', 'routes', - 'db', 'db.factory', 'cache', 'cache.store', 'config', 'cookie', - 'encrypter', 'hash', 'router', 'translator', 'url', 'log', - ], - - /* - |-------------------------------------------------------------------------- - | Instances here will be cleared on every request. - |-------------------------------------------------------------------------- - */ - 'instances' => [ - 'auth', - ], - - /* - |-------------------------------------------------------------------------- - | Providers here will be registered on every request. - |-------------------------------------------------------------------------- - */ - 'providers' => [ - Illuminate\Pagination\PaginationServiceProvider::class, - ], - - /* - |-------------------------------------------------------------------------- - | Resetters for sandbox app. - |-------------------------------------------------------------------------- - */ - 'resetters' => [ - SwooleTW\Http\Server\Resetters\ResetConfig::class, - SwooleTW\Http\Server\Resetters\ResetSession::class, - SwooleTW\Http\Server\Resetters\ResetCookie::class, - SwooleTW\Http\Server\Resetters\ClearInstances::class, - SwooleTW\Http\Server\Resetters\BindRequest::class, - SwooleTW\Http\Server\Resetters\RebindKernelContainer::class, - SwooleTW\Http\Server\Resetters\RebindRouterContainer::class, - SwooleTW\Http\Server\Resetters\RebindViewContainer::class, - SwooleTW\Http\Server\Resetters\ResetProviders::class, - ], - - /* - |-------------------------------------------------------------------------- - | Define your swoole tables here. - | - | @see https://www.swoole.co.uk/docs/modules/swoole-table - |-------------------------------------------------------------------------- - */ - 'tables' => [ - // 'table_name' => [ - // 'size' => 1024, - // 'columns' => [ - // ['name' => 'column_name', 'type' => Table::TYPE_STRING, 'size' => 1024], - // ] - // ], - ], -]; diff --git a/config/swoole_websocket.php b/config/swoole_websocket.php deleted file mode 100644 index 867b9df..0000000 --- a/config/swoole_websocket.php +++ /dev/null @@ -1,107 +0,0 @@ - SwooleTW\Http\Websocket\SocketIO\WebsocketHandler::class, - - /* - |-------------------------------------------------------------------------- - | Default frame parser - | Replace it if you want to customize your websocket payload - |-------------------------------------------------------------------------- - */ - 'parser' => SwooleTW\Http\Websocket\SocketIO\SocketIOParser::class, - - /* - |-------------------------------------------------------------------------- - | Websocket route file path - |-------------------------------------------------------------------------- - */ - 'route_file' => base_path('routes/websocket.php'), - - /* - |-------------------------------------------------------------------------- - | Default middleware for on connect request - |-------------------------------------------------------------------------- - */ - 'middleware' => [ - // SwooleTW\Http\Websocket\Middleware\DecryptCookies::class, - // SwooleTW\Http\Websocket\Middleware\StartSession::class, - // SwooleTW\Http\Websocket\Middleware\Authenticate::class, - ], - - /* - |-------------------------------------------------------------------------- - | Websocket handler for customized onHandShake callback - |-------------------------------------------------------------------------- - */ - 'handshake' => [ - 'enabled' => false, - 'handler' => SwooleTW\Http\Websocket\HandShakeHandler::class, - ], - - /* - |-------------------------------------------------------------------------- - | Default websocket driver - |-------------------------------------------------------------------------- - */ - 'default' => 'table', - - /* - |-------------------------------------------------------------------------- - | Websocket client's heartbeat interval (ms) - |-------------------------------------------------------------------------- - */ - 'ping_interval' => 25000, - - /* - |-------------------------------------------------------------------------- - | Websocket client's heartbeat interval timeout (ms) - |-------------------------------------------------------------------------- - */ - 'ping_timeout' => 60000, - - /* - |-------------------------------------------------------------------------- - | Room drivers mapping - |-------------------------------------------------------------------------- - */ - 'drivers' => [ - 'table' => SwooleTW\Http\Websocket\Rooms\TableRoom::class, - 'redis' => SwooleTW\Http\Websocket\Rooms\RedisRoom::class, - ], - - /* - |-------------------------------------------------------------------------- - | Room drivers settings - |-------------------------------------------------------------------------- - */ - 'settings' => [ - - 'table' => [ - 'room_rows' => 4096, - 'room_size' => 2048, - 'client_rows' => 8192, - 'client_size' => 2048, - ], - - 'redis' => [ - 'server' => [ - 'host' => env('REDIS_HOST', '127.0.0.1'), - 'password' => env('REDIS_PASSWORD', null), - 'port' => env('REDIS_PORT', 6379), - 'database' => 0, - 'persistent' => true, - ], - 'options' => [ - // - ], - 'prefix' => 'swoole:', - ], - ], -]; diff --git a/docs/docker-compose安装指南.md b/docs/docker-compose安装指南.md index 566011e..90d2b20 100644 --- a/docs/docker-compose安装指南.md +++ b/docs/docker-compose安装指南.md @@ -21,16 +21,18 @@ git clone -b compose-new --depth 1 https://github.com/cedar2025/Xboard cd Xboard ``` -2. 安装数据库: -```bash -# 快速安装(推荐新手使用) -docker compose run -it --rm \ - -e enable_sqlite=true \ - -e enable_redis=true \ - -e admin_account=admin@demo.com \ - web php artisan xboard:install +2. 安装数据库: -# 自定义配置安装(高级用户) +- 快速安装(推荐新手使用) +```bash +docker compose run -it --rm \ + -e ENABLE_SQLITE=true \ + -e ENABLE_REDIS=true \ + -e ADMIN_ACCOUNT=admin@demo.com \ + web php artisan xboard:install && \ +``` +- 自定义配置安装(高级用户) +```bash docker compose run -it --rm web php artisan xboard:install ``` > 安装完成后请保存返回的后台地址和管理员账号密码