mirror of
https://github.com/vitodeploy/vito.git
synced 2025-04-19 09:51: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([
|
||||
'server_id' => $server->id,
|
||||
'charset' => $input['charset'],
|
||||
'collation' => $input['collation'],
|
||||
'name' => $input['name'],
|
||||
]);
|
||||
/** @var \App\SSH\Services\Database\Database $databaseHandler */
|
||||
$databaseHandler = $server->database()->handler();
|
||||
$databaseHandler->create($database->name);
|
||||
$databaseHandler->create($database->name, $database->charset, $database->collation);
|
||||
$database->status = DatabaseStatus::READY;
|
||||
$database->save();
|
||||
|
||||
@ -42,6 +44,14 @@ public static function rules(Server $server, array $input): array
|
||||
'alpha_dash',
|
||||
Rule::unique('databases', 'name')->where('server_id', $server->id),
|
||||
],
|
||||
'charset' => [
|
||||
'required',
|
||||
'string',
|
||||
],
|
||||
'collation' => [
|
||||
'required',
|
||||
'string',
|
||||
],
|
||||
];
|
||||
if (isset($input['user']) && $input['user']) {
|
||||
$rules['username'] = [
|
||||
|
@ -41,6 +41,8 @@ public function index(Project $project, Server $server): ResourceCollection
|
||||
#[Post('/', name: 'api.projects.servers.databases.create', middleware: 'ability:write')]
|
||||
#[Endpoint(title: 'create', description: 'Create a new database.')]
|
||||
#[BodyParam(name: 'name', required: true)]
|
||||
#[BodyParam(name: 'charset', required: true)]
|
||||
#[BodyParam(name: 'collation', required: true)]
|
||||
#[ResponseFromApiResource(DatabaseResource::class, Database::class)]
|
||||
public function create(Request $request, Project $project, Server $server): DatabaseResource
|
||||
{
|
||||
|
@ -12,6 +12,8 @@
|
||||
/**
|
||||
* @property int $server_id
|
||||
* @property string $name
|
||||
* @property string $collation
|
||||
* @property string $charset
|
||||
* @property string $status
|
||||
* @property Server $server
|
||||
* @property Backup[] $backups
|
||||
@ -25,6 +27,8 @@ class Database extends AbstractModel
|
||||
protected $fillable = [
|
||||
'server_id',
|
||||
'name',
|
||||
'collation',
|
||||
'charset',
|
||||
'status',
|
||||
];
|
||||
|
||||
|
@ -11,6 +11,16 @@
|
||||
|
||||
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
|
||||
{
|
||||
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);
|
||||
$this->service->validateInstall($status);
|
||||
$this->service->server->os()->cleanup();
|
||||
|
||||
$this->updateCharsets();
|
||||
$this->syncDatabases();
|
||||
}
|
||||
|
||||
public function deletionRules(): array
|
||||
@ -83,11 +96,13 @@ public function uninstall(): void
|
||||
/**
|
||||
* @throws SSHError
|
||||
*/
|
||||
public function create(string $name): void
|
||||
public function create(string $name, string $charset, string $collation): void
|
||||
{
|
||||
$this->service->server->ssh()->exec(
|
||||
view($this->getScriptView('create'), [
|
||||
'name' => $name,
|
||||
'charset' => $charset,
|
||||
'collation' => $collation,
|
||||
]),
|
||||
'create-database'
|
||||
);
|
||||
@ -219,4 +234,124 @@ public function restoreBackup(BackupFile $backupFile, string $database): void
|
||||
'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
|
||||
{
|
||||
public function create(string $name): void;
|
||||
public function create(string $name, string $charset, string $collation): 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 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
|
||||
{
|
||||
//
|
||||
protected array $systemDbs = ['information_schema', 'performance_schema', 'mysql', 'sys'];
|
||||
|
||||
protected string $defaultCharset = 'utf8mb3';
|
||||
}
|
||||
|
@ -4,5 +4,7 @@
|
||||
|
||||
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
|
||||
{
|
||||
//
|
||||
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'],
|
||||
'password' => $input['password'],
|
||||
'database' => $input['database'],
|
||||
'database_charset' => $input['charset'],
|
||||
'database_collation' => $input['collation'],
|
||||
'database_user' => $input['database_user'],
|
||||
'database_password' => $input['database_password'],
|
||||
];
|
||||
@ -96,20 +98,28 @@ public function install(): void
|
||||
$webserver = $this->site->server->webserver()->handler();
|
||||
$webserver->createVHost($this->site);
|
||||
$this->progress(30);
|
||||
|
||||
/** @var Database $database */
|
||||
$database = app(CreateDatabase::class)->create($this->site->server, [
|
||||
'name' => $this->site->type_data['database'],
|
||||
'charset' => $this->site->type_data['database_charset'],
|
||||
'collation' => $this->site->type_data['database_collation'],
|
||||
]);
|
||||
|
||||
/** @var DatabaseUser $databaseUser */
|
||||
$databaseUser = app(CreateDatabaseUser::class)->create($this->site->server, [
|
||||
'username' => $this->site->type_data['database_user'],
|
||||
'password' => $this->site->type_data['database_password'],
|
||||
'collation' => $this->site->type_data['database_collation'],
|
||||
'charset' => $this->site->type_data['database_charset'],
|
||||
'remote' => false,
|
||||
'host' => 'localhost',
|
||||
], [$database->name]);
|
||||
|
||||
app(LinkUser::class)->link($databaseUser, [
|
||||
'databases' => [$database->name],
|
||||
]);
|
||||
|
||||
$this->site->php()?->restart();
|
||||
$this->progress(60);
|
||||
app(\App\SSH\Wordpress\Wordpress::class)->install($this->site);
|
||||
|
@ -4,11 +4,15 @@
|
||||
|
||||
use App\Actions\Database\CreateDatabase;
|
||||
use App\Models\Database;
|
||||
use App\Models\Server;
|
||||
use App\Web\Contracts\HasSecondSubNav;
|
||||
use App\Web\Pages\Servers\Page;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Forms\Components\Checkbox;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Get;
|
||||
use Filament\Forms\Set;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Support\Enums\MaxWidth;
|
||||
|
||||
@ -25,6 +29,59 @@ public function mount(): void
|
||||
$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
|
||||
{
|
||||
return [
|
||||
@ -36,6 +93,8 @@ protected function getHeaderActions(): array
|
||||
->form([
|
||||
TextInput::make('name')
|
||||
->rules(fn (callable $get) => CreateDatabase::rules($this->server, $get())['name']),
|
||||
self::getCharsetInput($this->server),
|
||||
self::getCollationInput($this->server),
|
||||
Checkbox::make('user')
|
||||
->label('Create User')
|
||||
->default(false)
|
||||
|
@ -27,6 +27,12 @@ protected function getTableColumns(): array
|
||||
return [
|
||||
TextColumn::make('name')
|
||||
->searchable(),
|
||||
TextColumn::make('charset')
|
||||
->label('Charset / Encoding')
|
||||
->sortable(),
|
||||
TextColumn::make('collation')
|
||||
->label('Collation')
|
||||
->sortable(),
|
||||
TextColumn::make('status')
|
||||
->label('Status')
|
||||
->badge()
|
||||
|
@ -199,6 +199,8 @@ private function wordpressFields(): Component
|
||||
TextInput::make('database')
|
||||
->helperText('It will create a database with this name')
|
||||
->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')
|
||||
->helperText('It will create a db user with this username')
|
||||
->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
|
||||
fi
|
||||
|
||||
if ! sudo mysql -e "FLUSH PRIVILEGES"; then
|
||||
if ! sudo mariadb -e "FLUSH PRIVILEGES"; then
|
||||
echo 'VITO_SSH_ERROR' && exit 1
|
||||
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
|
||||
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
|
||||
fi
|
||||
|
||||
if ! sudo mysql -e "FLUSH PRIVILEGES"; then
|
||||
if ! sudo mariadb -e "FLUSH PRIVILEGES"; then
|
||||
echo 'VITO_SSH_ERROR' && exit 1
|
||||
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
|
||||
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
|
||||
fi
|
||||
|
||||
if ! sudo mysql -e "FLUSH PRIVILEGES"; then
|
||||
if ! sudo mariadb -e "FLUSH PRIVILEGES"; then
|
||||
echo 'VITO_SSH_ERROR' && exit 1
|
||||
fi
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
echo 'VITO_SSH_ERROR' && exit 1
|
||||
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
|
||||
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
|
||||
|
||||
|
@ -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
|
||||
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
|
||||
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
|
||||
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,
|
||||
]), [
|
||||
'name' => 'database',
|
||||
'charset' => 'utf8mb4',
|
||||
'collation' => 'utf8mb4_unicode_ci',
|
||||
])
|
||||
->assertSuccessful()
|
||||
->assertJsonFragment([
|
||||
|
@ -27,6 +27,8 @@ public function test_create_database(): void
|
||||
])
|
||||
->callAction('create', [
|
||||
'name' => 'database',
|
||||
'charset' => 'utf8mb4',
|
||||
'collation' => 'utf8mb4_unicode_ci',
|
||||
])
|
||||
->assertSuccessful();
|
||||
|
||||
@ -47,6 +49,8 @@ public function test_create_database_with_user(): void
|
||||
])
|
||||
->callAction('create', [
|
||||
'name' => 'database',
|
||||
'charset' => 'utf8mb4',
|
||||
'collation' => 'utf8mb4_unicode_ci',
|
||||
'user' => true,
|
||||
'username' => 'user',
|
||||
'password' => 'password',
|
||||
|
@ -361,6 +361,8 @@ public static function create_data(): array
|
||||
'email' => 'email@example.com',
|
||||
'password' => 'password',
|
||||
'database' => 'example',
|
||||
'charset' => 'utf8mb4',
|
||||
'collation' => 'utf8mb4_unicode_ci',
|
||||
'database_user' => 'example',
|
||||
'database_password' => 'password',
|
||||
],
|
||||
@ -376,6 +378,8 @@ public static function create_data(): array
|
||||
'email' => 'email@example.com',
|
||||
'password' => 'password',
|
||||
'database' => 'example',
|
||||
'charset' => 'utf8mb4',
|
||||
'collation' => 'utf8mb4_unicode_ci',
|
||||
'database_user' => 'example',
|
||||
'database_password' => 'password',
|
||||
'user' => 'example',
|
||||
|
@ -87,6 +87,11 @@ private function setupServer(): void
|
||||
$this->server->services()->update([
|
||||
'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
|
||||
|
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