Skip to content

Commit

Permalink
Adjust setup to support SQLite and PostgreSQL (#831)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kovah committed Aug 14, 2024
1 parent edb71c8 commit fb73148
Show file tree
Hide file tree
Showing 14 changed files with 146 additions and 35 deletions.
1 change: 0 additions & 1 deletion app/Helper/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ function setupCompleted(): ?bool
try {
return systemsettings('setup_completed');
} catch (PDOException $e) {
Log::error($e->getMessage());
return false;
}
}
Expand Down
40 changes: 24 additions & 16 deletions app/Http/Controllers/Setup/DatabaseController.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

class DatabaseController extends Controller
{
protected string $connection;
protected array $dbConfig;

public function index(): View
Expand Down Expand Up @@ -45,17 +46,22 @@ public function configure(SetupDatabaseRequest $request): RedirectResponse
return redirect()->route('setup.account');
}

protected function createTempDatabaseConnection(array $credentials): void
protected function createTempDatabaseConnection(array $configuration): void
{
$this->dbConfig = config('database.connections.mysql');

$this->dbConfig['host'] = $credentials['db_host'];
$this->dbConfig['port'] = $credentials['db_port'];
$this->dbConfig['database'] = $credentials['db_name'];
$this->dbConfig['username'] = $credentials['db_user'];
$this->dbConfig['password'] = $credentials['db_password'];
$this->connection = $configuration['connection'];
$this->dbConfig = config('database.connections.' . $this->connection);

if ($this->connection === 'sqlite') {
$this->dbConfig['database'] = $configuration['db_path'];
} else {
$this->dbConfig['host'] = $configuration['db_host'];
$this->dbConfig['port'] = $configuration['db_port'];
$this->dbConfig['database'] = $configuration['db_name'];
$this->dbConfig['username'] = $configuration['db_user'];
$this->dbConfig['password'] = $configuration['db_password'];
}

Config::set('database.connections.mysql', $this->dbConfig);
Config::set('database.connections.' . $this->connection, $this->dbConfig);
}

/**
Expand All @@ -70,7 +76,7 @@ protected function migrateDatabase(): bool
{
try {
Artisan::call('migrate:fresh', [
'--database' => 'mysql', // Specify the correct connection
'--database' => $this->connection, // Specify the correct connection
'--force' => true, // Needed for production
'--no-interaction' => true,
]);
Expand All @@ -93,17 +99,19 @@ protected function storeConfigurationInEnv(): void
$envContent = File::get(base_path('.env'));

$envContent = preg_replace([
'/DB_CONNECTION=(.*)\S/',
'/DB_HOST=(.*)\S/',
'/DB_PORT=(.*)\S/',
'/DB_DATABASE=(.*)\S/',
'/DB_USERNAME=(.*)\S/',
'/DB_PASSWORD=(.*)\S/',
], [
'DB_HOST=' . $this->dbConfig['host'],
'DB_PORT=' . $this->dbConfig['port'],
'DB_DATABASE=' . $this->dbConfig['database'],
'DB_USERNAME=' . $this->dbConfig['username'],
'DB_PASSWORD=' . $this->dbConfig['password'],
'DB_CONNECTION=' . $this->connection,
'DB_HOST=' . ($this->dbConfig['host'] ?? ''),
'DB_PORT=' . ($this->dbConfig['port'] ?? ''),
'DB_DATABASE=' . ($this->dbConfig['database'] ?? ''),
'DB_USERNAME=' . ($this->dbConfig['username'] ?? ''),
'DB_PASSWORD=' . ($this->dbConfig['password'] ?? ''),
], $envContent);

if ($envContent !== null) {
Expand All @@ -121,7 +129,7 @@ protected function storeConfigurationInEnv(): void
protected function databaseHasData(): bool
{
try {
$presentTables = DB::connection('mysql')
$presentTables = DB::connection($this->connection)
->getDoctrineSchemaManager()
->listTableNames();
} catch (PDOException|\Doctrine\DBAL\Exception $e) {
Expand Down
21 changes: 18 additions & 3 deletions app/Http/Controllers/Setup/RequirementsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,33 @@ protected function checkRequirements(): array
'php_version' => PHP_VERSION_ID >= 80110,
'extension_bcmath' => extension_loaded('bcmath'),
'extension_ctype' => extension_loaded('ctype'),
'extension_curl' => extension_loaded('curl'),
'extension_dom' => extension_loaded('dom'),
'extension_fileinfo' => extension_loaded('fileinfo'),
'extension_filter' => extension_loaded('filter'),
'extension_hash' => extension_loaded('hash'),
'extension_json' => extension_loaded('json'),
'extension_mbstring' => extension_loaded('mbstring'),
'extension_openssl' => extension_loaded('openssl'),
'extension_pdo_mysql' => extension_loaded('pdo_mysql'),
'extension_pcre' => extension_loaded('pcre'),
'extension_session' => extension_loaded('session'),
'extension_tokenizer' => extension_loaded('tokenizer'),
'extension_xml' => extension_loaded('xml'),
];

$dbResults = [
'extension_pdo_mysql' => extension_loaded('pdo_mysql'),
'extension_pdo_pgsql' => extension_loaded('pdo_pgsql'),
'extension_pdo_sqlite' => extension_loaded('pdo_sqlite'),
];

$additionalResults = [
'env_writable' => File::isWritable(base_path('.env')),
'storage_writable' => File::isWritable(storage_path()) && File::isWritable(storage_path('logs')),
];

$success = !in_array(false, $results, true);
$success = !in_array(false, $results, true) && !in_array(false, $additionalResults, true);

return [$success, $results];
return [$success, array_merge($results, $dbResults, $additionalResults)];
}
}
15 changes: 11 additions & 4 deletions app/Http/Requests/SetupDatabaseRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,25 @@ class SetupDatabaseRequest extends FormRequest
public function rules(): array
{
return [
'db_host' => [
'connection' => [
'required',
'in:sqlite,mysql,pgsql',
],
'db_path' => [
'required_if:connection,sqlite',
],
'db_host' => [
'required_unless:connection,sqlite',
],
'db_port' => [
'required',
'required_unless:connection,sqlite',
'numeric',
],
'db_name' => [
'required',
'required_unless:connection,sqlite',
],
'db_user' => [
'required',
'required_unless:connection,sqlite',
],
'db_password' => [
'nullable',
Expand Down
2 changes: 1 addition & 1 deletion config/database.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'engine' => env('DB_ENGINE', 'InnoDB'),
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
Expand Down
Binary file modified database/schema/pgsql-schema.dump
Binary file not shown.
11 changes: 11 additions & 0 deletions lang/en_US/setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,19 @@
'requirements.php_version' => 'PHP version >= 7.4.0',
'requirements.extension_bcmath' => 'PHP Extension: BCMath',
'requirements.extension_ctype' => 'PHP Extension: Ctype',
'requirements.extension_curl' => 'PHP Extension: Curl',
'requirements.extension_dom' => 'PHP Extension: DOM',
'requirements.extension_fileinfo' => 'PHP Extension: Fileinfo',
'requirements.extension_filter' => 'PHP Extension: Filter',
'requirements.extension_hash' => 'PHP Extension: Hash',
'requirements.extension_json' => 'PHP Extension: JSON',
'requirements.extension_mbstring' => 'PHP Extension: Mbstring',
'requirements.extension_openssl' => 'PHP Extension: OpenSSL',
'requirements.extension_pcre' => 'PHP Extension: PCRE',
'requirements.extension_pdo_mysql' => 'PHP Extension: PDO MySQL',
'requirements.extension_pdo_pgsql' => 'PHP Extension: PDO PostgreSQL',
'requirements.extension_pdo_sqlite' => 'PHP Extension: PDO SQLite',
'requirements.extension_session' => 'PHP Extension: Session',
'requirements.extension_tokenizer' => 'PHP Extension: Tokenizer',
'requirements.extension_xml' => 'PHP Extension: XML',
'requirements.env_writable' => '.env file is present and writable',
Expand All @@ -29,6 +38,8 @@
'database_configure' => 'Configure Database',
'database.intro' => 'If you already filled the database details in your .env file the input fields should be pre-filled. Otherwise, fill the fields with the corresponding information for your database.',
'database.config_error' => 'Database could not be configured. Please check your connection details. Details:',
'database.connection' => 'Database Type',
'database.db_path' => 'Database File Path',
'database.db_host' => 'Database Host',
'database.db_port' => 'Database Port',
'database.db_name' => 'Database Name',
Expand Down
4 changes: 3 additions & 1 deletion resources/assets/js/app.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { register } from './lib/views';

import Base from './components/Base';

import BookmarkTimer from './components/BookmarkTimer';
import BulkEdit from './components/BulkEdit';
import DatabaseSetup from './components/Setup';
import GenerateCronToken from './components/GenerateCronToken';
import Import from './components/Import';
import LoadingButton from './components/LoadingButton';
Expand All @@ -17,6 +18,7 @@ function registerViews () {
register('#app', Base);
register('.bm-timer', BookmarkTimer);
register('.bulk-edit', BulkEdit);
register('.database-setup', DatabaseSetup);
register('.cron-token', GenerateCronToken);
register('.import-form', Import);
register('.share-toggle', ShareToggleAll);
Expand Down
6 changes: 4 additions & 2 deletions resources/assets/js/components/Base.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ export default class Base {

initAppData () {
// Load data passed by the backend to the JS
let data = document.querySelector('meta[property="la-app-data"]').getAttribute('content');
window.appData = JSON.parse(data);
let data = document.querySelector('meta[property="la-app-data"]')?.getAttribute('content');
if (data) {
window.appData = JSON.parse(data);
}
}

initBootstrapTooltips () {
Expand Down
30 changes: 30 additions & 0 deletions resources/assets/js/components/Setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export default class DatabaseSetup {

constructor ($el) {
this.$connection = $el.querySelector('#connection');
this.$dbPath = $el.querySelector('.db-path');
this.$dbHost = $el.querySelector('.db-host');
this.$dbPort = $el.querySelector('.db-port');
this.$dbName = $el.querySelector('.db-name');
this.$dbUser = $el.querySelector('.db-user');
this.$dbPass = $el.querySelector('.db-password');
this.$submit = $el.querySelector('.db-submit');

this.$connection.addEventListener('change', this.handleConnectionChange.bind(this));
this.handleConnectionChange();

$el.addEventListener('submit', () => {
this.$submit.toggleAttribute('disabled');
});
}

handleConnectionChange () {
const connection = this.$connection.options[this.$connection.selectedIndex].value;
this.$dbPath.classList.toggle('d-none', connection !== 'sqlite');
this.$dbHost.classList.toggle('d-none', connection === 'sqlite');
this.$dbPort.classList.toggle('d-none', connection === 'sqlite');
this.$dbName.classList.toggle('d-none', connection === 'sqlite');
this.$dbUser.classList.toggle('d-none', connection === 'sqlite');
this.$dbPass.classList.toggle('d-none', connection === 'sqlite');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,10 @@ COPY --from=npm_builder --chown=www-data:www-data /srv/public/assets/dist/css /a
COPY --from=npm_builder --chown=www-data:www-data /srv/public/mix-manifest.json /app/public/mix-manifest.json

# Create a SQLite database file ready to be used
RUN touch ./database/database.sqlite
RUN touch ./database/database.sqlite \
&& chown www-data:www-data ./database/database.sqlite \
&& chmod +w ./database/database.sqlite \
&& chmod +r ./database

# Configure Supervisor for PHP + Caddy
ENV PORT=80
Expand Down
1 change: 1 addition & 0 deletions resources/views/components/icon/warning.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<svg {{ $attributes->merge(['class' => 'icon', 'viewBox' =>'0 0 512 512', 'xmlns' => 'http://www.w3.org/2000/svg', 'aria-hidden' => 'true', 'focusable' => 'false']) }}><path d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480H40c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24V296c0 13.3 10.7 24 24 24s24-10.7 24-24V184c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/></svg>
43 changes: 37 additions & 6 deletions resources/views/setup/database.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,41 @@

@include('partials.alerts')

<form action="{{ route('setup.save-database') }}" method="POST">
<form action="{{ route('setup.save-database') }}" method="POST" class="database-setup">
@csrf

<div class="mb-3">
<label for="connection">
@lang('setup.database.connection')
</label>
<select name="connection" id="connection"
class="form-select{{ $errors->has('connection') ? ' is-invalid' : '' }}">
<option value="mysql" @selected(old('connection') === 'mysql')>MySQL / MariaDB</option>
<option value="sqlite" @selected(old('connection') === 'sqlite')>SQLite</option>
<option value="pgsql" @selected(old('connection') === 'pgsql')>PostgreSQL</option>
</select>
@if ($errors->has('connection'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('connection') }}
</p>
@endif
</div>

<div class="mb-3 db-path">
<label for="db_path">
@lang('setup.database.db_path')
</label>
<input type="text" name="db_path" id="db_path" required
class="form-control{{ $errors->has('db_path') ? ' is-invalid' : '' }}"
placeholder="localhost" value="{{ old('db_path') ?: database_path('database.sqlite') }}">
@if ($errors->has('db_path'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('db_path') }}
</p>
@endif
</div>

<div class="mb-3 db-host">
<label for="db_host">
@lang('setup.database.db_host')
</label>
Expand All @@ -32,7 +63,7 @@ class="form-control{{ $errors->has('db_host') ? ' is-invalid' : '' }}"
@endif
</div>

<div class="mb-3">
<div class="mb-3 db-port">
<label for="db_port">
@lang('setup.database.db_port')
</label>
Expand All @@ -46,7 +77,7 @@ class="form-control{{ $errors->has('db_port') ? ' is-invalid' : '' }}"
@endif
</div>

<div class="mb-3">
<div class="mb-3 db-name">
<label for="db_name">
@lang('setup.database.db_name')
</label>
Expand All @@ -60,7 +91,7 @@ class="form-control{{ $errors->has('db_name') ? ' is-invalid' : '' }}"
@endif
</div>

<div class="mb-3">
<div class="mb-3 db-user">
<label for="db_user">
@lang('setup.database.db_user')
</label>
Expand All @@ -74,7 +105,7 @@ class="form-control{{ $errors->has('db_user') ? ' is-invalid' : '' }}"
@endif
</div>

<div class="mb-3">
<div class="mb-3 db-password">
<label for="db_password">
@lang('setup.database.db_password')
</label>
Expand Down Expand Up @@ -102,7 +133,7 @@ class="form-control{{ $errors->has('db_password') ? ' is-invalid' : '' }}"
</div>
@endif

<button type="submit" class="btn btn-primary">
<button type="submit" class="btn btn-primary db-submit">
@if($errors->any())
@lang('setup.try_again')
@else
Expand Down
2 changes: 2 additions & 0 deletions resources/views/setup/requirements.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
@lang('setup.requirements.' . $key)
@if($successful)
<x-icon.check class="text-success"/>
@elseif(str_contains($key, 'pdo'))
<x-icon.warning class="text-warning"/>
@else
<x-icon.ban class="text-danger"/>
@endif
Expand Down

0 comments on commit fb73148

Please sign in to comment.