Merge pull request #11519 from MrPetovan/task/11511-console-domain-move
Add relocate console command and fix the missing DB tables for the relocation
This commit is contained in:
commit
47a3d8e6ce
8 changed files with 661 additions and 521 deletions
205
src/Console/Relocate.php
Normal file
205
src/Console/Relocate.php
Normal file
|
@ -0,0 +1,205 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2022, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Console;
|
||||
|
||||
use Asika\SimpleConsole\Console;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Worker;
|
||||
use Friendica\Util\Strings;
|
||||
use Friendica\Worker\Delivery;
|
||||
|
||||
class Relocate extends Console
|
||||
{
|
||||
protected $helpOptions = ['h', 'help', '?'];
|
||||
|
||||
/**
|
||||
* @var IManageConfigValues
|
||||
*/
|
||||
private $config;
|
||||
/**
|
||||
* @var \Friendica\App\BaseURL
|
||||
*/
|
||||
private $baseUrl;
|
||||
/**
|
||||
* @var \Friendica\Database\Database
|
||||
*/
|
||||
private $database;
|
||||
|
||||
protected function getHelp()
|
||||
{
|
||||
$help = <<<HELP
|
||||
console relocate - Update the node base URL
|
||||
Usage
|
||||
bin/console relocate <new base URL> [-h|--help|-?] [-v]
|
||||
|
||||
Description
|
||||
Warning! Advanced function. Could make this server unreachable.
|
||||
|
||||
Change the base URL for this server. Sends relocation message to all the Friendica and Diaspora* contacts of all local users.
|
||||
This process updates all the database fields that may contain a URL pointing at the current domain, as a result it takes
|
||||
a while and the node will be in maintenance mode for the whole duration.
|
||||
|
||||
Options
|
||||
-h|--help|-? Show help information
|
||||
-v Show more debug information.
|
||||
HELP;
|
||||
return $help;
|
||||
}
|
||||
|
||||
public function __construct(\Friendica\App\BaseURL $baseUrl, \Friendica\Database\Database $database, IManageConfigValues $config, $argv = null)
|
||||
{
|
||||
parent::__construct($argv);
|
||||
|
||||
$this->baseUrl = $baseUrl;
|
||||
$this->database = $database;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
protected function doExecute()
|
||||
{
|
||||
if (count($this->args) == 0) {
|
||||
$this->out($this->getHelp());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count($this->args) > 1) {
|
||||
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
|
||||
}
|
||||
|
||||
$new_url = rtrim($this->getArgument(0), '/');
|
||||
|
||||
$parsed = @parse_url($new_url);
|
||||
if (!is_array($parsed) || empty($parsed['host']) || empty($parsed['scheme'])) {
|
||||
throw new \InvalidArgumentException('Can not parse new base URL. Must have at least <scheme>://<domain>');
|
||||
}
|
||||
|
||||
$this->out(sprintf('Relocation started from %s to %s. Could take a while to complete.', $this->baseUrl->get(true), $this->getArgument(0)));
|
||||
|
||||
$old_url = $this->baseUrl->get(true);
|
||||
|
||||
// Generate host names for relocation the addresses in the format user@address.tld
|
||||
$new_host = str_replace('http://', '@', Strings::normaliseLink($new_url));
|
||||
$old_host = str_replace('http://', '@', Strings::normaliseLink($old_url));
|
||||
|
||||
$this->out('Entering maintenance mode');
|
||||
$this->config->set('system', 'maintenance', true);
|
||||
$this->config->set('system', 'maintenance_reason', 'Relocating node to ' . $new_url);
|
||||
|
||||
try {
|
||||
if (!$this->database->transaction()) {
|
||||
throw new \Exception('Unable to start a transaction, please retry later.');
|
||||
}
|
||||
|
||||
// update tables
|
||||
$this->out('Updating apcontact table fields');
|
||||
$this->database->replaceInTableFields('apcontact', ['url', 'inbox', 'outbox', 'sharedinbox', 'photo', 'header', 'alias', 'subscribe', 'baseurl'], $old_url, $new_url);
|
||||
$this->database->replaceInTableFields('apcontact', ['addr'], $old_host, $new_host);
|
||||
|
||||
$this->out('Updating contact table fields');
|
||||
$this->database->replaceInTableFields('contact', ['photo', 'thumb', 'micro', 'url', 'alias', 'request', 'batch', 'notify', 'poll', 'subscribe', 'baseurl', 'confirm', 'poco', 'avatar', 'header'], $old_url, $new_url);
|
||||
$this->database->replaceInTableFields('contact', ['nurl'], Strings::normaliseLink($old_url), Strings::normaliseLink($new_url));
|
||||
$this->database->replaceInTableFields('contact', ['addr'], $old_host, $new_host);
|
||||
|
||||
$this->out('Updating conv table fields');
|
||||
$this->database->replaceInTableFields('conv', ['creator', 'recips'], $old_host, $new_host);
|
||||
|
||||
$this->out('Updating delayed-post table fields');
|
||||
$this->database->replaceInTableFields('delayed-post', ['uri'], $old_url, $new_url);
|
||||
|
||||
$this->out('Updating endpoint table fields');
|
||||
$this->database->replaceInTableFields('endpoint', ['url'], $old_url, $new_url);
|
||||
|
||||
$this->out('Updating event table fields');
|
||||
$this->database->replaceInTableFields('event', ['uri'], $old_url, $new_url);
|
||||
|
||||
$this->out('Updating fcontact table fields');
|
||||
$this->database->replaceInTableFields('fcontact', ['url', 'photo', 'request', 'batch', 'poll', 'confirm', 'alias'], $old_url, $new_url);
|
||||
$this->database->replaceInTableFields('fcontact', ['addr'], $old_host, $new_host);
|
||||
|
||||
$this->out('Updating fsuggest table fields');
|
||||
$this->database->replaceInTableFields('fsuggest', ['url', 'request', 'photo'], $old_url, $new_url);
|
||||
|
||||
$this->out('Updating gserver table fields');
|
||||
$this->database->replaceInTableFields('gserver', ['url'], $old_url, $new_url);
|
||||
$this->database->replaceInTableFields('gserver', ['nurl'], Strings::normaliseLink($old_url), Strings::normaliseLink($new_url));
|
||||
|
||||
$this->out('Updating inbox-status table fields');
|
||||
$this->database->replaceInTableFields('inbox-status', ['url'], $old_url, $new_url);
|
||||
|
||||
$this->out('Updating item-uri table fields');
|
||||
$this->database->replaceInTableFields('item-uri', ['uri'], $old_url, $new_url);
|
||||
|
||||
$this->out('Updating mail table fields');
|
||||
$this->database->replaceInTableFields('mail', ['from-photo', 'from-url', 'uri', 'thr-parent'], $old_url, $new_url);
|
||||
$this->database->replaceInTableFields('mail', ['parent-uri'], $old_host, $new_host);
|
||||
|
||||
$this->out('Updating notify table fields');
|
||||
$this->database->replaceInTableFields('notify', ['url', 'photo', 'link', 'msg', 'name_cache', 'msg_cache'], $old_url, $new_url);
|
||||
|
||||
$this->out('Updating profile table fields');
|
||||
$this->database->replaceInTableFields('profile', ['photo', 'thumb'], $old_url, $new_url);
|
||||
|
||||
$this->out('Updating post-content table fields');
|
||||
$this->database->replaceInTableFields('post-content', ['body', 'raw-body', 'rendered-html', 'target', 'plink'], $old_url, $new_url);
|
||||
$this->database->replaceInTableFields('post-content', ['body', 'raw-body', 'rendered-html', 'target'], $old_host, $new_host);
|
||||
|
||||
$this->out('Updating post-history table fields');
|
||||
$this->database->replaceInTableFields('post-history', ['body', 'raw-body', 'rendered-html', 'target', 'plink'], $old_url, $new_url);
|
||||
$this->database->replaceInTableFields('post-history', ['body', 'raw-body', 'rendered-html', 'target'], $old_host, $new_host);
|
||||
|
||||
$this->out('Updating post-link table fields');
|
||||
$this->database->replaceInTableFields('post-link', ['url'], $old_url, $new_url);
|
||||
|
||||
$this->out('Updating post-media table fields');
|
||||
$this->database->replaceInTableFields('post-media', ['url', 'preview', 'author-url', 'author-image', 'publisher-url', 'publisher-image'], $old_url, $new_url);
|
||||
|
||||
$this->out('Updating tag table fields');
|
||||
$this->database->replaceInTableFields('tag', ['url'], $old_url, $new_url);
|
||||
|
||||
// update config
|
||||
$this->out('Updating config values');
|
||||
$this->config->set('system', 'url', $new_url);
|
||||
$this->baseUrl->saveByURL($new_url);
|
||||
|
||||
$this->database->commit();
|
||||
} catch (\Throwable $e) {
|
||||
$this->database->rollback();
|
||||
|
||||
$this->out('Process aborted with message: ' . $e->getMessage() . ' thrown in ' . $e->getFile() . ':' . $e->getLine());
|
||||
|
||||
return 1;
|
||||
} finally {
|
||||
$this->out('Leaving maintenance mode');
|
||||
$this->config->set('system', 'maintenance', false);
|
||||
$this->config->set('system', 'maintenance_reason', '');
|
||||
}
|
||||
|
||||
// send relocate
|
||||
$this->out('Schedule relocation messages to remote Friendica and Diaspora hosts');
|
||||
$users = $this->database->selectToArray('user', ['uid'], ['account_removed' => false, 'account_expired' => false]);
|
||||
foreach ($users as $user) {
|
||||
Worker::add(PRIORITY_HIGH, 'Notifier', Delivery::RELOCATION, $user['uid']);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -65,6 +65,7 @@ Commands:
|
|||
po2php Generate a strings.php file from a messages.po file
|
||||
typo Checks for parse errors in Friendica files
|
||||
postupdate Execute pending post update scripts (can last days)
|
||||
relocate Update node base URL
|
||||
serverblock Manage blocked servers
|
||||
storage Manage storage backend
|
||||
relay Manage ActivityPub relay servers
|
||||
|
@ -97,6 +98,7 @@ HELP;
|
|||
'postupdate' => Friendica\Console\PostUpdate::class,
|
||||
'po2php' => Friendica\Console\PoToPhp::class,
|
||||
'relay' => Friendica\Console\Relay::class,
|
||||
'relocate' => Friendica\Console\Relocate::class,
|
||||
'serverblock' => Friendica\Console\ServerBlock::class,
|
||||
'storage' => Friendica\Console\Storage::class,
|
||||
'test' => Friendica\Console\Test::class,
|
||||
|
|
|
@ -1153,7 +1153,7 @@ class Database
|
|||
*
|
||||
* @return boolean Was the command executed successfully?
|
||||
*/
|
||||
public function transaction()
|
||||
public function transaction(): bool
|
||||
{
|
||||
if (!$this->performCommit()) {
|
||||
return false;
|
||||
|
@ -1790,4 +1790,32 @@ class Database
|
|||
{
|
||||
array_walk($arr, [$this, 'escapeArrayCallback'], $add_quotation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces a string in the provided fields of the provided table
|
||||
*
|
||||
* @param string $table_name
|
||||
* @param array $fields List of field names in the provided table
|
||||
* @param string $search
|
||||
* @param string $replace
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function replaceInTableFields(string $table_name, array $fields, string $search, string $replace)
|
||||
{
|
||||
$search = $this->escape($search);
|
||||
$replace = $this->escape($replace);
|
||||
|
||||
$upd = [];
|
||||
foreach ($fields as $field) {
|
||||
$field = DBA::quoteIdentifier($field);
|
||||
$upd[] = "$field = REPLACE($field, '$search', '$replace')";
|
||||
}
|
||||
|
||||
$upds = implode(', ', $upd);
|
||||
|
||||
$r = $this->e(sprintf("UPDATE %s SET %s;", $table_name, $upds));
|
||||
if (!$this->isResult($r)) {
|
||||
throw new \RuntimeException("Failed updating `$table_name`: " . $this->errorMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
namespace Friendica\Module\Admin;
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\Core\Relocate;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\Search;
|
||||
use Friendica\Core\System;
|
||||
|
@ -60,74 +61,6 @@ class Site extends BaseAdmin
|
|||
return;
|
||||
}
|
||||
|
||||
// relocate
|
||||
// @TODO This file could benefit from moving this feature away in a Module\Admin\Relocate class for example
|
||||
if (!empty($_POST['relocate']) && !empty($_POST['relocate_url']) && $_POST['relocate_url'] != "") {
|
||||
$new_url = $_POST['relocate_url'];
|
||||
$new_url = rtrim($new_url, "/");
|
||||
|
||||
$parsed = @parse_url($new_url);
|
||||
if (!is_array($parsed) || empty($parsed['host']) || empty($parsed['scheme'])) {
|
||||
notice(DI::l10n()->t("Can not parse base url. Must have at least <scheme>://<domain>"));
|
||||
DI::baseUrl()->redirect('admin/site');
|
||||
}
|
||||
|
||||
/* steps:
|
||||
* replace all "baseurl" to "new_url" in config, profile, term, items and contacts
|
||||
* send relocate for every local user
|
||||
* */
|
||||
|
||||
$old_url = DI::baseUrl()->get(true);
|
||||
|
||||
// Generate host names for relocation the addresses in the format user@address.tld
|
||||
$new_host = str_replace("http://", "@", Strings::normaliseLink($new_url));
|
||||
$old_host = str_replace("http://", "@", Strings::normaliseLink($old_url));
|
||||
|
||||
function update_table(App $a, $table_name, $fields, $old_url, $new_url)
|
||||
{
|
||||
$dbold = DBA::escape($old_url);
|
||||
$dbnew = DBA::escape($new_url);
|
||||
|
||||
$upd = [];
|
||||
foreach ($fields as $f) {
|
||||
$upd[] = "`$f` = REPLACE(`$f`, '$dbold', '$dbnew')";
|
||||
}
|
||||
|
||||
$upds = implode(", ", $upd);
|
||||
|
||||
$r = DBA::e(sprintf("UPDATE %s SET %s;", $table_name, $upds));
|
||||
if (!DBA::isResult($r)) {
|
||||
notice("Failed updating '$table_name': " . DBA::errorMessage());
|
||||
DI::baseUrl()->redirect('admin/site');
|
||||
}
|
||||
}
|
||||
|
||||
// update tables
|
||||
// update profile links in the format "http://server.tld"
|
||||
update_table($a, "profile", ['photo', 'thumb'], $old_url, $new_url);
|
||||
update_table($a, "contact", ['photo', 'thumb', 'micro', 'url', 'nurl', 'alias', 'request', 'notify', 'poll', 'confirm', 'poco', 'avatar'], $old_url, $new_url);
|
||||
update_table($a, "post-content", ['body'], $old_url, $new_url);
|
||||
|
||||
// update profile addresses in the format "user@server.tld"
|
||||
update_table($a, "contact", ['addr'], $old_host, $new_host);
|
||||
|
||||
// update config
|
||||
DI::config()->set('system', 'url', $new_url);
|
||||
DI::baseUrl()->saveByURL($new_url);
|
||||
|
||||
// send relocate
|
||||
$usersStmt = DBA::select('user', ['uid'], ['account_removed' => false, 'account_expired' => false]);
|
||||
while ($user = DBA::fetch($usersStmt)) {
|
||||
Worker::add(PRIORITY_HIGH, 'Notifier', Delivery::RELOCATION, $user['uid']);
|
||||
}
|
||||
DBA::close($usersStmt);
|
||||
|
||||
info(DI::l10n()->t("Relocation started. Could take a while to complete."));
|
||||
|
||||
DI::baseUrl()->redirect('admin/site');
|
||||
}
|
||||
// end relocate
|
||||
|
||||
$sitename = (!empty($_POST['sitename']) ? trim($_POST['sitename']) : '');
|
||||
$sender_email = (!empty($_POST['sender_email']) ? trim($_POST['sender_email']) : '');
|
||||
$banner = (!empty($_POST['banner']) ? trim($_POST['banner']) : false);
|
||||
|
@ -512,8 +445,9 @@ class Site extends BaseAdmin
|
|||
'$no_relay_list' => DI::l10n()->t('The system is not subscribed to any relays at the moment.'),
|
||||
'$relay_list_title' => DI::l10n()->t('The system is currently subscribed to the following relays:'),
|
||||
'$relay_list' => Relay::getList(['url']),
|
||||
'$relocate' => DI::l10n()->t('Relocate Instance'),
|
||||
'$relocate_warning' => DI::l10n()->t('<strong>Warning!</strong> Advanced function. Could make this server unreachable.'),
|
||||
'$relocate' => DI::l10n()->t('Relocate Node'),
|
||||
'$relocate_msg' => DI::l10n()->t('Relocating your node enables you to change the DNS domain of this node and keep all the existing users and posts. This process takes a while and can only be started from the relocate console command like this:'),
|
||||
'$relocate_cmd' => DI::l10n()->t('(Friendica directory)# bin/console relocate https://newdomain.com'),
|
||||
'$baseurl' => DI::baseUrl()->get(true),
|
||||
|
||||
// name, label, value, help string, extra data...
|
||||
|
@ -601,8 +535,6 @@ class Site extends BaseAdmin
|
|||
'$temppath' => ['temppath', DI::l10n()->t('Temp path'), DI::config()->get('system', 'temppath'), DI::l10n()->t('If you have a restricted system where the webserver can\'t access the system temp path, enter another path here.')],
|
||||
'$only_tag_search' => ['only_tag_search', DI::l10n()->t('Only search in tags'), DI::config()->get('system', 'only_tag_search'), DI::l10n()->t('On large systems the text search can slow down the system extremely.')],
|
||||
|
||||
'$relocate_url' => ['relocate_url', DI::l10n()->t('New base url'), DI::baseUrl()->get(), DI::l10n()->t('Change base url for this server. Sends relocate message to all Friendica and Diaspora* contacts of all users.')],
|
||||
|
||||
'$worker_queues' => ['worker_queues', DI::l10n()->t('Maximum number of parallel workers'), DI::config()->get('system', 'worker_queues'), DI::l10n()->t('On shared hosters set this to %d. On larger systems, values of %d are great. Default value is %d.', 5, 20, 10)],
|
||||
'$worker_fastlane' => ['worker_fastlane', DI::l10n()->t('Enable fastlane'), DI::config()->get('system', 'worker_fastlane'), DI::l10n()->t('When enabed, the fastlane mechanism starts an additional worker if processes with higher priority are blocked by processes of lower priority.')],
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ class StaticDatabase extends Database
|
|||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function transaction()
|
||||
public function transaction(): bool
|
||||
{
|
||||
if (!$this->in_transaction && !$this->connection->beginTransaction()) {
|
||||
return false;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -148,14 +148,10 @@
|
|||
|
||||
</form>
|
||||
|
||||
{{* separate form for relocate... *}}
|
||||
<form action="{{$baseurl}}/admin/site" method="post">
|
||||
<input type='hidden' name='form_security_token' value='{{$form_security_token}}'>
|
||||
<div>
|
||||
<h2>{{$relocate}}</h2>
|
||||
<p>{{$relocate_warning nofilter}}</p>
|
||||
{{include file="field_input.tpl" field=$relocate_url}}
|
||||
<input type="hidden" name="page_site" value="{{$submit}}">
|
||||
<div class="submit"><input type="submit" name="relocate" value="{{$relocate_button}}"/></div>
|
||||
</form>
|
||||
<p>{{$relocate_msg}}</p>
|
||||
<p><code>{{$relocate_cmd}}</code></p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -321,14 +321,7 @@
|
|||
</div>
|
||||
</form>
|
||||
|
||||
<!--
|
||||
/*
|
||||
* Relocate
|
||||
*/ -->
|
||||
<form id="relocate-form" class="panel" action="{{$baseurl}}/admin/site" method="post">
|
||||
<input type="hidden" name="form_security_token" value="{{$form_security_token}}">
|
||||
<input type="hidden" name="page_site" value="{{$submit}}">
|
||||
<input type="hidden" name="active_panel" value="admin-settings-relocate-collapse">
|
||||
<div class="panel">
|
||||
<div class="section-subtitle-wrapper panel-heading" role="tab" id="admin-settings-relocate">
|
||||
<h2>
|
||||
<button class="btn-link accordion-toggle collapsed" data-toggle="collapse" data-parent="#admin-settings" href="#admin-settings-relocate-collapse" aria-expanded="false" aria-controls="admin-settings-relocate-collapse">
|
||||
|
@ -338,15 +331,12 @@
|
|||
</div>
|
||||
<div id="admin-settings-relocate-collapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="admin-settings-relocate">
|
||||
<div class="panel-body">
|
||||
<div class="alert alert-danger alert-dismissible">
|
||||
{{$relocate_warning nofilter}}
|
||||
</div>
|
||||
{{include file="field_input.tpl" field=$relocate_url}}
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<input type="submit" name="relocate" class="btn btn-primary" value="{{$relocate_button}}"/>
|
||||
<p>
|
||||
{{$relocate_msg}}
|
||||
</p>
|
||||
<p><code>{{$relocate_cmd}}</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue