mirror of
https://github.com/vitodeploy/vito.git
synced 2025-04-19 18:01:37 +00:00
Database collations (#489)
* SyncDatabases * Collation on Create inc WordPress * Refactored Enum * Resolve sync issue * Fix for PostgreSQL * pint * reversed enum * style adjustments * add unit tests * style * fix tests * more tests --------- Co-authored-by: Saeed Vaziry <61919774+saeedvaziry@users.noreply.github.com> Co-authored-by: Saeed Vaziry <mr.saeedvaziry@gmail.com>
This commit is contained in:
parent
269ee8d962
commit
5a12ed76bb
@ -14,11 +14,13 @@ public function create(Server $server, array $input): Database
|
|||||||
{
|
{
|
||||||
$database = new Database([
|
$database = new Database([
|
||||||
'server_id' => $server->id,
|
'server_id' => $server->id,
|
||||||
|
'charset' => $input['charset'],
|
||||||
|
'collation' => $input['collation'],
|
||||||
'name' => $input['name'],
|
'name' => $input['name'],
|
||||||
]);
|
]);
|
||||||
/** @var \App\SSH\Services\Database\Database $databaseHandler */
|
/** @var \App\SSH\Services\Database\Database $databaseHandler */
|
||||||
$databaseHandler = $server->database()->handler();
|
$databaseHandler = $server->database()->handler();
|
||||||
$databaseHandler->create($database->name);
|
$databaseHandler->create($database->name, $database->charset, $database->collation);
|
||||||
$database->status = DatabaseStatus::READY;
|
$database->status = DatabaseStatus::READY;
|
||||||
$database->save();
|
$database->save();
|
||||||
|
|
||||||
@ -42,6 +44,14 @@ public static function rules(Server $server, array $input): array
|
|||||||
'alpha_dash',
|
'alpha_dash',
|
||||||
Rule::unique('databases', 'name')->where('server_id', $server->id),
|
Rule::unique('databases', 'name')->where('server_id', $server->id),
|
||||||
],
|
],
|
||||||
|
'charset' => [
|
||||||
|
'required',
|
||||||
|
'string',
|
||||||
|
],
|
||||||
|
'collation' => [
|
||||||
|
'required',
|
||||||
|
'string',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
if (isset($input['user']) && $input['user']) {
|
if (isset($input['user']) && $input['user']) {
|
||||||
$rules['username'] = [
|
$rules['username'] = [
|
||||||
|
@ -41,6 +41,8 @@ public function index(Project $project, Server $server): ResourceCollection
|
|||||||
#[Post('/', name: 'api.projects.servers.databases.create', middleware: 'ability:write')]
|
#[Post('/', name: 'api.projects.servers.databases.create', middleware: 'ability:write')]
|
||||||
#[Endpoint(title: 'create', description: 'Create a new database.')]
|
#[Endpoint(title: 'create', description: 'Create a new database.')]
|
||||||
#[BodyParam(name: 'name', required: true)]
|
#[BodyParam(name: 'name', required: true)]
|
||||||
|
#[BodyParam(name: 'charset', required: true)]
|
||||||
|
#[BodyParam(name: 'collation', required: true)]
|
||||||
#[ResponseFromApiResource(DatabaseResource::class, Database::class)]
|
#[ResponseFromApiResource(DatabaseResource::class, Database::class)]
|
||||||
public function create(Request $request, Project $project, Server $server): DatabaseResource
|
public function create(Request $request, Project $project, Server $server): DatabaseResource
|
||||||
{
|
{
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
/**
|
/**
|
||||||
* @property int $server_id
|
* @property int $server_id
|
||||||
* @property string $name
|
* @property string $name
|
||||||
|
* @property string $collation
|
||||||
|
* @property string $charset
|
||||||
* @property string $status
|
* @property string $status
|
||||||
* @property Server $server
|
* @property Server $server
|
||||||
* @property Backup[] $backups
|
* @property Backup[] $backups
|
||||||
@ -25,6 +27,8 @@ class Database extends AbstractModel
|
|||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'server_id',
|
'server_id',
|
||||||
'name',
|
'name',
|
||||||
|
'collation',
|
||||||
|
'charset',
|
||||||
'status',
|
'status',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -11,6 +11,16 @@
|
|||||||
|
|
||||||
abstract class AbstractDatabase extends AbstractService implements Database
|
abstract class AbstractDatabase extends AbstractService implements Database
|
||||||
{
|
{
|
||||||
|
protected array $systemDbs = [];
|
||||||
|
|
||||||
|
protected string $defaultCharset;
|
||||||
|
|
||||||
|
protected string $separator = "\t";
|
||||||
|
|
||||||
|
protected int $headerLines = 1;
|
||||||
|
|
||||||
|
protected bool $removeLastRow = false;
|
||||||
|
|
||||||
protected function getScriptView(string $script): string
|
protected function getScriptView(string $script): string
|
||||||
{
|
{
|
||||||
return 'ssh.services.database.'.$this->service->name.'.'.$script;
|
return 'ssh.services.database.'.$this->service->name.'.'.$script;
|
||||||
@ -43,6 +53,9 @@ public function install(): void
|
|||||||
$status = $this->service->server->systemd()->status($this->service->unit);
|
$status = $this->service->server->systemd()->status($this->service->unit);
|
||||||
$this->service->validateInstall($status);
|
$this->service->validateInstall($status);
|
||||||
$this->service->server->os()->cleanup();
|
$this->service->server->os()->cleanup();
|
||||||
|
|
||||||
|
$this->updateCharsets();
|
||||||
|
$this->syncDatabases();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deletionRules(): array
|
public function deletionRules(): array
|
||||||
@ -83,11 +96,13 @@ public function uninstall(): void
|
|||||||
/**
|
/**
|
||||||
* @throws SSHError
|
* @throws SSHError
|
||||||
*/
|
*/
|
||||||
public function create(string $name): void
|
public function create(string $name, string $charset, string $collation): void
|
||||||
{
|
{
|
||||||
$this->service->server->ssh()->exec(
|
$this->service->server->ssh()->exec(
|
||||||
view($this->getScriptView('create'), [
|
view($this->getScriptView('create'), [
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
|
'charset' => $charset,
|
||||||
|
'collation' => $collation,
|
||||||
]),
|
]),
|
||||||
'create-database'
|
'create-database'
|
||||||
);
|
);
|
||||||
@ -219,4 +234,124 @@ public function restoreBackup(BackupFile $backupFile, string $database): void
|
|||||||
'restore-database'
|
'restore-database'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws SSHError
|
||||||
|
*/
|
||||||
|
public function updateCharsets(): void
|
||||||
|
{
|
||||||
|
$data = $this->service->server->ssh()->exec(
|
||||||
|
view($this->getScriptView('get-charsets')),
|
||||||
|
'get-database-charsets'
|
||||||
|
);
|
||||||
|
|
||||||
|
$charsets = $this->tableToArray($data);
|
||||||
|
|
||||||
|
$results = [];
|
||||||
|
$charsetCollations = [];
|
||||||
|
|
||||||
|
foreach ($charsets as $key => $charset) {
|
||||||
|
if (empty($charsetCollations[$charset[1]])) {
|
||||||
|
$charsetCollations[$charset[1]] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$charsetCollations[$charset[1]][] = $charset[0];
|
||||||
|
|
||||||
|
if ($charset[3] === 'Yes') {
|
||||||
|
$results[$charset[1]] = [
|
||||||
|
'default' => $charset[0],
|
||||||
|
'list' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($key == count($charsets) - 1) {
|
||||||
|
$results[$charset[1]] = [
|
||||||
|
'default' => null,
|
||||||
|
'list' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($results as $charset => $data) {
|
||||||
|
$results[$charset]['list'] = $charsetCollations[$charset];
|
||||||
|
}
|
||||||
|
|
||||||
|
ksort($results);
|
||||||
|
|
||||||
|
$data = array_merge(
|
||||||
|
$this->service->type_data ?? [],
|
||||||
|
['charsets' => $results, 'defaultCharset' => $this->defaultCharset]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->service->update(['type_data' => $data]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws SSHError
|
||||||
|
*/
|
||||||
|
public function syncDatabases(bool $createNew = true): void
|
||||||
|
{
|
||||||
|
$data = $this->service->server->ssh()->exec(
|
||||||
|
view($this->getScriptView('get-db-list')),
|
||||||
|
'get-db-list'
|
||||||
|
);
|
||||||
|
|
||||||
|
$databases = $this->tableToArray($data);
|
||||||
|
|
||||||
|
foreach ($databases as $database) {
|
||||||
|
if (in_array($database[0], $this->systemDbs)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = $this->service->server->databases()
|
||||||
|
->where('name', $database[0])
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if ($db === null) {
|
||||||
|
if ($createNew) {
|
||||||
|
$this->service->server->databases()->create([
|
||||||
|
'name' => $database[0],
|
||||||
|
'collation' => $database[2],
|
||||||
|
'charset' => $database[1],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($db->collation !== $database[2] || $db->charset !== $database[1]) {
|
||||||
|
$db->update([
|
||||||
|
'collation' => $database[2],
|
||||||
|
'charset' => $database[1],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tableToArray(string $data, bool $keepHeader = false): array
|
||||||
|
{
|
||||||
|
$lines = explode("\n", trim($data));
|
||||||
|
|
||||||
|
if (! $keepHeader) {
|
||||||
|
for ($i = 0; $i < $this->headerLines; $i++) {
|
||||||
|
array_shift($lines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->removeLastRow) {
|
||||||
|
array_pop($lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows = [];
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
$row = explode($this->separator, $line);
|
||||||
|
$row = array_map('trim', $row);
|
||||||
|
$rows[] = $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rows;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
interface Database
|
interface Database
|
||||||
{
|
{
|
||||||
public function create(string $name): void;
|
public function create(string $name, string $charset, string $collation): void;
|
||||||
|
|
||||||
public function delete(string $name): void;
|
public function delete(string $name): void;
|
||||||
|
|
||||||
@ -21,4 +21,8 @@ public function unlink(string $username, string $host): void;
|
|||||||
public function runBackup(BackupFile $backupFile): void;
|
public function runBackup(BackupFile $backupFile): void;
|
||||||
|
|
||||||
public function restoreBackup(BackupFile $backupFile, string $database): void;
|
public function restoreBackup(BackupFile $backupFile, string $database): void;
|
||||||
|
|
||||||
|
public function updateCharsets(): void;
|
||||||
|
|
||||||
|
public function syncDatabases(bool $createNew = true): void;
|
||||||
}
|
}
|
||||||
|
@ -4,5 +4,7 @@
|
|||||||
|
|
||||||
class Mariadb extends AbstractDatabase
|
class Mariadb extends AbstractDatabase
|
||||||
{
|
{
|
||||||
//
|
protected array $systemDbs = ['information_schema', 'performance_schema', 'mysql', 'sys'];
|
||||||
|
|
||||||
|
protected string $defaultCharset = 'utf8mb3';
|
||||||
}
|
}
|
||||||
|
@ -4,5 +4,7 @@
|
|||||||
|
|
||||||
class Mysql extends AbstractDatabase
|
class Mysql extends AbstractDatabase
|
||||||
{
|
{
|
||||||
//
|
protected array $systemDbs = ['information_schema', 'performance_schema', 'mysql', 'sys'];
|
||||||
|
|
||||||
|
protected string $defaultCharset = 'utf8mb3';
|
||||||
}
|
}
|
||||||
|
@ -4,5 +4,13 @@
|
|||||||
|
|
||||||
class Postgresql extends AbstractDatabase
|
class Postgresql extends AbstractDatabase
|
||||||
{
|
{
|
||||||
//
|
protected array $systemDbs = ['template0', 'template1', 'postgres'];
|
||||||
|
|
||||||
|
protected string $defaultCharset = 'UTF8';
|
||||||
|
|
||||||
|
protected int $headerLines = 2;
|
||||||
|
|
||||||
|
protected string $separator = '|';
|
||||||
|
|
||||||
|
protected bool $removeLastRow = true;
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,8 @@ public function data(array $input): array
|
|||||||
'email' => $input['email'],
|
'email' => $input['email'],
|
||||||
'password' => $input['password'],
|
'password' => $input['password'],
|
||||||
'database' => $input['database'],
|
'database' => $input['database'],
|
||||||
|
'database_charset' => $input['charset'],
|
||||||
|
'database_collation' => $input['collation'],
|
||||||
'database_user' => $input['database_user'],
|
'database_user' => $input['database_user'],
|
||||||
'database_password' => $input['database_password'],
|
'database_password' => $input['database_password'],
|
||||||
];
|
];
|
||||||
@ -96,20 +98,28 @@ public function install(): void
|
|||||||
$webserver = $this->site->server->webserver()->handler();
|
$webserver = $this->site->server->webserver()->handler();
|
||||||
$webserver->createVHost($this->site);
|
$webserver->createVHost($this->site);
|
||||||
$this->progress(30);
|
$this->progress(30);
|
||||||
|
|
||||||
/** @var Database $database */
|
/** @var Database $database */
|
||||||
$database = app(CreateDatabase::class)->create($this->site->server, [
|
$database = app(CreateDatabase::class)->create($this->site->server, [
|
||||||
'name' => $this->site->type_data['database'],
|
'name' => $this->site->type_data['database'],
|
||||||
|
'charset' => $this->site->type_data['database_charset'],
|
||||||
|
'collation' => $this->site->type_data['database_collation'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/** @var DatabaseUser $databaseUser */
|
/** @var DatabaseUser $databaseUser */
|
||||||
$databaseUser = app(CreateDatabaseUser::class)->create($this->site->server, [
|
$databaseUser = app(CreateDatabaseUser::class)->create($this->site->server, [
|
||||||
'username' => $this->site->type_data['database_user'],
|
'username' => $this->site->type_data['database_user'],
|
||||||
'password' => $this->site->type_data['database_password'],
|
'password' => $this->site->type_data['database_password'],
|
||||||
|
'collation' => $this->site->type_data['database_collation'],
|
||||||
|
'charset' => $this->site->type_data['database_charset'],
|
||||||
'remote' => false,
|
'remote' => false,
|
||||||
'host' => 'localhost',
|
'host' => 'localhost',
|
||||||
], [$database->name]);
|
], [$database->name]);
|
||||||
|
|
||||||
app(LinkUser::class)->link($databaseUser, [
|
app(LinkUser::class)->link($databaseUser, [
|
||||||
'databases' => [$database->name],
|
'databases' => [$database->name],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->site->php()?->restart();
|
$this->site->php()?->restart();
|
||||||
$this->progress(60);
|
$this->progress(60);
|
||||||
app(\App\SSH\Wordpress\Wordpress::class)->install($this->site);
|
app(\App\SSH\Wordpress\Wordpress::class)->install($this->site);
|
||||||
|
@ -4,11 +4,15 @@
|
|||||||
|
|
||||||
use App\Actions\Database\CreateDatabase;
|
use App\Actions\Database\CreateDatabase;
|
||||||
use App\Models\Database;
|
use App\Models\Database;
|
||||||
|
use App\Models\Server;
|
||||||
use App\Web\Contracts\HasSecondSubNav;
|
use App\Web\Contracts\HasSecondSubNav;
|
||||||
use App\Web\Pages\Servers\Page;
|
use App\Web\Pages\Servers\Page;
|
||||||
use Filament\Actions\Action;
|
use Filament\Actions\Action;
|
||||||
use Filament\Forms\Components\Checkbox;
|
use Filament\Forms\Components\Checkbox;
|
||||||
|
use Filament\Forms\Components\Select;
|
||||||
use Filament\Forms\Components\TextInput;
|
use Filament\Forms\Components\TextInput;
|
||||||
|
use Filament\Forms\Get;
|
||||||
|
use Filament\Forms\Set;
|
||||||
use Filament\Notifications\Notification;
|
use Filament\Notifications\Notification;
|
||||||
use Filament\Support\Enums\MaxWidth;
|
use Filament\Support\Enums\MaxWidth;
|
||||||
|
|
||||||
@ -25,6 +29,59 @@ public function mount(): void
|
|||||||
$this->authorize('viewAny', [Database::class, $this->server]);
|
$this->authorize('viewAny', [Database::class, $this->server]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getCharsetInput(Server $server): Select
|
||||||
|
{
|
||||||
|
return Select::make('charset')
|
||||||
|
->label('Charset / Encoding')
|
||||||
|
->native(false)
|
||||||
|
->live()
|
||||||
|
->default(function () use ($server) {
|
||||||
|
$service = $server->defaultService('database');
|
||||||
|
|
||||||
|
return $service->type_data['defaultCharset'] ?? null;
|
||||||
|
})
|
||||||
|
->options(function () use ($server) {
|
||||||
|
$service = $server->defaultService('database');
|
||||||
|
$charsets = $service->type_data['charsets'] ?? [];
|
||||||
|
|
||||||
|
return array_combine(
|
||||||
|
array_keys($charsets),
|
||||||
|
array_keys($charsets)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
->afterStateUpdated(function (Get $get, Set $set, $state) use ($server) {
|
||||||
|
$service = $server->defaultService('database');
|
||||||
|
$charsets = $service->type_data['charsets'] ?? [];
|
||||||
|
$set('collation', $charsets[$state]['default'] ?? null);
|
||||||
|
})
|
||||||
|
->rules(fn (callable $get) => CreateDatabase::rules($server, $get())['charset']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getCollationInput(Server $server): Select
|
||||||
|
{
|
||||||
|
return Select::make('collation')
|
||||||
|
->label('Collation')
|
||||||
|
->native(false)
|
||||||
|
->live()
|
||||||
|
->default(function (Get $get) use ($server) {
|
||||||
|
$service = $server->defaultService('database');
|
||||||
|
$charsets = $service->type_data['charsets'] ?? [];
|
||||||
|
$charset = $get('charset') ?? $service->type_data['default'] ?? 'utf8mb4';
|
||||||
|
|
||||||
|
return $charsets[$charset]['default'] ?? null;
|
||||||
|
})
|
||||||
|
->options(function (Get $get) use ($server) {
|
||||||
|
$service = $server->defaultService('database');
|
||||||
|
$collations = $service->type_data['charsets'][$get('charset')]['list'] ?? [];
|
||||||
|
|
||||||
|
return array_combine(
|
||||||
|
array_values($collations),
|
||||||
|
array_values($collations)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
->rules(fn (callable $get) => CreateDatabase::rules($server, $get())['collation']);
|
||||||
|
}
|
||||||
|
|
||||||
protected function getHeaderActions(): array
|
protected function getHeaderActions(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@ -36,6 +93,8 @@ protected function getHeaderActions(): array
|
|||||||
->form([
|
->form([
|
||||||
TextInput::make('name')
|
TextInput::make('name')
|
||||||
->rules(fn (callable $get) => CreateDatabase::rules($this->server, $get())['name']),
|
->rules(fn (callable $get) => CreateDatabase::rules($this->server, $get())['name']),
|
||||||
|
self::getCharsetInput($this->server),
|
||||||
|
self::getCollationInput($this->server),
|
||||||
Checkbox::make('user')
|
Checkbox::make('user')
|
||||||
->label('Create User')
|
->label('Create User')
|
||||||
->default(false)
|
->default(false)
|
||||||
|
@ -27,6 +27,12 @@ protected function getTableColumns(): array
|
|||||||
return [
|
return [
|
||||||
TextColumn::make('name')
|
TextColumn::make('name')
|
||||||
->searchable(),
|
->searchable(),
|
||||||
|
TextColumn::make('charset')
|
||||||
|
->label('Charset / Encoding')
|
||||||
|
->sortable(),
|
||||||
|
TextColumn::make('collation')
|
||||||
|
->label('Collation')
|
||||||
|
->sortable(),
|
||||||
TextColumn::make('status')
|
TextColumn::make('status')
|
||||||
->label('Status')
|
->label('Status')
|
||||||
->badge()
|
->badge()
|
||||||
|
@ -199,6 +199,8 @@ private function wordpressFields(): Component
|
|||||||
TextInput::make('database')
|
TextInput::make('database')
|
||||||
->helperText('It will create a database with this name')
|
->helperText('It will create a database with this name')
|
||||||
->rules(fn (Get $get) => CreateSite::rules($this->server, $get())['database']),
|
->rules(fn (Get $get) => CreateSite::rules($this->server, $get())['database']),
|
||||||
|
\App\Web\Pages\Servers\Databases\Index::getCharsetInput($this->server),
|
||||||
|
\App\Web\Pages\Servers\Databases\Index::getCollationInput($this->server),
|
||||||
TextInput::make('database_user')
|
TextInput::make('database_user')
|
||||||
->helperText('It will create a db user with this username')
|
->helperText('It will create a db user with this username')
|
||||||
->rules(fn (Get $get) => CreateSite::rules($this->server, $get())['database']),
|
->rules(fn (Get $get) => CreateSite::rules($this->server, $get())['database']),
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Enums\ServerStatus;
|
||||||
|
use App\Models\Server;
|
||||||
|
use App\SSH\Services\Database\Database;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('databases', function (Blueprint $table) {
|
||||||
|
$table->string('collation')->nullable();
|
||||||
|
$table->string('charset')->nullable();
|
||||||
|
});
|
||||||
|
|
||||||
|
$servers = Server::query()->where('status', ServerStatus::READY)->get();
|
||||||
|
|
||||||
|
/** @var Server $server */
|
||||||
|
foreach ($servers as $server) {
|
||||||
|
$service = $server->database();
|
||||||
|
|
||||||
|
if (! $service) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var Database $db */
|
||||||
|
$db = $service->handler();
|
||||||
|
|
||||||
|
$db->syncDatabases(false);
|
||||||
|
$db->updateCharsets();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('databases', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('collation');
|
||||||
|
$table->dropColumn('charset');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -1,8 +1,8 @@
|
|||||||
if ! sudo mysql -e "CREATE USER IF NOT EXISTS '{{ $username }}'@'{{ $host }}' IDENTIFIED BY '{{ $password }}'"; then
|
if ! sudo mariadb -e "CREATE USER IF NOT EXISTS '{{ $username }}'@'{{ $host }}' IDENTIFIED BY '{{ $password }}'"; then
|
||||||
echo 'VITO_SSH_ERROR' && exit 1
|
echo 'VITO_SSH_ERROR' && exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! sudo mysql -e "FLUSH PRIVILEGES"; then
|
if ! sudo mariadb -e "FLUSH PRIVILEGES"; then
|
||||||
echo 'VITO_SSH_ERROR' && exit 1
|
echo 'VITO_SSH_ERROR' && exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
if ! sudo mysql -e "CREATE DATABASE IF NOT EXISTS {{ $name }} CHARACTER SET utf8 COLLATE utf8_general_ci"; then
|
if ! sudo mariadb -e "CREATE DATABASE IF NOT EXISTS {{ $name }} CHARACTER SET '{{ $charset }}' COLLATE '{{ $collation }}'"; then
|
||||||
echo 'VITO_SSH_ERROR' && exit 1
|
echo 'VITO_SSH_ERROR' && exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
if ! sudo mysql -e "DROP USER IF EXISTS '{{ $username }}'@'{{ $host }}'"; then
|
if ! sudo mariadb -e "DROP USER IF EXISTS '{{ $username }}'@'{{ $host }}'"; then
|
||||||
echo 'VITO_SSH_ERROR' && exit 1
|
echo 'VITO_SSH_ERROR' && exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! sudo mysql -e "FLUSH PRIVILEGES"; then
|
if ! sudo mariadb -e "FLUSH PRIVILEGES"; then
|
||||||
echo 'VITO_SSH_ERROR' && exit 1
|
echo 'VITO_SSH_ERROR' && exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
if ! sudo mysql -e "DROP DATABASE IF EXISTS {{ $name }}"; then
|
if ! sudo mariadb -e "DROP DATABASE IF EXISTS {{ $name }}"; then
|
||||||
echo 'VITO_SSH_ERROR' && exit 1
|
echo 'VITO_SSH_ERROR' && exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
if ! sudo mariadb -e "SHOW COLLATION;";
|
||||||
|
then
|
||||||
|
echo 'VITO_SSH_ERROR' && exit 1
|
||||||
|
fi
|
@ -0,0 +1,10 @@
|
|||||||
|
if ! sudo mariadb -e "SELECT
|
||||||
|
SCHEMA_NAME AS database_name,
|
||||||
|
DEFAULT_CHARACTER_SET_NAME AS charset,
|
||||||
|
DEFAULT_COLLATION_NAME AS collation
|
||||||
|
FROM information_schema.SCHEMATA;";
|
||||||
|
then
|
||||||
|
echo 'VITO_SSH_ERROR' && exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
|||||||
if ! sudo mysql -e "GRANT ALL PRIVILEGES ON {{ $database }}.* TO '{{ $username }}'@'{{ $host }}'"; then
|
if ! sudo mariadb -e "GRANT ALL PRIVILEGES ON {{ $database }}.* TO '{{ $username }}'@'{{ $host }}'"; then
|
||||||
echo 'VITO_SSH_ERROR' && exit 1
|
echo 'VITO_SSH_ERROR' && exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! sudo mysql -e "FLUSH PRIVILEGES"; then
|
if ! sudo mariadb -e "FLUSH PRIVILEGES"; then
|
||||||
echo 'VITO_SSH_ERROR' && exit 1
|
echo 'VITO_SSH_ERROR' && exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
echo 'VITO_SSH_ERROR' && exit 1
|
echo 'VITO_SSH_ERROR' && exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! sudo DEBIAN_FRONTEND=noninteractive mysql -u root {{ $database }} < {{ $file }}.sql; then
|
if ! sudo DEBIAN_FRONTEND=noninteractive mariadb -u root {{ $database }} < {{ $file }}.sql; then
|
||||||
echo 'VITO_SSH_ERROR' && exit 1
|
echo 'VITO_SSH_ERROR' && exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
sudo service mysql stop
|
sudo service mariadb stop
|
||||||
|
|
||||||
sudo DEBIAN_FRONTEND=noninteractive apt-get remove mariadb-server mariadb-backup -y
|
sudo DEBIAN_FRONTEND=noninteractive apt-get remove mariadb-server mariadb-backup -y
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
if ! sudo mysql -e "REVOKE ALL PRIVILEGES, GRANT OPTION FROM '{{ $username }}'@'{{ $host }}'"; then
|
if ! sudo mariadb -e "REVOKE ALL PRIVILEGES, GRANT OPTION FROM '{{ $username }}'@'{{ $host }}'"; then
|
||||||
echo 'VITO_SSH_ERROR' && exit 1
|
echo 'VITO_SSH_ERROR' && exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
if ! sudo mysql -e "CREATE DATABASE IF NOT EXISTS {{ $name }} CHARACTER SET utf8 COLLATE utf8_general_ci"; then
|
if ! sudo mysql -e "CREATE DATABASE IF NOT EXISTS {{ $name }} CHARACTER SET '{{ $charset }}' COLLATE '{{ $collation }}'"; then
|
||||||
echo 'VITO_SSH_ERROR' && exit 1
|
echo 'VITO_SSH_ERROR' && exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
if ! sudo mysql -e "SHOW COLLATION;"; then
|
||||||
|
echo 'VITO_SSH_ERROR' && exit 1
|
||||||
|
fi
|
@ -0,0 +1,8 @@
|
|||||||
|
if ! sudo mysql -e "SELECT
|
||||||
|
SCHEMA_NAME AS database_name,
|
||||||
|
DEFAULT_CHARACTER_SET_NAME AS charset,
|
||||||
|
DEFAULT_COLLATION_NAME AS collation
|
||||||
|
FROM information_schema.SCHEMATA;";
|
||||||
|
then
|
||||||
|
echo 'VITO_SSH_ERROR' && exit 1
|
||||||
|
fi
|
@ -1,4 +1,4 @@
|
|||||||
if ! sudo -u postgres psql -c "CREATE DATABASE \"{{ $name }}\""; then
|
if ! sudo -u postgres psql -c "CREATE DATABASE \"{{ $name }}\" WITH ENCODING '{{ $charset }}'"; then
|
||||||
echo 'VITO_SSH_ERROR' && exit 1
|
echo 'VITO_SSH_ERROR' && exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
if ! sudo -u postgres psql -c "SELECT collname as collation,
|
||||||
|
pg_encoding_to_char(collencoding) as charset,
|
||||||
|
'' as id,
|
||||||
|
'' as \"default\",
|
||||||
|
'Yes' as compiled,
|
||||||
|
'' as sortlen,
|
||||||
|
'' as pad_attribute
|
||||||
|
FROM pg_collation
|
||||||
|
WHERE not pg_encoding_to_char(collencoding) = ''
|
||||||
|
ORDER BY charset;";
|
||||||
|
then
|
||||||
|
echo 'VITO_SSH_ERROR' && exit 1
|
||||||
|
fi
|
@ -0,0 +1,8 @@
|
|||||||
|
if ! sudo -u postgres psql -c "SELECT
|
||||||
|
datname AS database_name,
|
||||||
|
pg_encoding_to_char(encoding) AS charset,
|
||||||
|
datcollate AS collation
|
||||||
|
FROM pg_database;";
|
||||||
|
then
|
||||||
|
echo 'VITO_SSH_ERROR' && exit 1
|
||||||
|
fi
|
@ -24,6 +24,8 @@ public function test_create_database(): void
|
|||||||
'server' => $this->server,
|
'server' => $this->server,
|
||||||
]), [
|
]), [
|
||||||
'name' => 'database',
|
'name' => 'database',
|
||||||
|
'charset' => 'utf8mb4',
|
||||||
|
'collation' => 'utf8mb4_unicode_ci',
|
||||||
])
|
])
|
||||||
->assertSuccessful()
|
->assertSuccessful()
|
||||||
->assertJsonFragment([
|
->assertJsonFragment([
|
||||||
|
@ -27,6 +27,8 @@ public function test_create_database(): void
|
|||||||
])
|
])
|
||||||
->callAction('create', [
|
->callAction('create', [
|
||||||
'name' => 'database',
|
'name' => 'database',
|
||||||
|
'charset' => 'utf8mb4',
|
||||||
|
'collation' => 'utf8mb4_unicode_ci',
|
||||||
])
|
])
|
||||||
->assertSuccessful();
|
->assertSuccessful();
|
||||||
|
|
||||||
@ -47,6 +49,8 @@ public function test_create_database_with_user(): void
|
|||||||
])
|
])
|
||||||
->callAction('create', [
|
->callAction('create', [
|
||||||
'name' => 'database',
|
'name' => 'database',
|
||||||
|
'charset' => 'utf8mb4',
|
||||||
|
'collation' => 'utf8mb4_unicode_ci',
|
||||||
'user' => true,
|
'user' => true,
|
||||||
'username' => 'user',
|
'username' => 'user',
|
||||||
'password' => 'password',
|
'password' => 'password',
|
||||||
|
@ -361,6 +361,8 @@ public static function create_data(): array
|
|||||||
'email' => 'email@example.com',
|
'email' => 'email@example.com',
|
||||||
'password' => 'password',
|
'password' => 'password',
|
||||||
'database' => 'example',
|
'database' => 'example',
|
||||||
|
'charset' => 'utf8mb4',
|
||||||
|
'collation' => 'utf8mb4_unicode_ci',
|
||||||
'database_user' => 'example',
|
'database_user' => 'example',
|
||||||
'database_password' => 'password',
|
'database_password' => 'password',
|
||||||
],
|
],
|
||||||
@ -376,6 +378,8 @@ public static function create_data(): array
|
|||||||
'email' => 'email@example.com',
|
'email' => 'email@example.com',
|
||||||
'password' => 'password',
|
'password' => 'password',
|
||||||
'database' => 'example',
|
'database' => 'example',
|
||||||
|
'charset' => 'utf8mb4',
|
||||||
|
'collation' => 'utf8mb4_unicode_ci',
|
||||||
'database_user' => 'example',
|
'database_user' => 'example',
|
||||||
'database_password' => 'password',
|
'database_password' => 'password',
|
||||||
'user' => 'example',
|
'user' => 'example',
|
||||||
|
@ -87,6 +87,11 @@ private function setupServer(): void
|
|||||||
$this->server->services()->update([
|
$this->server->services()->update([
|
||||||
'status' => ServiceStatus::READY,
|
'status' => ServiceStatus::READY,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$this->server->database()?->update(['type_data' => [
|
||||||
|
'charsets' => ['utf8mb3' => ['default' => 'utf8mb3_general_ci', 'list' => ['utf8mb3_general_ci']]],
|
||||||
|
'defaultCharset' => 'utf8mb3',
|
||||||
|
]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function setupSite(): void
|
private function setupSite(): void
|
||||||
|
80
tests/Unit/SSH/Services/Database/SyncDatabasesTest.php
Normal file
80
tests/Unit/SSH/Services/Database/SyncDatabasesTest.php
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Unit\SSH\Services\Database;
|
||||||
|
|
||||||
|
use App\Facades\SSH;
|
||||||
|
use App\SSH\Services\Database\Database;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class SyncDatabasesTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @dataProvider data
|
||||||
|
*/
|
||||||
|
public function test_sync_databases(string $name, string $version, string $output): void
|
||||||
|
{
|
||||||
|
$database = $this->server->database();
|
||||||
|
$database->name = $name;
|
||||||
|
$database->version = $version;
|
||||||
|
$database->save();
|
||||||
|
|
||||||
|
SSH::fake($output);
|
||||||
|
|
||||||
|
/** @var Database $databaseHandler */
|
||||||
|
$databaseHandler = $database->handler();
|
||||||
|
$databaseHandler->syncDatabases();
|
||||||
|
|
||||||
|
$this->assertDatabaseHas('databases', [
|
||||||
|
'server_id' => $this->server->id,
|
||||||
|
'name' => 'vito',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @TODO Add more test cases
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
*/
|
||||||
|
public static function data(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'mysql',
|
||||||
|
'8.0',
|
||||||
|
<<<'EOD'
|
||||||
|
database_name charset collation
|
||||||
|
mysql utf8mb4 utf8mb4_0900_ai_ci
|
||||||
|
information_schema utf8mb3 utf8mb3_general_ci
|
||||||
|
performance_schema utf8mb4 utf8mb4_0900_ai_ci
|
||||||
|
sys utf8mb4 utf8mb4_0900_ai_ci
|
||||||
|
vito utf8mb3 utf8mb3_general_ci
|
||||||
|
EOD
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'mysql',
|
||||||
|
'5.7',
|
||||||
|
<<<'EOD'
|
||||||
|
database_name charset collation
|
||||||
|
mysql utf8mb4 utf8mb4_0900_ai_ci
|
||||||
|
information_schema utf8mb3 utf8mb3_general_ci
|
||||||
|
performance_schema utf8mb4 utf8mb4_0900_ai_ci
|
||||||
|
sys utf8mb4 utf8mb4_0900_ai_ci
|
||||||
|
vito utf8mb3 utf8mb3_general_ci
|
||||||
|
EOD
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'postgresql',
|
||||||
|
'16',
|
||||||
|
<<<'EOD'
|
||||||
|
database_name | charset | collation
|
||||||
|
---------------+---------+-------------
|
||||||
|
postgres | UTF8 | en_US.UTF-8
|
||||||
|
template1 | UTF8 | en_US.UTF-8
|
||||||
|
template0 | UTF8 | en_US.UTF-8
|
||||||
|
vito | UTF8 | en_US.UTF-8
|
||||||
|
(3 rows)
|
||||||
|
EOD
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
131
tests/Unit/SSH/Services/Database/UpdateCharsetsTest.php
Normal file
131
tests/Unit/SSH/Services/Database/UpdateCharsetsTest.php
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Unit\SSH\Services\Database;
|
||||||
|
|
||||||
|
use App\Facades\SSH;
|
||||||
|
use App\SSH\Services\Database\Database;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class UpdateCharsetsTest extends TestCase
|
||||||
|
{
|
||||||
|
protected static array $mysqlCharsets = [
|
||||||
|
'armscii8' => [
|
||||||
|
'default' => 'armscii8_general_ci',
|
||||||
|
'list' => [
|
||||||
|
'armscii8_bin',
|
||||||
|
'armscii8_general_ci',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'ascii' => [
|
||||||
|
'default' => 'ascii_general_ci',
|
||||||
|
'list' => [
|
||||||
|
'ascii_bin',
|
||||||
|
'ascii_general_ci',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'big5' => [
|
||||||
|
'default' => 'big5_chinese_ci',
|
||||||
|
'list' => [
|
||||||
|
'big5_bin',
|
||||||
|
'big5_chinese_ci',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider data
|
||||||
|
*/
|
||||||
|
public function test_update_charsets(string $name, string $version, string $output, array $expected): void
|
||||||
|
{
|
||||||
|
$database = $this->server->database();
|
||||||
|
$database->name = $name;
|
||||||
|
$database->version = $version;
|
||||||
|
$database->save();
|
||||||
|
|
||||||
|
SSH::fake($output);
|
||||||
|
|
||||||
|
/** @var Database $databaseHandler */
|
||||||
|
$databaseHandler = $database->handler();
|
||||||
|
$databaseHandler->updateCharsets();
|
||||||
|
|
||||||
|
$database->refresh();
|
||||||
|
$this->assertEquals($expected, $database->type_data['charsets']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @TODO Add more test cases
|
||||||
|
*
|
||||||
|
* @return array[]
|
||||||
|
*/
|
||||||
|
public static function data(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'mysql',
|
||||||
|
'8.0',
|
||||||
|
<<<'EOD'
|
||||||
|
Collation Charset Id Default Compiled Sortlen Pad_attribute
|
||||||
|
armscii8_bin armscii8 64 Yes 1 PAD SPACE
|
||||||
|
armscii8_general_ci armscii8 32 Yes Yes 1 PAD SPACE
|
||||||
|
ascii_bin ascii 65 Yes 1 PAD SPACE
|
||||||
|
ascii_general_ci ascii 11 Yes Yes 1 PAD SPACE
|
||||||
|
big5_bin big5 84 Yes 1 PAD SPACE
|
||||||
|
big5_chinese_ci big5 1 Yes Yes 1 PAD SPACE
|
||||||
|
EOD,
|
||||||
|
static::$mysqlCharsets,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'mysql',
|
||||||
|
'5.7',
|
||||||
|
<<<'EOD'
|
||||||
|
Collation Charset Id Default Compiled Sortlen Pad_attribute
|
||||||
|
armscii8_bin armscii8 64 Yes 1 PAD SPACE
|
||||||
|
armscii8_general_ci armscii8 32 Yes Yes 1 PAD SPACE
|
||||||
|
ascii_bin ascii 65 Yes 1 PAD SPACE
|
||||||
|
ascii_general_ci ascii 11 Yes Yes 1 PAD SPACE
|
||||||
|
big5_bin big5 84 Yes 1 PAD SPACE
|
||||||
|
big5_chinese_ci big5 1 Yes Yes 1 PAD SPACE
|
||||||
|
EOD,
|
||||||
|
static::$mysqlCharsets,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'mariadb',
|
||||||
|
'10.5',
|
||||||
|
<<<'EOD'
|
||||||
|
Collation Charset Id Default Compiled Sortlen Pad_attribute
|
||||||
|
armscii8_bin armscii8 64 Yes 1 PAD SPACE
|
||||||
|
armscii8_general_ci armscii8 32 Yes Yes 1 PAD SPACE
|
||||||
|
ascii_bin ascii 65 Yes 1 PAD SPACE
|
||||||
|
ascii_general_ci ascii 11 Yes Yes 1 PAD SPACE
|
||||||
|
big5_bin big5 84 Yes 1 PAD SPACE
|
||||||
|
big5_chinese_ci big5 1 Yes Yes 1 PAD SPACE
|
||||||
|
EOD,
|
||||||
|
static::$mysqlCharsets,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'postgresql',
|
||||||
|
'16',
|
||||||
|
<<<'EOD'
|
||||||
|
collation | charset | id | default | compiled | sortlen | pad_attribute
|
||||||
|
------------+---------+----+---------+----------+---------+---------------
|
||||||
|
ucs_basic | UTF8 | | | Yes | |
|
||||||
|
C.utf8 | UTF8 | | | Yes | |
|
||||||
|
en_US.utf8 | UTF8 | | | Yes | |
|
||||||
|
en_US | UTF8 | | | Yes | |
|
||||||
|
(4 rows)
|
||||||
|
EOD,
|
||||||
|
[
|
||||||
|
'UTF8' => [
|
||||||
|
'default' => null,
|
||||||
|
'list' => [
|
||||||
|
'ucs_basic',
|
||||||
|
'C.utf8',
|
||||||
|
'en_US.utf8',
|
||||||
|
'en_US',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user