优化install安装流程
This commit is contained in:
@@ -14,6 +14,10 @@ use app\admin\model\Admin as AdminModel;
|
||||
use app\admin\model\User as UserModel;
|
||||
use support\Response;
|
||||
use Webman\Http\Request;
|
||||
use Phinx\Config\Config as PhinxConfig;
|
||||
use Phinx\Migration\Manager as PhinxManager;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Output\NullOutput;
|
||||
|
||||
/**
|
||||
* 安装控制器
|
||||
@@ -74,7 +78,7 @@ class Install extends Api
|
||||
{
|
||||
$this->setRequest($request);
|
||||
if ($this->isInstallComplete()) {
|
||||
return $this->error(__('The system has completed installation. If you need to reinstall, please delete the %s file first', ['public/' . self::$lockFileName]));
|
||||
return $this->error(__('The system has completed installation. If you need to reinstall, please delete the %file% file first', ['%file%' => 'public/' . self::$lockFileName]));
|
||||
}
|
||||
|
||||
(new Terminal())->exec(false);
|
||||
@@ -85,7 +89,7 @@ class Install extends Api
|
||||
{
|
||||
$this->setRequest($request);
|
||||
if ($this->isInstallComplete()) {
|
||||
return $this->error(__('The system has completed installation. If you need to reinstall, please delete the %s file first', ['public/' . self::$lockFileName]));
|
||||
return $this->error(__('The system has completed installation. If you need to reinstall, please delete the %file% file first', ['%file%' => 'public/' . self::$lockFileName]));
|
||||
}
|
||||
|
||||
$newPackageManager = $request->post('manager', config('terminal.npm_package_manager'));
|
||||
@@ -104,7 +108,7 @@ class Install extends Api
|
||||
{
|
||||
$this->setRequest($request);
|
||||
if ($this->isInstallComplete()) {
|
||||
return $this->error(__('The system has completed installation. If you need to reinstall, please delete the %s file first', ['public/' . self::$lockFileName]), []);
|
||||
return $this->error(__('The system has completed installation. If you need to reinstall, please delete the %file% file first', ['%file%' => 'public/' . self::$lockFileName]), []);
|
||||
}
|
||||
if (($_ENV['DATABASE_TYPE'] ?? getenv('DATABASE_TYPE'))) {
|
||||
return $this->error(__('The .env file with database configuration was detected. Please clean up and try again!'));
|
||||
@@ -410,7 +414,7 @@ class Install extends Api
|
||||
{
|
||||
$this->setRequest($request);
|
||||
if ($this->isInstallComplete()) {
|
||||
return $this->error(__('The system has completed installation. If you need to reinstall, please delete the %s file first', ['public/' . self::$lockFileName]));
|
||||
return $this->error(__('The system has completed installation. If you need to reinstall, please delete the %file% file first', ['%file%' => 'public/' . self::$lockFileName]));
|
||||
}
|
||||
|
||||
$envOk = $this->commandExecutionCheck();
|
||||
@@ -456,27 +460,32 @@ class Install extends Api
|
||||
return $this->error(__('File has no write permission:%s', ['config/' . self::$dbConfigFileName]));
|
||||
}
|
||||
|
||||
// 写入 dafuweng-webman/.env-example
|
||||
$envFile = root_path() . '.env-example';
|
||||
$envFileContent = @file_get_contents($envFile);
|
||||
if ($envFileContent) {
|
||||
$databasePos = stripos($envFileContent, '[DATABASE]');
|
||||
if ($databasePos !== false) {
|
||||
$envFileContent = substr($envFileContent, 0, $databasePos);
|
||||
}
|
||||
$envFileContent .= "\n" . '[DATABASE]' . "\n";
|
||||
$envFileContent .= 'TYPE = mysql' . "\n";
|
||||
$envFileContent .= 'HOSTNAME = ' . $databaseParam['hostname'] . "\n";
|
||||
$envFileContent .= 'DATABASE = ' . $databaseParam['database'] . "\n";
|
||||
$envFileContent .= 'USERNAME = ' . $databaseParam['username'] . "\n";
|
||||
$envFileContent .= 'PASSWORD = ' . $databaseParam['password'] . "\n";
|
||||
$envFileContent .= 'HOSTPORT = ' . $databaseParam['hostport'] . "\n";
|
||||
$envFileContent .= 'PREFIX = ' . ($databaseParam['prefix'] ?? '') . "\n";
|
||||
$envFileContent .= 'CHARSET = utf8mb4' . "\n";
|
||||
$envFileContent .= 'DEBUG = true' . "\n";
|
||||
$result = @file_put_contents($envFile, $envFileContent);
|
||||
if (!$result) {
|
||||
return $this->error(__('File has no write permission:%s', ['/' . $envFile]));
|
||||
// 写入 .env 和 .env-example(仅使用 Dotenv 可解析的 DATABASE_XXX 格式,避免 [DATABASE] 导致解析失败)
|
||||
$databaseBlock = "\n# Database\n"
|
||||
. 'DATABASE_TYPE = mysql' . "\n"
|
||||
. 'DATABASE_HOSTNAME = ' . $databaseParam['hostname'] . "\n"
|
||||
. 'DATABASE_DATABASE = ' . $databaseParam['database'] . "\n"
|
||||
. 'DATABASE_USERNAME = ' . $databaseParam['username'] . "\n"
|
||||
. 'DATABASE_PASSWORD = ' . $databaseParam['password'] . "\n"
|
||||
. 'DATABASE_HOSTPORT = ' . $databaseParam['hostport'] . "\n"
|
||||
. 'DATABASE_CHARSET = utf8mb4' . "\n"
|
||||
. 'DATABASE_PREFIX = ' . ($databaseParam['prefix'] ?? '') . "\n";
|
||||
foreach (['.env', '.env-example'] as $envName) {
|
||||
$envFile = root_path() . $envName;
|
||||
$envFileContent = is_file($envFile) ? @file_get_contents($envFile) : '';
|
||||
if ($envFileContent !== false) {
|
||||
$cutPos = strlen($envFileContent);
|
||||
foreach (['[DATABASE]', "\n# Database\n", "\n# 数据库", "\nDATABASE_DRIVER", "\nDATABASE_TYPE"] as $marker) {
|
||||
$pos = stripos($envFileContent, $marker);
|
||||
if ($pos !== false && $pos < $cutPos) {
|
||||
$cutPos = $pos;
|
||||
}
|
||||
}
|
||||
$envFileContent = rtrim(substr($envFileContent, 0, $cutPos)) . $databaseBlock;
|
||||
$result = @file_put_contents($envFile, $envFileContent);
|
||||
if (!$result && is_file($envFile)) {
|
||||
return $this->error(__('File has no write permission:%s', ['%s' => $envName]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,13 +509,66 @@ class Install extends Api
|
||||
return $this->error(__('File has no write permission:%s', ['public/' . self::$lockFileName]));
|
||||
}
|
||||
|
||||
// 自动执行数据库迁移(无需手动运行 phinx 命令)
|
||||
$migrateResult = $this->runPhinxMigrate($databaseParam);
|
||||
if ($migrateResult !== true) {
|
||||
return $this->error($migrateResult);
|
||||
}
|
||||
|
||||
return $this->success('', [
|
||||
'rootPath' => $rootPath,
|
||||
'executionWebCommand' => $envOk,
|
||||
'migrateCommand' => $migrateCommand,
|
||||
'rootPath' => $rootPath,
|
||||
'executionWebCommand' => $envOk,
|
||||
'migrateCommand' => $migrateCommand,
|
||||
'migrationCompleted' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 程序化执行 Phinx 数据库迁移
|
||||
* @param array $databaseParam 数据库连接参数
|
||||
* @return true|string 成功返回 true,失败返回错误信息
|
||||
*/
|
||||
private function runPhinxMigrate(array $databaseParam): true|string
|
||||
{
|
||||
try {
|
||||
$baseDir = root_path();
|
||||
$phinxConfigPath = $baseDir . 'phinx.php';
|
||||
|
||||
if (!is_file($phinxConfigPath)) {
|
||||
return __('Failed to install SQL execution:%msg%', ['%msg%' => 'phinx.php not found']);
|
||||
}
|
||||
|
||||
// 临时设置环境变量,供 phinx 读取数据库配置
|
||||
$_ENV['DATABASE_HOSTNAME'] = $databaseParam['hostname'] ?? '127.0.0.1';
|
||||
$_ENV['DATABASE_DATABASE'] = $databaseParam['database'] ?? '';
|
||||
$_ENV['DATABASE_USERNAME'] = $databaseParam['username'] ?? 'root';
|
||||
$_ENV['DATABASE_PASSWORD'] = $databaseParam['password'] ?? '';
|
||||
$_ENV['DATABASE_HOSTPORT'] = $databaseParam['hostport'] ?? '3306';
|
||||
$_ENV['DATABASE_PREFIX'] = $databaseParam['prefix'] ?? '';
|
||||
putenv('DATABASE_HOSTNAME=' . $_ENV['DATABASE_HOSTNAME']);
|
||||
putenv('DATABASE_DATABASE=' . $_ENV['DATABASE_DATABASE']);
|
||||
putenv('DATABASE_USERNAME=' . $_ENV['DATABASE_USERNAME']);
|
||||
putenv('DATABASE_PASSWORD=' . $_ENV['DATABASE_PASSWORD']);
|
||||
putenv('DATABASE_HOSTPORT=' . $_ENV['DATABASE_HOSTPORT']);
|
||||
putenv('DATABASE_PREFIX=' . $_ENV['DATABASE_PREFIX']);
|
||||
|
||||
$config = PhinxConfig::fromPhp($phinxConfigPath);
|
||||
$input = new ArrayInput([]);
|
||||
$output = new NullOutput();
|
||||
$manager = new PhinxManager($config, $input, $output);
|
||||
|
||||
$environment = $config->getDefaultEnvironment();
|
||||
$manager->migrate($environment);
|
||||
return true;
|
||||
} catch (Throwable $e) {
|
||||
$msg = $e->getMessage();
|
||||
if ($e->getPrevious()) {
|
||||
$msg .= ' | ' . $e->getPrevious()->getMessage();
|
||||
}
|
||||
return __('Failed to install SQL execution:%msg%', ['%msg%' => $msg]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function isInstallComplete(): bool
|
||||
{
|
||||
if (is_file(public_path(self::$lockFileName))) {
|
||||
@@ -526,7 +588,7 @@ class Install extends Api
|
||||
{
|
||||
$this->setRequest($request);
|
||||
if ($this->isInstallComplete()) {
|
||||
return $this->error(__('The system has completed installation. If you need to reinstall, please delete the %s file first', ['public/' . self::$lockFileName]));
|
||||
return $this->error(__('The system has completed installation. If you need to reinstall, please delete the %file% file first', ['%file%' => 'public/' . self::$lockFileName]));
|
||||
}
|
||||
|
||||
$param = $request->only(['type', 'adminname', 'adminpassword', 'sitename']);
|
||||
@@ -586,6 +648,36 @@ class Install extends Api
|
||||
return $envOk;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取安装完成后的访问地址(根据请求来源区分 API 与前端开发模式)
|
||||
* - 通过 API 访问(8787):index.html#/admin、index.html#/
|
||||
* - 通过前端开发服务访问(1818):/#/admin、/#/
|
||||
*/
|
||||
public function accessUrls(Request $request): Response
|
||||
{
|
||||
$this->setRequest($request);
|
||||
$host = $request->header('host', '127.0.0.1:8787');
|
||||
$port = '8787';
|
||||
if (str_contains($host, ':')) {
|
||||
$port = substr($host, strrpos($host, ':') + 1);
|
||||
}
|
||||
$scheme = $request->header('x-forwarded-proto', 'http');
|
||||
$base = rtrim($scheme . '://' . $host, '/');
|
||||
|
||||
if ($port === '1818') {
|
||||
$adminUrl = $base . '/#/admin';
|
||||
$frontUrl = $base . '/#/';
|
||||
} else {
|
||||
$adminUrl = $base . '/index.html#/admin';
|
||||
$frontUrl = $base . '/index.html#/';
|
||||
}
|
||||
|
||||
return $this->success('', [
|
||||
'adminUrl' => $adminUrl,
|
||||
'frontUrl' => $frontUrl,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装指引
|
||||
*/
|
||||
|
||||
@@ -18,9 +18,9 @@ return [
|
||||
'already installed' => 'Installed',
|
||||
'Not installed' => 'Not installed',
|
||||
'File has no write permission:%s' => 'File has no write permission:%s',
|
||||
'The system has completed installation. If you need to reinstall, please delete the %s file first' => 'The system has been installed, if you need to reinstall, please delete the %s file first.',
|
||||
'The system has completed installation. If you need to reinstall, please delete the %file% file first' => 'The system has been installed. If you need to reinstall, please delete the %file% file first.',
|
||||
'Database connection failed:%s' => 'Database connection failure:%s',
|
||||
'Failed to install SQL execution:%s' => 'Installation SQL execution failed:%s',
|
||||
'Failed to install SQL execution:%msg%' => 'Installation SQL execution failed: %msg%',
|
||||
'unknown' => 'Unknown',
|
||||
'Database does not exist' => 'Database does not exist!',
|
||||
'No built front-end file found, please rebuild manually!' => 'No built front-end file found, please rebuild manually.',
|
||||
|
||||
@@ -18,9 +18,9 @@ return [
|
||||
'already installed' => '已安装',
|
||||
'Not installed' => '未安装',
|
||||
'File has no write permission:%s' => '文件无写入权限:%s',
|
||||
'The system has completed installation. If you need to reinstall, please delete the %s file first' => '系统已完成安装。如果需要重新安装,请先删除 %s 文件',
|
||||
'The system has completed installation. If you need to reinstall, please delete the %file% file first' => '系统已完成安装。如果需要重新安装,请先删除 %file% 文件',
|
||||
'Database connection failed:%s' => '数据库连接失败:%s',
|
||||
'Failed to install SQL execution:%s' => '安装SQL执行失败:%s',
|
||||
'Failed to install SQL execution:%msg%' => '安装SQL执行失败:%msg%',
|
||||
'unknown' => '未知',
|
||||
'Database does not exist' => '数据库不存在!',
|
||||
'No built front-end file found, please rebuild manually!' => '没有找到构建好的前端文件,请手动重新构建!',
|
||||
|
||||
Reference in New Issue
Block a user