From b2826b5f4f932387aa4c11fa007a5f31922a745a Mon Sep 17 00:00:00 2001 From: dena Date: Mon, 10 Nov 2025 17:36:00 +0100 Subject: [PATCH 1/7] extend Wiki/Delete Command --- app/Console/Commands/Wiki/Delete.php | 44 +++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/app/Console/Commands/Wiki/Delete.php b/app/Console/Commands/Wiki/Delete.php index 78c1be772..2633c82b9 100644 --- a/app/Console/Commands/Wiki/Delete.php +++ b/app/Console/Commands/Wiki/Delete.php @@ -3,7 +3,10 @@ namespace App\Console\Commands\Wiki; use App\Wiki; +use App\WikiDb; +use Illuminate\Support\Facades\App; use Illuminate\Console\Command; +use Illuminate\Database\DatabaseManager; class Delete extends Command { public const SUCCESS = 'Success!'; @@ -12,15 +15,17 @@ class Delete extends Command { public const ERR_AMBIGUOUS_KEY_VALUE = 'Wiki deletion failed. Multiple wikis match the given key and value.'; + public const ERR_FAILED_DATA_DELETION = 'Deleting data in the wikis database failed.'; + protected $signature = 'wbs-wiki:delete {key} {value}'; - protected $description = 'Soft deletes the Wiki matching the given key and value.'; + protected $description = 'Soft deletes the Wiki matching the given key and value, while cleaning up some user data.'; public function handle(): int { $key = trim($this->argument('key')); $value = trim($this->argument('value')); - $wikis = Wiki::where($key, $value); + $wikis = Wiki::with('wikidb')->where($key, $value); if ($wikis->count() === 0) { $this->error(self::ERR_WIKI_DOES_NOT_EXIST); @@ -29,13 +34,44 @@ public function handle(): int { } elseif ($wikis->count() > 1) { $this->error(self::ERR_AMBIGUOUS_KEY_VALUE); - return 1; + return 2; } - $wikis->delete(); + $wiki = $wikis->first(); + + if (!$this->cleanupUserData($wiki)) { + $this->error(self::ERR_FAILED_DATA_DELETION); + $this->error($wiki); + + return 3; + } + + $wiki->delete(); $this->info(self::SUCCESS); return 0; } + + private function cleanupUserData($wiki): bool { + $wikiDb = $wiki->wikidb; + $prefix = $wikiDb->prefix; + + // Replaces current mw database connection config with scoped wiki credentials + app()->config->set('database.connections.mw.database', $wikiDb->name); + app()->config->set('database.connections.mw.username', $wikiDb->user); + app()->config->set('database.connections.mw.password', $wikiDb->password); + + $manager = App::make(DatabaseManager::class); + $mwConn = $manager->connection('mw'); + + if (!$mwConn instanceof \Illuminate\Database\Connection) { + throw new \RuntimeException('Must be run on a PDO based DB connection'); + } + + $mediawikiPdo = $mwConn->getPdo(); + $statement = $mediawikiPdo->prepare("UPDATE ${prefix}_user SET user_real_name = '', user_email = '', user_password = ''"); + + return $statement->execute(); + } } From 9a39e9fc26a4a2279fcd36d6e4d5948834a2e51c Mon Sep 17 00:00:00 2001 From: dena Date: Tue, 11 Nov 2025 17:08:49 +0100 Subject: [PATCH 2/7] refactor connection config reuse --- app/Console/Commands/Wiki/Delete.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/app/Console/Commands/Wiki/Delete.php b/app/Console/Commands/Wiki/Delete.php index 2633c82b9..bae84f807 100644 --- a/app/Console/Commands/Wiki/Delete.php +++ b/app/Console/Commands/Wiki/Delete.php @@ -57,13 +57,20 @@ private function cleanupUserData($wiki): bool { $wikiDb = $wiki->wikidb; $prefix = $wikiDb->prefix; - // Replaces current mw database connection config with scoped wiki credentials - app()->config->set('database.connections.mw.database', $wikiDb->name); - app()->config->set('database.connections.mw.username', $wikiDb->user); - app()->config->set('database.connections.mw.password', $wikiDb->password); + // Configure a DB connection with wiki credentials + $wikiDbConnectionConfig = array_replace( + config('database.connections.mw'), + [ + 'database' => $wikiDb->name, + 'username' => $wikiDb->user, + 'password' => $wikiDb->password, + ] + ); + + config('database.connections.singleWiki', $wikiDbConnectionConfig); $manager = App::make(DatabaseManager::class); - $mwConn = $manager->connection('mw'); + $mwConn = $manager->connection('singleWiki'); if (!$mwConn instanceof \Illuminate\Database\Connection) { throw new \RuntimeException('Must be run on a PDO based DB connection'); From e59e942920a1e8bfc3db7c83ed0b16d9cf27446f Mon Sep 17 00:00:00 2001 From: dena Date: Tue, 11 Nov 2025 19:06:29 +0100 Subject: [PATCH 3/7] move db drop to own command --- app/Console/Commands/Wiki/Delete.php | 51 ++-------------- app/Console/Commands/Wiki/DropDatabase.php | 70 ++++++++++++++++++++++ 2 files changed, 74 insertions(+), 47 deletions(-) create mode 100644 app/Console/Commands/Wiki/DropDatabase.php diff --git a/app/Console/Commands/Wiki/Delete.php b/app/Console/Commands/Wiki/Delete.php index bae84f807..78c1be772 100644 --- a/app/Console/Commands/Wiki/Delete.php +++ b/app/Console/Commands/Wiki/Delete.php @@ -3,10 +3,7 @@ namespace App\Console\Commands\Wiki; use App\Wiki; -use App\WikiDb; -use Illuminate\Support\Facades\App; use Illuminate\Console\Command; -use Illuminate\Database\DatabaseManager; class Delete extends Command { public const SUCCESS = 'Success!'; @@ -15,17 +12,15 @@ class Delete extends Command { public const ERR_AMBIGUOUS_KEY_VALUE = 'Wiki deletion failed. Multiple wikis match the given key and value.'; - public const ERR_FAILED_DATA_DELETION = 'Deleting data in the wikis database failed.'; - protected $signature = 'wbs-wiki:delete {key} {value}'; - protected $description = 'Soft deletes the Wiki matching the given key and value, while cleaning up some user data.'; + protected $description = 'Soft deletes the Wiki matching the given key and value.'; public function handle(): int { $key = trim($this->argument('key')); $value = trim($this->argument('value')); - $wikis = Wiki::with('wikidb')->where($key, $value); + $wikis = Wiki::where($key, $value); if ($wikis->count() === 0) { $this->error(self::ERR_WIKI_DOES_NOT_EXIST); @@ -34,51 +29,13 @@ public function handle(): int { } elseif ($wikis->count() > 1) { $this->error(self::ERR_AMBIGUOUS_KEY_VALUE); - return 2; - } - - $wiki = $wikis->first(); - - if (!$this->cleanupUserData($wiki)) { - $this->error(self::ERR_FAILED_DATA_DELETION); - $this->error($wiki); - - return 3; + return 1; } - $wiki->delete(); + $wikis->delete(); $this->info(self::SUCCESS); return 0; } - - private function cleanupUserData($wiki): bool { - $wikiDb = $wiki->wikidb; - $prefix = $wikiDb->prefix; - - // Configure a DB connection with wiki credentials - $wikiDbConnectionConfig = array_replace( - config('database.connections.mw'), - [ - 'database' => $wikiDb->name, - 'username' => $wikiDb->user, - 'password' => $wikiDb->password, - ] - ); - - config('database.connections.singleWiki', $wikiDbConnectionConfig); - - $manager = App::make(DatabaseManager::class); - $mwConn = $manager->connection('singleWiki'); - - if (!$mwConn instanceof \Illuminate\Database\Connection) { - throw new \RuntimeException('Must be run on a PDO based DB connection'); - } - - $mediawikiPdo = $mwConn->getPdo(); - $statement = $mediawikiPdo->prepare("UPDATE ${prefix}_user SET user_real_name = '', user_email = '', user_password = ''"); - - return $statement->execute(); - } } diff --git a/app/Console/Commands/Wiki/DropDatabase.php b/app/Console/Commands/Wiki/DropDatabase.php new file mode 100644 index 000000000..c75885b50 --- /dev/null +++ b/app/Console/Commands/Wiki/DropDatabase.php @@ -0,0 +1,70 @@ +argument('wikiDomain')); + $wiki = Wiki::with('wikidb')->firstWhere('domain', $wikiDomain); + + if (!$wiki) { + $this->error("Wiki not found by domain '$wikiDomain'"); + + return 1; + } + + if (!$this->dropWikiDb($wiki->wikidb)) { + $this->error('Failed to drop the mediawiki database of wiki.'); + $this->error($wiki); + + return 2; + } + + $this->info('MediaWiki database dropped.'); + + $wiki->delete(); + + $this->info('Wiki soft-deleted.'); + $this->info($wiki); + + return 0; + } + + private function dropWikiDb(WikiDb $wikiDb): bool { + $connection = $this->getWikiDbConnection($wikiDb); + + if (!$connection instanceof \Illuminate\Database\Connection) { + throw new \RuntimeException('Must be run on a PDO based DB connection'); + } + + $mediawikiPdo = $connection->getPdo(); + $statement = $mediawikiPdo->prepare('DROP DATABASE ' . $wikiDb->name); + + return $statement->execute([$wikiDb->name]); + } + + // Creates a temporary DB connection with wiki scoped credentials + private function getWikiDbConnection(WikiDb $wikiDb): mixed { + $wikiDbConnectionConfig = array_replace( + app()->config->get('database.connections.mw'), + [ + 'database' => $wikiDb->name, + 'username' => $wikiDb->user, + 'password' => $wikiDb->password, + ] + ); + + app()->config->set('database.connections.wikiTemp', $wikiDbConnectionConfig); + + return App::make(DatabaseManager::class)->connection('wikiTemp'); + } +} From fd7841b8441b0bdcb402fe809c14938d1debb5b1 Mon Sep 17 00:00:00 2001 From: dena Date: Tue, 11 Nov 2025 19:11:35 +0100 Subject: [PATCH 4/7] add test --- tests/Commands/Wiki/DropDatabaseTest.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/Commands/Wiki/DropDatabaseTest.php diff --git a/tests/Commands/Wiki/DropDatabaseTest.php b/tests/Commands/Wiki/DropDatabaseTest.php new file mode 100644 index 000000000..2916cf9fb --- /dev/null +++ b/tests/Commands/Wiki/DropDatabaseTest.php @@ -0,0 +1,21 @@ +artisan( + 'wbs-wiki:dropDatabase', [ + 'wikiDomain' => 'imaginarywiki.wbaas.dev', + ]) + ->assertExitCode(1) + ->assertFailed(); + } +} From bd57703538025d3770829b2e3a3be4375962575a Mon Sep 17 00:00:00 2001 From: dena Date: Tue, 11 Nov 2025 19:14:18 +0100 Subject: [PATCH 5/7] add comment --- app/Console/Commands/Wiki/DropDatabase.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/Console/Commands/Wiki/DropDatabase.php b/app/Console/Commands/Wiki/DropDatabase.php index c75885b50..f9b7dd4c2 100644 --- a/app/Console/Commands/Wiki/DropDatabase.php +++ b/app/Console/Commands/Wiki/DropDatabase.php @@ -39,6 +39,10 @@ public function handle(): int { return 0; } + // This is a rather ugly way to clean up some of the user data from a wiki that we are about to soft delete. + // + // This directly accesses the MW DB from the platform API which is a pattern we are generally trying to avoid but + // the database is likely never going to be read by MW every again since it's next step is hard deletion. private function dropWikiDb(WikiDb $wikiDb): bool { $connection = $this->getWikiDbConnection($wikiDb); From 2f5e5532347d0a7eeafcf87c7a2c8653511bd44a Mon Sep 17 00:00:00 2001 From: dena Date: Tue, 11 Nov 2025 19:19:47 +0100 Subject: [PATCH 6/7] linting --- app/Console/Commands/Wiki/DropDatabase.php | 5 +++-- tests/Commands/Wiki/DropDatabaseTest.php | 2 -- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/Console/Commands/Wiki/DropDatabase.php b/app/Console/Commands/Wiki/DropDatabase.php index f9b7dd4c2..3f07af06c 100644 --- a/app/Console/Commands/Wiki/DropDatabase.php +++ b/app/Console/Commands/Wiki/DropDatabase.php @@ -4,16 +4,17 @@ use App\Wiki; use App\WikiDb; -use Illuminate\Support\Facades\App; use Illuminate\Console\Command; use Illuminate\Database\DatabaseManager; +use Illuminate\Support\Facades\App; class dropDatabase extends Command { protected $signature = 'wbs-wiki:dropDatabase {wikiDomain}'; + protected $description = 'Drops the MediaWiki database of a wiki and soft-deletes it.'; public function handle(): int { - $wikiDomain = trim($this->argument('wikiDomain')); + $wikiDomain = trim($this->argument('wikiDomain')); $wiki = Wiki::with('wikidb')->firstWhere('domain', $wikiDomain); if (!$wiki) { diff --git a/tests/Commands/Wiki/DropDatabaseTest.php b/tests/Commands/Wiki/DropDatabaseTest.php index 2916cf9fb..4e2e2c1ae 100644 --- a/tests/Commands/Wiki/DropDatabaseTest.php +++ b/tests/Commands/Wiki/DropDatabaseTest.php @@ -2,8 +2,6 @@ namespace Tests\Commands; -use App\Console\Commands\Wiki\Delete; -use App\Wiki; use Illuminate\Foundation\Testing\DatabaseTransactions; use Tests\TestCase; From 03401909322e7140059be60e3a260c286e67c173 Mon Sep 17 00:00:00 2001 From: Thomas Arrow Date: Wed, 12 Nov 2025 11:47:06 +0000 Subject: [PATCH 7/7] remove outdated comment --- app/Console/Commands/Wiki/DropDatabase.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/Console/Commands/Wiki/DropDatabase.php b/app/Console/Commands/Wiki/DropDatabase.php index 3f07af06c..69756e33b 100644 --- a/app/Console/Commands/Wiki/DropDatabase.php +++ b/app/Console/Commands/Wiki/DropDatabase.php @@ -40,10 +40,6 @@ public function handle(): int { return 0; } - // This is a rather ugly way to clean up some of the user data from a wiki that we are about to soft delete. - // - // This directly accesses the MW DB from the platform API which is a pattern we are generally trying to avoid but - // the database is likely never going to be read by MW every again since it's next step is hard deletion. private function dropWikiDb(WikiDb $wikiDb): bool { $connection = $this->getWikiDbConnection($wikiDb);