Files
nexusphp/nexus/Install/Install.php
2025-11-02 23:08:19 +07:00

802 lines
29 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace Nexus\Install;
use App\Models\Setting;
use App\Models\TrackerUrl;
use App\Models\User;
use App\Repositories\SearchBoxRepository;
use App\Repositories\UserRepository;
use Illuminate\Support\Str;
use Nexus\Database\NexusDB;
class Install
{
protected $currentStep;
protected $minimumPhpVersion = '8.2.0';
protected $progressKeyPrefix = '__step';
protected $steps = ['Env check', 'Create .env', 'Create tables', 'Import data', 'Create administrator'];
protected $initializeTables = [
'adminpanel', 'agent_allowed_exception', 'agent_allowed_family', 'allowedemails', 'audiocodecs', 'bannedemails', 'categories',
'caticons', 'codecs', 'countries', 'downloadspeed', 'faq', 'isp', 'language', 'media', 'modpanel', 'processings', 'rules', 'schools',
'searchbox', 'secondicons', 'sources', 'standards', 'stylesheets', 'sysoppanel', 'teams', 'torrents_state', 'uploadspeed',
];
protected $envNames = [
'TIMEZONE',
'DB_HOST', 'DB_PORT', 'DB_USERNAME', 'DB_PASSWORD', 'DB_DATABASE',
'REDIS_HOST', 'REDIS_PORT', 'REDIS_DB', 'REDIS_PASSWORD',
'UID_STARTS',
];
protected array $requiredExtensions = [
'ctype', 'curl', 'fileinfo', 'json', 'mbstring', 'openssl', 'pdo_mysql', 'tokenizer', 'xml',
'mysqli', 'bcmath', 'redis', 'gd', 'gmp', 'Zend OPcache', 'pcntl', 'posix', 'sockets', 'zip', 'intl',
'sqlite3', 'pdo_sqlite'
];
protected array $conflictExtensions = [
'mysql',
];
protected array $optionalExtensions = [
// ['name' => 'swoole', 'desc' => "If use swoole for Octane, make sure 'current' shows 1"],
];
protected array $requiredFunctions = [
'symlink', 'putenv', 'proc_open', 'proc_get_status', 'exec',
'pcntl_signal', 'pcntl_alarm', 'pcntl_async_signals'
];
const INSTALL_LOCK_FILE = 'dont_delete_install.lock';
protected string $lockFile = self::INSTALL_LOCK_FILE;
public function __construct()
{
if (!session_id()) {
session_start();
}
if (!$this->runningInConsole()) {
$this->checkLock();
}
$this->currentStep = min(intval($_REQUEST['step'] ?? 1) ?: 1, count($this->steps) + 1);
}
public function listShouldInitializeTables()
{
return $this->initializeTables;
}
public function currentStep()
{
return $this->currentStep;
}
public function runningInConsole(): bool
{
return php_sapi_name() == 'cli';
}
public function canAccessStep($step)
{
for ($i = 1; $i < $step; $i++) {
$progressKey = $this->getProgressKey($i);
if (!isset($_SESSION[$progressKey])) {
$this->doLog("check step: $i, session doesn't have: " . json_encode($_SESSION));
return false;
}
}
$this->doLog("check step: $step, can access" );
return true;
}
public function doneStep($step)
{
$progressKey = $this->getProgressKey($step);
$this->doLog("doneStep: $step, $progressKey = 1");
$_SESSION[$progressKey] = 1;
}
private function getProgressKey($step)
{
return $this->progressKeyPrefix . $step;
}
public function getLogFile()
{
return sprintf('%s/nexus-install-%s.log', sys_get_temp_dir(), date('Ymd'));
}
public function getInsallDirectory()
{
return ROOT_PATH . 'public/install';
}
public function doLog($log)
{
$log = sprintf('[%s] [%s] %s%s', date('Y-m-d H:i:s'), $this->currentStep, $log, PHP_EOL);
file_put_contents($this->getLogFile(), $log, FILE_APPEND);
}
public function listAllTableCreate($sqlFile = '')
{
if (empty($sqlFile)) {
$sqlFile = ROOT_PATH . '_db/dbstructure_v1.6.sql';
}
$pattern = '/CREATE TABLE `(.*)`.*;/isU';
$string = file_get_contents($sqlFile);
if ($string === false) {
throw new \RuntimeException("sql file: $sqlFile can not read, make sure it exits and can be read.");
}
$count = preg_match_all($pattern, $string, $matches, PREG_SET_ORDER);
if ($count == 0) {
return [];
}
return array_column($matches, 0, 1);
}
public function listAllTableCreateFromMigrations()
{
$tables = [];
foreach (glob(ROOT_PATH . "database/migrations/*.php") as $path) {
$filename = basename($path);
$count = preg_match('/create_(.*)_table.php/', $filename, $matches);
if ($count) {
$tableName = $matches[1];
//Special treatment
$tableName = str_replace("seedbox_records", "seed_box_records", $tableName);
$tables[$tableName] = "database/migrations/$filename";
}
}
return $tables;
}
public function listExistsTable()
{
$sql = 'show tables';
$res = sql_query($sql);
$data = [];
while ($row = mysql_fetch_row($res)) {
$data[] = $row[0];
}
return $data;
}
public function listRequirementTableRows()
{
$gdInfo = function_exists('gd_info') ? gd_info() : [];
$tableRows = [];
$phpVersionRequire = '>= ' . $this->minimumPhpVersion;
$tableRows[] = [
'label' => 'PHP version',
'required' => $phpVersionRequire,
'current' => PHP_VERSION,
'result' => $this->yesOrNo(version_compare(PHP_VERSION, $this->minimumPhpVersion, '>=')),
];
$disabledFunctions = [];
foreach ($this->requiredFunctions as $fn) {
if (str_starts_with($fn, "pcntl_") && function_exists('exec')) {
$output = [];
$command = "php -r 'var_export(function_exists(\"$fn\"));'";
$result = exec($command, $output, $result_code);
$lastFourChars = substr(trim($output[0]), -4);
$exists = $lastFourChars == 'true';
if (!$exists) {
$disabledFunctions[] = $fn;
}
} elseif (!function_exists($fn)) {
$disabledFunctions[] = $fn;
}
}
$tableRows[] = [
'label' => 'Required functions',
'required' => 'true',
'current' => empty($disabledFunctions) ? '1' : "These functions are Disabled: " . implode(',', $disabledFunctions),
'result' => $this->yesOrNo(empty($disabledFunctions)),
];
foreach ($this->conflictExtensions as $extension) {
$loaded = extension_loaded($extension);
$tableRows[] = [
'label' => "PHP extension $extension",
'required' => 'disabled',
'current' => (int)$loaded,
'result' => $loaded ? 'NO' : 'YES',
];
}
foreach ($this->requiredExtensions as $extension) {
if ($extension == 'pcntl' && function_exists('exec')) {
$output = [];
$result = exec("php -m", $output, $result_code);
$outputString = implode("\n", $output);
$loadedStr = $outputString;
$loadedArr = preg_split("/[\r\n]+/", $loadedStr);
$loaded = in_array($extension, $loadedArr);
} else {
$loaded = extension_loaded($extension);
}
$tableRows[] = [
'label' => "PHP extension $extension",
'required' => 'enabled',
'current' => (int)$loaded,
'result' => $this->yesOrNo($loaded),
];
}
$tableRows[] = [
'label' => 'PHP extension gd JPEG Support',
'required' => 'true',
'current' => $gdInfo['JPEG Support'] ?? '',
'result' => $this->yesOrNo($gdInfo['JPEG Support'] ?? ''),
];
$tableRows[] = [
'label' => 'PHP extension gd PNG Support',
'required' => 'true',
'current' => $gdInfo['PNG Support'] ?? '',
'result' => $this->yesOrNo($gdInfo['PNG Support'] ?? ''),
];
$tableRows[] = [
'label' => 'PHP extension gd GIF Read Support',
'required' => 'true',
'current' => $gdInfo['GIF Read Support'] ?? '',
'result' => $this->yesOrNo($gdInfo['GIF Read Support'] ?? ''),
];
foreach ($this->optionalExtensions as $extension) {
$tableRows[] = [
'label' => "PHP extension " . $extension['name'],
'required' => 'enabled',
'current' => (int)extension_loaded($extension['name']),
'result' => $extension['desc'],
];
}
$fails = array_filter($tableRows, function ($value) use ($phpVersionRequire) {
return in_array($value['required'], ['true', 'enabled', $phpVersionRequire]) && $value['result'] == 'NO';
});
$pass = empty($fails);
return [
'table_rows' => $tableRows,
'fails' => $fails,
'pass' => $pass,
];
}
public function listSettingTableRows()
{
$defaultSettingsFile = __DIR__ . '/settings.default.php';
$originalConfigFile = ROOT_PATH . 'config/allconfig.php';
if (!file_exists($defaultSettingsFile)) {
throw new \RuntimeException("default setting file: $defaultSettingsFile not exists.");
}
if (!file_exists($originalConfigFile)) {
throw new \RuntimeException("original setting file: $originalConfigFile not exists.");
}
$tableRows = [
[
'label' => basename($defaultSettingsFile),
'required' => 'exists && readable',
'current' => $defaultSettingsFile,
'result' => $this->yesOrNo(file_exists($defaultSettingsFile) && is_readable($defaultSettingsFile)),
],
[
'label' => basename($originalConfigFile),
'required' => 'exists && readable',
'current' => $originalConfigFile,
'result' => $this->yesOrNo(file_exists($originalConfigFile) && is_readable($originalConfigFile)),
],
];
$requireDirs = [
'main' => ['bitbucket', ],
'attachment' => ['savedirectory', ],
];
$symbolicLinks = [];
require $originalConfigFile;
$settings = require $defaultSettingsFile;
$settingsFromDb = [];
if (NexusDB::hasTable('settings') && Setting::query()->count() > 0) {
if (!NexusDB::hasColumn('settings', 'autoload')) {
$this->runMigrate('database/migrations/2022_05_06_191830_add_autoload_to_settings_table.php');
}
$settingsFromDb = Setting::getFromDb();
}
$this->doLog("settings form db: " . json_encode($settingsFromDb));
foreach ($settings as $prefix => &$group) {
$prefixUpperCase = strtoupper($prefix);
$oldGroupValues = $$prefixUpperCase ?? null;
foreach ($group as $key => &$value) {
//merge original config or db config to default setting, exclude code part
if ($prefix != 'code') {
if (isset($settingsFromDb[$prefix][$key])) {
$this->doLog(sprintf(
"$prefix.$key, db exists, change from: %s => %s",
is_scalar($value) ? $value : json_encode($value),
is_scalar($settingsFromDb[$prefix][$key]) ? $settingsFromDb[$prefix][$key] : json_encode($settingsFromDb[$prefix][$key]))
);
$value = $settingsFromDb[$prefix][$key];
} elseif (isset($oldGroupValues) && isset($oldGroupValues[$key])) {
$this->doLog(sprintf(
"$prefix.$key, original config file exists, change from: %s => %s",
is_scalar($value) ? $value : json_encode($value),
is_scalar($oldGroupValues[$key]) ? $oldGroupValues[$key] : json_encode($oldGroupValues[$key]))
);
$value = $oldGroupValues[$key];
}
}
if ($prefix == 'basic' && Str::startsWith($value, 'localhost')) {
$value = '';
}
if (isset($requireDirs[$prefix]) && in_array($key, $requireDirs[$prefix])) {
$dir = getFullDirectory($value);
$tableRows[] = [
'label' => "{$prefix}.{$key}",
'required' => 'exists && readable',
'current' => $dir,
'result' => $this->yesOrNo(is_dir($dir) && is_readable($dir)),
];
$symbolicLinks[] = $dir;
}
}
}
$fails = array_filter($tableRows, function ($value) {return $value['required'] == 'true' && $value['result'] == 'NO';});
$pass = empty($fails);
return [
'table_rows' => $tableRows,
'symbolic_links' => $symbolicLinks,
'settings' => $settings,
'fails' => $fails,
'pass' => $pass,
];
}
public function nextStep()
{
$this->doneStep($this->currentStep);
$this->gotoStep($this->currentStep + 1);
}
public function gotoStep($step)
{
if ($this->runningInConsole()) {
$this->currentStep = $step;
} else {
nexus_redirect(getBaseUrl() . "?step=$step");
die(0);
}
}
public function maxStep()
{
return count($this->steps);
}
public function yesOrNo($condition) {
if ($condition) {
return 'YES';
}
return 'NO';
}
public function renderTable($header, $data)
{
$table = '<div class="table w-full text-left">';
$table .= '<div class="table-row-group">';
$table .= '<div class="table-row">';
foreach ($header as $text) {
$table .= '<div class="table-cell bg-gray-400 text-gray-700 px-4 py-2">' . $text . '</div>';
}
$table .= '</div>';
foreach ($data as $value) {
$table .= '<div class="table-row">';
foreach ($header as $name => $text) {
$color = 'gray';
if ($name == 'result' && in_array($value[$name], ['YES', 'NO'])) {
$color = $value[$name] == 'YES' ? 'green' : 'red';
}
$table .= '<div class="table-cell bg-gray-200 text-' . $color . '-700 px-4 py-2 text-sm">' . $value[$name] . '</div>';
}
$table .= '</div>';
}
$table .= '</div>';
$table .= '</div>';
return $table;
}
public function renderForm($formControls, $formWidth = '1/2', $labelWidth = '1/3', $valueWidth = '2/3')
{
$form = '<div class="inline-block w-' . $formWidth . '">';
foreach ($formControls as $value) {
$form .= '<div class="flex mt-2">';
$form .= sprintf('<div class="w-%s flex justify-end items-center pr-10"><span>%s</span></div>', $labelWidth, $value['label']);
$form .= sprintf('<div class="w-%s flex justify-start items-center pr-10">', $valueWidth);
if (isset($value['type']) && $value['type'] == 'select') {
$form .= sprintf('<select class="border py-2 px-3 text-grey-darkest w-full" name="%s"/>', $value['name']);
foreach ($value['options'] as $option) {
$selected = '';
if ($option == $value['value']) {
$selected = ' selected';
}
$form .= sprintf('<option value="%s"%s>%s</option>', $option, $selected, $option);
}
$form .= '</select>';
} else {
$form .= sprintf(
'<input class="border py-2 px-3 text-grey-darkest w-full" type="text" name="%s" value="%s" />',
$value['name'], $value['value'] ?? ''
);
}
$form .= '</div>';
$form .= '</div>';
}
$form .= '</div>';
return $form;
}
public function renderSteps()
{
$steps = '<div class="flex mt-10 step text-center">';
$currentStep = $this->currentStep();
foreach ($this->steps as $key => $value) {
$steps .= sprintf('<div class="flex-1 %s">', $currentStep > $key + 1 ? 'text-green-500' : ($currentStep < $key + 1 ? 'text-gray-500' : ''));
$steps .= sprintf('<div>step %s</div>', $key + 1);
$steps .= sprintf('<div>%s</div>', $value);
$steps .= '</div>';
}
$steps .= '</div>';
return $steps;
}
public function listEnvFormControls()
{
$envExampleFile = ROOT_PATH . ".env.example";
$envExampleData = readEnvFile($envExampleFile);
$envFile = ROOT_PATH . '.env';
$envData = [];
if (file_exists($envFile) && is_readable($envFile)) {
//already exists, read it ,and merge
$envData = readEnvFile($envFile);
}
$mergeData = array_merge($envExampleData, $envData);
$formControls = [];
foreach ($this->envNames as $name) {
$value = $mergeData[$name];
if (isset($_POST[$name])) {
$value = $_POST[$name];
}
$item = [
'type' => 'text',
'options' => [],
'label' => $name,
'name' => $name,
'value' => $value
];
if ($name == 'TIMEZONE') {
$item['type'] = 'select';
$item['options'] = $this->listTimeZone();
}
$formControls[] = $item;
}
return $formControls;
}
public function createAdministrator($username, $email, $password, $confirmPassword)
{
$class = User::CLASS_STAFF_LEADER;
$count = get_row_count('users', 'where class = ' . $class);
if ($count > 0) {
throw new \InvalidArgumentException("Administrator already exists");
}
$data = [
'username' => $username,
'email' => $email,
'password' => $password,
'password_confirmation' => $confirmPassword,
'class' => $class,
'id' => 1,
];
$user = (new UserRepository())->store($data);
$this->doLog("[CREATE ADMINISTRATOR] " . $user->toJson());
return $user;
}
public function createEnvFile($data, $scene = 'install')
{
$envExampleFile = ROOT_PATH . ".env.example";
$envExampleData = readEnvFile($envExampleFile);
$envFile = ROOT_PATH . ".env";
$newData = [];
if (file_exists($envFile) && is_readable($envFile)) {
//already exists, read it ,and merge post data
$newData = readEnvFile($envFile);
$this->doLog("[CREATE ENV] .env exists, data: " . json_encode($newData));
}
$this->doLog("[CREATE ENV] newData: " . json_encode($newData));
foreach ($envExampleData as $key => $value) {
if (isset($data[$key])) {
$value = trim($data[$key]);
$this->doLog("[CREATE ENV] key: $key, new value: $value from post.");
$newData[$key] = $value;
} elseif (!isset($newData[$key])) {
$this->doLog("[CREATE ENV] key: $key, new value: $value from example.");
$newData[$key] = $value;
}
if ($key == 'CACHE_DRIVER') {
$newData[$key] = 'redis';
}
if ($key == 'QUEUE_CONNECTION') {
$newData[$key] = 'redis';
}
if ($scene == 'install' || !file_exists($envFile)) {
if ($key == 'APP_ENV') {
$newData[$key] = 'production';
}
if ($key == 'APP_DEBUG') {
$newData[$key] = 'false';
}
if ($key == 'LOG_LEVEL') {
$newData[$key] = 'info';
}
}
}
$this->doLog("[CREATE ENV] final newData: " . json_encode($newData));
unset($key, $value);
mysql_connect($newData['DB_HOST'], $newData['DB_USERNAME'], $newData['DB_PASSWORD'], $newData['DB_DATABASE'], (int)$newData['DB_PORT']);
$redis = new \Redis();
$redis->connect($newData['REDIS_HOST'], $newData['REDIS_PORT'] ?: 6379);
if (!empty($data['REDIS_PASSWORD'])) {
$redis->auth($data['REDIS_PASSWORD']);
}
if (isset($newData['REDIS_DB'])) {
if (!ctype_digit($newData['REDIS_DB']) || $newData['REDIS_DB'] < 0 || $newData['REDIS_DB'] > 15) {
throw new \InvalidArgumentException("invalid redis database: " . $newData['REDIS_DB']);
}
$redis->select($newData['REDIS_DB']);
}
$content = "";
foreach ($newData as $key => $value) {
$content .= "{$key}={$value}\n";
}
$fp = @fopen($envFile, 'w');
if ($fp === false) {
throw new \RuntimeException("can't create env file, make sure php has permission to create file at: " . ROOT_PATH);
}
fwrite($fp, $content);
fclose($fp);
$this->doLog("[CREATE ENV] $envFile with content: $content");
return true;
}
public function listShouldCreateTable()
{
$existsTable = $this->listExistsTable();
$tableCreate = $this->listAllTableCreateFromMigrations();
$shouldCreateTable = [];
foreach ($tableCreate as $table => $sql) {
if (in_array($table, $existsTable)) {
continue;
}
$shouldCreateTable[$table] = $sql;
}
return $shouldCreateTable;
}
public function createTable(array $createTable)
{
foreach ($createTable as $table => $sql) {
$this->doLog("[CREATE TABLE] $table \n $sql");
sql_query($sql);
}
return true;
}
public function saveSettings($settings)
{
if (!NexusDB::hasTable('settings')) {
$this->runMigrate('database/migrations/2021_06_08_113437_create_settings_table.php');
}
if (!NexusDB::hasColumn('settings', 'autoload')) {
$this->runMigrate('database/migrations/2022_05_06_191830_add_autoload_to_settings_table.php');
}
foreach ($settings as $prefix => $group) {
$this->doLog("[SAVE SETTING], prefix: $prefix, nameAndValues: " . json_encode($group));
saveSetting($prefix, $group);
}
}
public function createSymbolicLinks($symbolicLinks)
{
foreach ($symbolicLinks as $path) {
$linkName = ROOT_PATH . 'public/' . basename($path);
if (is_link($linkName) || is_file($linkName)) {
$delResult = unlink($linkName);
$this->doLog("path: $linkName already exits, try to delete it, delResult: " . var_export($delResult, true));
}
if (is_dir($linkName)) {
$this->doLog("path: $linkName already exits, skip create symbolic link $linkName -> $path");
continue;
}
$linkResult = symlink($path, $linkName);
if ($linkResult === false) {
throw new \RuntimeException("can not make symbolic link: $linkName -> $path");
}
$this->doLog("[CREATE SYMBOLIC LINK] success make symbolic link: $linkName -> $path");
}
return true;
}
public function importInitialData($sqlFile = '')
{
if (empty($sqlFile)) {
$sqlFile = ROOT_PATH . '_db/dbstructure_v1.6.sql';
}
$string = file_get_contents($sqlFile);
if ($string === false) {
throw new \RuntimeException("can't not read dbstructure file: $sqlFile");
}
//@todo test in php 7.3
$pattern = "/INSERT INTO `(\w+)` VALUES \(.*\);/i";
preg_match_all($pattern, $string, $matches, PREG_SET_ORDER);
$this->doLog("[IMPORT DATA] matches count: " . count($matches));
foreach ($matches as $match) {
$table = $match[1];
$sql = trim($match[0]);
if (!in_array($table, $this->initializeTables)) {
continue;
}
//if table not empty, skip
$count = get_row_count($table);
if ($count > 0) {
$this->doLog("[IMPORT DATA] $table, not empty, skip");
continue;
}
$this->doLog("[IMPORT DATA] $table, $sql");
sql_query("truncate table $table");
sql_query($sql);
}
return true;
}
public function runMigrate($path = null)
{
if (!WITH_LARAVEL) {
throw new \RuntimeException('Laravel is not available.');
}
$command = "php " . ROOT_PATH . "artisan migrate";
if (!is_null($path)) {
foreach ((array)$path as $key => $value) {
$command .= " --path=$value";
}
}
$command .= " --force";
$this->executeCommand($command);
$this->doLog("[MIGRATE] success.");
}
public function executeCommand($command)
{
executeCommand($command);
// $this->doLog("command: $command");
// $result = exec($command, $output, $result_code);
// $this->doLog(sprintf('result_code: %s, result: %s', $result_code, $result));
// $this->doLog("output: " . json_encode($output));
// if ($result_code != 0) {
// throw new \RuntimeException(json_encode($output));
// }
}
public function runDatabaseSeeder()
{
if (!WITH_LARAVEL) {
throw new \RuntimeException('Laravel is not available.');
}
$command = "php " . ROOT_PATH . "artisan db:seed --force";
$result = exec($command, $output, $result_code);
$this->doLog(sprintf('command: %s, result_code: %s, result: %s', $command, $result_code, $result));
$this->doLog("output: " . json_encode($output));
if ($result_code != 0) {
throw new \RuntimeException(json_encode($output));
} else {
$this->doLog("[DATABASE_SEED] success.");
}
}
public function listTimeZone()
{
$results = \DateTimeZone::listIdentifiers(\DateTimeZone::ALL);
$utc = array_pop($results);
array_unshift($results, $utc);
return $results;
}
public function getMysqlVersionInfo(): array
{
$sql = 'select version() as v';
$result = NexusDB::select($sql);
$version = $result[0]['v'];
$minVersion = '5.7.8';
$match = version_compare($version, $minVersion, '>=');
return compact('version', 'match', 'minVersion');
}
public function getRedisVersionInfo(): array
{
$redis = NexusDB::redis();
$result = $redis->info();
$version = $result['redis_version'];
$minVersion = '4.0.0';
$match = version_compare($version, $minVersion, '>=');
return compact('version', 'match', 'minVersion');
}
public function checkLock()
{
$fullFilename = ROOT_PATH . $this->lockFile;
if (file_exists($fullFilename)) {
die("Locked! Delete .lock file first");
}
}
public function setLock()
{
$fullFilename = ROOT_PATH . $this->lockFile;
$res = file_put_contents($fullFilename, "Lock at: " . date('Y-m-d H:i:s'));
$this->doLog("set lock at: $fullFilename, result: " . var_export($res, true));
}
public function getStepName($step): string
{
return $this->steps[$step - 1] ?? '';
}
public function migrateSearchBoxModeRelated()
{
$this->doLog("[migrateSearchBoxModeRelated]");
$searchBoxRep = new SearchBoxRepository();
$searchBoxRep->migrateToModeRelated();
}
/**
* 初始化,注意这里不能使用 get_tracker_schema_and_host()。里面会调用 TrackerUrl 这本来就是要往里面插入数据
*
* @param string $scene install or update
* @return void
*/
public function initTrackerUrl(string $scene): void
{
if ($scene == "update") {
$announceUrl = get_setting("security.https_announce_url");
if (empty($announceUrl)) {
$announceUrl = get_setting("basic.announce_url");
}
}
if (empty($announceUrl)) {
$announceUrl = sprintf(
"%s/%s",
trim($_SERVER['HTTP_HOST'], '/'), trim(DEFAULT_TRACKER_URI, '/')
);
}
if (!str_starts_with($announceUrl, "http")) {
$announceUrl = (isHttps() ? "https://" : "http://"). $announceUrl;
}
TrackerUrl::query()->create([
"url" => $announceUrl,
"enabled" => 1,
"is_default" => 1,
]);
TrackerUrl::saveUrlCache();
$this->doLog("[initTrackerUrl] $announceUrl success.");
}
}