From 769ea469c95744c7846f387cbdf130cbfa63e49f Mon Sep 17 00:00:00 2001 From: Yassine Doghri Date: Thu, 8 Oct 2020 16:38:30 +0000 Subject: [PATCH] refactor: update install logic and add missing cache config step - add `.env.example` and `INSTALL.md` to castopod bundle for installation docs - update seeders to be silent on insert errors - update install layout - add manual config instructions when .env file is not writable - fix eslint error in Charts.ts module closes #32 --- .env.example | 27 ++ .rsync-filter | 2 + INSTALL.md | 73 +++++ app/Config/Routes.php | 10 +- app/Controllers/Admin/Podcast.php | 2 +- app/Controllers/Admin/PodcastSettings.php | 2 +- app/Controllers/Install.php | 318 ++++++++++++++-------- app/Database/Seeds/AuthSeeder.php | 11 +- app/Database/Seeds/CategorySeeder.php | 5 +- app/Database/Seeds/LanguageSeeder.php | 5 +- app/Database/Seeds/PlatformSeeder.php | 5 +- app/Helpers/url_helper.php | 22 ++ app/Language/en/Install.php | 41 ++- app/Language/en/Validation.php | 2 + app/Validation/Rules.php | 18 ++ app/Views/_assets/icons/arrow-right.svg | 6 + app/Views/_assets/icons/check.svg | 6 + app/Views/_assets/install.ts | 3 + app/Views/install/_layout.php | 3 +- app/Views/install/cache_config.php | 44 +++ app/Views/install/create_superadmin.php | 52 ++++ app/Views/install/database_config.php | 80 ++++++ app/Views/install/env.php | 98 ------- app/Views/install/error.php | 9 - app/Views/install/instance_config.php | 59 ++++ app/Views/install/manual_config.php | 14 + app/Views/install/superadmin.php | 53 ---- 27 files changed, 674 insertions(+), 296 deletions(-) create mode 100644 .env.example create mode 100644 INSTALL.md create mode 100644 app/Helpers/url_helper.php create mode 100644 app/Views/_assets/icons/arrow-right.svg create mode 100644 app/Views/_assets/icons/check.svg create mode 100644 app/Views/_assets/install.ts create mode 100644 app/Views/install/cache_config.php create mode 100644 app/Views/install/create_superadmin.php create mode 100644 app/Views/install/database_config.php delete mode 100644 app/Views/install/env.php delete mode 100644 app/Views/install/error.php create mode 100644 app/Views/install/instance_config.php create mode 100644 app/Views/install/manual_config.php delete mode 100644 app/Views/install/superadmin.php diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..a1b2a681 --- /dev/null +++ b/.env.example @@ -0,0 +1,27 @@ +#-------------------------------------------------------------------- +# Example Environment Configuration file +# +# This file can be used as a starting point for +# your castopod instance settings. +# +# For manual configuration: +# - copy this file's contents to a file named `.env` +# - replace all the default settings with your values +# - go to `/cp-install` to complete installation +#-------------------------------------------------------------------- + +# Instance configuration +app.baseURL="https://YOUR_DOMAIN_NAME/" +app.adminGateway="cp-admin" +app.authGateway="cp-auth" + +# Database configuration +database.default.hostname="localhost" +database.default.database="castopod" +database.default.username="root" +database.default.password="root" +database.default.DBPrefix="cp_" + +# Cache configuration (advanced) +# Keep as is if you don't know what this means +cache.handler="file" diff --git a/.rsync-filter b/.rsync-filter index 1ee766c2..606c8a91 100644 --- a/.rsync-filter +++ b/.rsync-filter @@ -5,7 +5,9 @@ + public/*** + vendor/*** + writable/*** ++ .env.example + DEPENDENCIES.md + LICENSE + README.md ++ INSTALL.md - ** diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 00000000..3f07f1aa --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,73 @@ +# How to install Castopod + +Castopod was thought to be easy to install. Whether using dedicated or shared hosting, you can install it on most PHP-MySQL compatible web servers. + +- [Install instructions](#install-instructions) + - [(optional) Manual configuration](#optional-manual-configuration) +- [Web Server Requirements](#web-server-requirements) + - [PHP v7.2 or higher](#php-v72-or-higher) + - [MySQL compatible database](#mysql-compatible-database) + - [(Optional) Other recommendations](#optional-other-recommendations) +- [Security concerns](#security-concerns) + +## Install instructions + +0. Create a MySQL database for Castopod with a user having access and modification privileges (for more info, see [Web Server Requirements](#web-server-requirements)). +1. Download and unzip the Castopod package onto the web server if you haven’t already. + - ⚠️ Set the web server document root to the `public/` sub-folder. +2. Run the Castopod install script by going to the install wizard page (`https://your_domain_name.com/cp-install`) in your favorite web browser. +3. Follow the instructions on your screen. + +All done, start podcasting! + +### (optional) Manual configuration + +Before uploading Castopod files to your web server: + +1. Rename the `.env.example` file to `.env` and update the default values with your own. +2. Upload the Castopod files with `.env` +3. Go to `/cp-install` to finish the install process. + +## Web Server Requirements + +### PHP v7.2 or higher + +PHP version 7.2 or higher is required, with the following extensions installed: + +- [intl](http://php.net/manual/en/intl.requirements.php) +- [libcurl](http://php.net/manual/en/curl.requirements.php) if you plan to use the HTTP\CURLRequest library +- [mbstring](http://php.net/manual/en/mbstring.installation.php) + +Additionally, make sure that the following extensions are enabled in your PHP: + +- json (enabled by default - don't turn it off) +- xml (enabled by default - don't turn it off) +- [mysqlnd](http://php.net/manual/en/mysqlnd.install.php) + +### MySQL compatible database + +> We recommend using [MariaDB](https://mariadb.org) + +You will need the server hostname, database name, username and password to complete the installation process. If you do not have these, please contact your server administrator. + +#### Privileges + +User must have at least these privileges on the database for Castopod to work: `ALTER`, `DELETE`, `EXECUTE`, `INDEX`, `INSERT`, `SELECT`, `UPDATE`. + +### (Optional) Other recommendations + +- Redis for better cache performances. +- CDN for better performances. +- e-mail gateway for lost passwords. + +## Security concerns + +Castopod is built on top of Codeigniter, a PHP framework that encourages [good security practices](https://codeigniter.com/user_guide/concepts/security.html). + +To maximize your instance safety and prevent any malicious attack, we recommend you update all your Castopod files permissions: + +- `writable/` folder must be **readable** and **writable**. +- `public/media/` folder must be **readable** and **writable**. +- any other file must be set to **readonly**. + +// TODO: add instructions on how to set file permissions. diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 159afc47..b580e3f4 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -45,8 +45,14 @@ $routes->get('/', 'Home::index', ['as' => 'home']); // Install Wizard route $routes->group(config('App')->installGateway, function ($routes) { $routes->get('/', 'Install', ['as' => 'install']); - $routes->post('generate-env', 'Install::attemptCreateEnv', [ - 'as' => 'generate-env', + $routes->post('instance-config', 'Install::attemptInstanceConfig', [ + 'as' => 'instance-config', + ]); + $routes->post('database-config', 'Install::attemptDatabaseConfig', [ + 'as' => 'database-config', + ]); + $routes->post('cache-config', 'Install::attemptCacheConfig', [ + 'as' => 'cache-config', ]); $routes->post('create-superadmin', 'Install::attemptCreateSuperAdmin', [ 'as' => 'create-superadmin', diff --git a/app/Controllers/Admin/Podcast.php b/app/Controllers/Admin/Podcast.php index 64594e07..1e68a844 100644 --- a/app/Controllers/Admin/Podcast.php +++ b/app/Controllers/Admin/Podcast.php @@ -176,7 +176,7 @@ class Podcast extends BaseController helper(['media', 'misc']); $rules = [ - 'imported_feed_url' => 'required|valid_url', + 'imported_feed_url' => 'required|validate_url', 'season_number' => 'is_natural_no_zero|permit_empty', 'max_episodes' => 'is_natural_no_zero|permit_empty', ]; diff --git a/app/Controllers/Admin/PodcastSettings.php b/app/Controllers/Admin/PodcastSettings.php index f4b65a89..e9c079c9 100644 --- a/app/Controllers/Admin/PodcastSettings.php +++ b/app/Controllers/Admin/PodcastSettings.php @@ -64,7 +64,7 @@ class PodcastSettings extends BaseController $platformLinkUrl = $platformLink['url']; if ( !empty($platformLinkUrl) && - $validation->check($platformLinkUrl, 'valid_url') + $validation->check($platformLinkUrl, 'validate_url') ) { $platformId = $platformModel->getPlatformId($platformName); array_push($platformLinksData, [ diff --git a/app/Controllers/Install.php b/app/Controllers/Install.php index fd5f4f68..712033c2 100644 --- a/app/Controllers/Install.php +++ b/app/Controllers/Install.php @@ -9,12 +9,26 @@ namespace App\Controllers; use App\Models\UserModel; +use CodeIgniter\Controller; use Config\Services; use Dotenv\Dotenv; -use Exception; -class Install extends BaseController +class Install extends Controller { + protected $helpers = ['form', 'components', 'svg']; + + /** + * Constructor. + */ + public function initController( + \CodeIgniter\HTTP\RequestInterface $request, + \CodeIgniter\HTTP\ResponseInterface $response, + \Psr\Log\LoggerInterface $logger + ) { + // Do Not Edit This Line + parent::initController($request, $response, $logger); + } + /** * Every operation goes through this method to handle * the install logic. @@ -26,25 +40,61 @@ class Install extends BaseController { try { // Check if .env is created and has all required fields - $dotenv = Dotenv::createImmutable(ROOTPATH); + $dotenv = Dotenv::createUnsafeImmutable(ROOTPATH); $dotenv->load(); - $dotenv->required([ - 'app.baseURL', - 'app.adminGateway', - 'app.authGateway', - 'database.default.hostname', - 'database.default.database', - 'database.default.username', - 'database.default.password', - 'database.default.DBPrefix', - ]); } catch (\Throwable $e) { - // Invalid .env file return $this->createEnv(); } - // Check if database configuration is ok + // Check if the created .env file is writable to continue install process + if (is_writable(ROOTPATH . '.env')) { + try { + $dotenv->required([ + 'app.baseURL', + 'app.adminGateway', + 'app.authGateway', + ]); + } catch (\Dotenv\Exception\ValidationException $e) { + // form to input instance configuration + return $this->instanceConfig(); + } + + try { + $dotenv->required([ + 'database.default.hostname', + 'database.default.database', + 'database.default.username', + 'database.default.password', + 'database.default.DBPrefix', + ]); + } catch (\Dotenv\Exception\ValidationException $e) { + return $this->databaseConfig(); + } + + try { + $dotenv->required('cache.handler'); + } catch (\Dotenv\Exception\ValidationException $e) { + return $this->cacheConfig(); + } + } else { + try { + $dotenv->required([ + 'app.baseURL', + 'app.adminGateway', + 'app.authGateway', + 'database.default.hostname', + 'database.default.database', + 'database.default.username', + 'database.default.password', + 'database.default.DBPrefix', + 'cache.handler', + ]); + } catch (\Dotenv\Exception\ValidationException $e) { + return view('install/manual_config'); + } + } + try { $db = db_connect(); @@ -57,10 +107,14 @@ class Install extends BaseController throw \CodeIgniter\Exceptions\PageNotFoundException::forPageNotFound(); } } catch (\CodeIgniter\Database\Exceptions\DatabaseException $e) { - // return an error view to - return view('install/error', [ - 'error' => lang('Install.messages.databaseConnectError'), - ]); + // Could not connect to the database + // show database config view to fix value + session()->setFlashdata( + 'error', + lang('Install.messages.databaseConnectError') + ); + + return view('install/database_config'); } // migrate if no user has been created @@ -77,92 +131,108 @@ class Install extends BaseController */ public function createEnv() { - helper('form'); + // create empty .env file + try { + $envFile = fopen(ROOTPATH . '.env', 'w'); + fclose($envFile); + } catch (\Throwable $e) { + // Could not create the .env file, redirect to a view with manual instructions on how to add it + return view('install/manual_config'); + } - return view('install/env'); + return redirect()->back(); } - /** - * Verifies that all fields have been submitted correctly and - * creates the .env file after user submits the install form. - */ - public function attemptCreateEnv() + public function instanceConfig() { - if ( - !$this->validate([ - 'hostname' => 'required|valid_url', - 'admin_gateway' => 'required|differs[auth_gateway]', - 'auth_gateway' => 'required|differs[admin_gateway]', - 'db_hostname' => 'required', - 'db_name' => 'required', - 'db_username' => 'required', - 'db_password' => 'required', - ]) - ) { + return view('install/instance_config'); + } + + public function attemptInstanceConfig() + { + $rules = [ + 'hostname' => 'required|validate_url', + 'admin_gateway' => 'required', + 'auth_gateway' => 'required|differs[admin_gateway]', + ]; + + if (!$this->validate($rules)) { return redirect() ->back() + ->withInput() ->with('errors', $this->validator->getErrors()); } - // Create .env file with post data - try { - $envFile = fopen(ROOTPATH . '.env', 'w'); - if (!$envFile) { - throw new Exception('File open failed.'); - } + self::writeEnv([ + 'app.baseURL' => $this->request->getPost('hostname'), + 'app.adminGateway' => $this->request->getPost('admin_gateway'), + 'app.authGateway' => $this->request->getPost('auth_gateway'), + ]); - $envMapping = [ - [ - 'key' => 'app.baseURL', - 'value' => $this->request->getPost('hostname'), - ], - [ - 'key' => 'app.adminGateway', - 'value' => $this->request->getPost('admin_gateway'), - ], - [ - 'key' => 'app.authGateway', - 'value' => $this->request->getPost('auth_gateway'), - ], - [ - 'key' => 'database.default.hostname', - 'value' => $this->request->getPost('db_hostname'), - ], - [ - 'key' => 'database.default.database', - 'value' => $this->request->getPost('db_name'), - ], - [ - 'key' => 'database.default.username', - 'value' => $this->request->getPost('db_username'), - ], - [ - 'key' => 'database.default.password', - 'value' => $this->request->getPost('db_password'), - ], - [ - 'key' => 'database.default.DBPrefix', - 'value' => $this->request->getPost('db_prefix'), - ], - ]; + return redirect()->back(); + } - foreach ($envMapping as $envVar) { - if ($envVar['value']) { - fwrite( - $envFile, - $envVar['key'] . '="' . $envVar['value'] . '"' . PHP_EOL - ); - } - } + public function databaseConfig() + { + return view('install/database_config'); + } - return redirect()->back(); - } catch (\Throwable $e) { + public function attemptDatabaseConfig() + { + $rules = [ + 'db_hostname' => 'required', + 'db_name' => 'required', + 'db_username' => 'required', + 'db_password' => 'required', + ]; + + if (!$this->validate($rules)) { return redirect() ->back() - ->with('error', $e->getMessage()); - } finally { - fclose($envFile); + ->withInput() + ->with('errors', $this->validator->getErrors()); } + + self::writeEnv([ + 'database.default.hostname' => $this->request->getPost( + 'db_hostname' + ), + 'database.default.database' => $this->request->getPost('db_name'), + 'database.default.username' => $this->request->getPost( + 'db_username' + ), + 'database.default.password' => $this->request->getPost( + 'db_password' + ), + 'database.default.DBPrefix' => $this->request->getPost('db_prefix'), + ]); + + return redirect()->back(); + } + + public function cacheConfig() + { + return view('install/cache_config'); + } + + public function attemptCacheConfig() + { + $rules = [ + 'cache_handler' => 'required', + ]; + + if (!$this->validate($rules)) { + return redirect() + ->back() + ->withInput() + ->with('errors', $this->validator->getErrors()); + } + + self::writeEnv([ + 'cache.handler' => $this->request->getPost('cache_handler'), + ]); + + return redirect()->back(); } /** @@ -172,14 +242,8 @@ class Install extends BaseController { $migrations = \Config\Services::migrations(); - if ( - !$migrations->setNamespace('Myth\Auth')->latest() or - !$migrations->setNamespace(APP_NAMESPACE)->latest() - ) { - return view('install/error', [ - 'error' => lang('Install.messages.migrationError'), - ]); - } + !$migrations->setNamespace('Myth\Auth')->latest(); + !$migrations->setNamespace(APP_NAMESPACE)->latest(); } /** @@ -187,16 +251,10 @@ class Install extends BaseController */ public function seed() { - try { - $seeder = \Config\Database::seeder(); + $seeder = \Config\Database::seeder(); - // Seed database - $seeder->call('AppSeeder'); - } catch (\Throwable $e) { - return view('install/error', [ - 'error' => lang('Install.messages.seedError'), - ]); - } + // Seed database + $seeder->call('AppSeeder'); } /** @@ -204,9 +262,7 @@ class Install extends BaseController */ public function createSuperAdmin() { - helper('form'); - - return view('install/superadmin'); + return view('install/create_superadmin'); } /** @@ -245,7 +301,7 @@ class Install extends BaseController $db->transStart(); if (!($userId = $userModel->insert($user, true))) { - $db->transComplete(); + $db->transRollback(); return redirect() ->back() @@ -260,11 +316,47 @@ class Install extends BaseController $db->transComplete(); // Success! - // set redirect url to admin page after being redirected to login page - $_SESSION['redirect_url'] = route_to('admin'); + // set redirect_url session as admin area to go to after login + session()->set('redirect_url', route_to('admin')); return redirect() ->route('login') ->with('message', lang('Install.messages.createSuperAdminSuccess')); } + + /** + * writes config values in .env file + * overwrites any existing key and appends new ones + * + * @param array $data key/value config pairs + * + * @return void + */ + public static function writeEnv($configData) + { + $envData = file(ROOTPATH . '.env'); // reads an array of lines + + foreach ($configData as $key => $value) { + $replaced = false; + $keyVal = $key . '="' . $value . '"' . PHP_EOL; + $envData = array_map(function ($line) use ( + $key, + $keyVal, + &$replaced + ) { + if (strpos($line, $key) === 0) { + $replaced = true; + return $keyVal; + } + return $line; + }, + $envData); + + if (!$replaced) { + array_push($envData, $keyVal); + } + } + + file_put_contents(ROOTPATH . '.env', implode('', $envData)); + } } diff --git a/app/Database/Seeds/AuthSeeder.php b/app/Database/Seeds/AuthSeeder.php index c07833b3..8c509669 100644 --- a/app/Database/Seeds/AuthSeeder.php +++ b/app/Database/Seeds/AuthSeeder.php @@ -247,10 +247,17 @@ class AuthSeeder extends Seeder } } - $this->db->table('auth_permissions')->insertBatch($dataPermissions); - $this->db->table('auth_groups')->insertBatch($dataGroups); + $this->db + ->table('auth_permissions') + ->ignore(true) + ->insertBatch($dataPermissions); + $this->db + ->table('auth_groups') + ->ignore(true) + ->insertBatch($dataGroups); $this->db ->table('auth_groups_permissions') + ->ignore(true) ->insertBatch($dataGroupsPermissions); } } diff --git a/app/Database/Seeds/CategorySeeder.php b/app/Database/Seeds/CategorySeeder.php index a6211398..f1c33c13 100644 --- a/app/Database/Seeds/CategorySeeder.php +++ b/app/Database/Seeds/CategorySeeder.php @@ -797,6 +797,9 @@ class CategorySeeder extends Seeder ], ]; - $this->db->table('categories')->insertBatch($data); + $this->db + ->table('categories') + ->ignore(true) + ->insertBatch($data); } } diff --git a/app/Database/Seeds/LanguageSeeder.php b/app/Database/Seeds/LanguageSeeder.php index da497e5e..31fee8fc 100644 --- a/app/Database/Seeds/LanguageSeeder.php +++ b/app/Database/Seeds/LanguageSeeder.php @@ -633,6 +633,9 @@ class LanguageSeeder extends Seeder ['code' => 'zu', 'name' => 'Zulu', 'native_name' => 'isiZulu'], ]; - $this->db->table('languages')->insertBatch($data); + $this->db + ->table('languages') + ->ignore(true) + ->insertBatch($data); } } diff --git a/app/Database/Seeds/PlatformSeeder.php b/app/Database/Seeds/PlatformSeeder.php index 11e6b96b..274e1e60 100644 --- a/app/Database/Seeds/PlatformSeeder.php +++ b/app/Database/Seeds/PlatformSeeder.php @@ -163,6 +163,9 @@ class PlatformSeeder extends Seeder 'icon_filename' => 'tunein.svg', ], ]; - $this->db->table('platforms')->insertBatch($data); + $this->db + ->table('platforms') + ->ignore(true) + ->insertBatch($data); } } diff --git a/app/Helpers/url_helper.php b/app/Helpers/url_helper.php new file mode 100644 index 00000000..7f9e157b --- /dev/null +++ b/app/Helpers/url_helper.php @@ -0,0 +1,22 @@ + 'Manual configuration', + 'manual_config_subtitle' => + 'Create a `.env` file with your settings and refresh the page to continue installation.', 'form' => [ - 'castopod_config' => 'Castopod configuration', + 'instance_config' => 'Instance configuration', 'hostname' => 'Hostname', 'admin_gateway' => 'Admin gateway', + 'admin_gateway_hint' => + 'The route to access the admin area (eg. https://example.com/cp-admin). It is set by default as cp-admin, we recommend you change it for security reasons.', 'auth_gateway' => 'Auth gateway', - 'db_config' => 'Database configuration', + 'auth_gateway_hint' => + 'The route to access the authentication pages (eg. https://example.com/cp-auth). It is set by default as cp-auth, we recommend you change it for security reasons.', + 'database_config' => 'Database configuration', + 'database_config_hint' => + 'Castopod needs to connect to your MySQL (or MariaDB) database. If you do not have these required info, please contact your server administrator.', 'db_hostname' => 'Database hostname', 'db_name' => 'Database name', 'db_username' => 'Database username', 'db_password' => 'Database password', 'db_prefix' => 'Database prefix', - 'submit_install' => 'Install!', + 'db_prefix_hint' => + 'The prefix of the Castopod table names, leave as is if you don\'t know what it means.', + 'cache_config' => 'Cache configuration', + 'cache_config_hint' => + 'Choose your preferred cache handler. Leave it as the default value if you have no clue what it means.', + 'cache_handler' => 'Cache handler', + 'cacheHandlerOptions' => [ + 'file' => 'File', + 'redis' => 'Redis', + 'memcached' => 'Memcached', + ], + 'next' => 'Next', + 'submit' => 'Finish install', 'create_superadmin' => 'Create your superadmin account', 'email' => 'Email', 'username' => 'Username', @@ -26,17 +47,11 @@ return [ 'submit_create_superadmin' => 'Create superadmin!', ], 'messages' => [ - 'migrateSuccess' => - 'Database has been created successfully, and all required data have been stored!', 'createSuperAdminSuccess' => - 'Your superadmin account has been created successfully. Let\'s login to the admin area!', + 'Your superadmin account has been created successfully. Login to start podcasting!', 'databaseConnectError' => - 'Unable to connect to the database. Make sure the values in .env are correct. If not, edit them and refresh the page or delete the .env file to restart install.', - 'migrationError' => - 'There was an issue during migration. Make sure the values in .env are correct. If not, edit them and refresh the page or delete the .env file to restart install.', - 'seedError' => - 'There was an issue when seeding the database. Make sure the values in .env are correct. If not, edit them and refresh the page or delete the .env file to restart install.', - 'error' => - 'An error occurred during install
{message}', + 'Castopod could not connect to your database. Edit your database configuration and try again.', + 'writeError' => + 'Couldn\'t create/write the `.env` file. You must create it manually by following the `.env.example` file template in the Castopod package.', ], ]; diff --git a/app/Language/en/Validation.php b/app/Language/en/Validation.php index f9877630..4fb15636 100644 --- a/app/Language/en/Validation.php +++ b/app/Language/en/Validation.php @@ -13,4 +13,6 @@ return [ '{field} is either not an image, or it is not wide or tall enough.', 'is_image_squared' => '{field} is either not an image, or it is not squared (width and height differ).', + 'validate_url' => + 'The {field} field must be a valid URL (eg. https://example.com/).', ]; diff --git a/app/Validation/Rules.php b/app/Validation/Rules.php index 4e98e01f..9b8ce5a4 100644 --- a/app/Validation/Rules.php +++ b/app/Validation/Rules.php @@ -29,4 +29,22 @@ class Rules } //-------------------------------------------------------------------- + + /** + * Checks a URL to ensure it's formed correctly. + * + * @param string $str + * + * @return boolean + */ + public function validate_url(string $str = null): bool + { + if (empty($str)) { + return false; + } + + return filter_var($str, FILTER_VALIDATE_URL) !== false; + } + + //-------------------------------------------------------------------- } diff --git a/app/Views/_assets/icons/arrow-right.svg b/app/Views/_assets/icons/arrow-right.svg new file mode 100644 index 00000000..f46779f7 --- /dev/null +++ b/app/Views/_assets/icons/arrow-right.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/Views/_assets/icons/check.svg b/app/Views/_assets/icons/check.svg new file mode 100644 index 00000000..a28368fc --- /dev/null +++ b/app/Views/_assets/icons/check.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/Views/_assets/install.ts b/app/Views/_assets/install.ts new file mode 100644 index 00000000..e3bb9d53 --- /dev/null +++ b/app/Views/_assets/install.ts @@ -0,0 +1,3 @@ +import Tooltip from "./modules/Tooltip"; + +Tooltip(); diff --git a/app/Views/install/_layout.php b/app/Views/install/_layout.php index 20bd2d2a..6cd4cb1e 100644 --- a/app/Views/install/_layout.php +++ b/app/Views/install/_layout.php @@ -8,6 +8,7 @@ + @@ -16,7 +17,7 @@ Castopod installer -
+
renderSection('content') ?>
diff --git a/app/Views/install/cache_config.php b/app/Views/install/cache_config.php new file mode 100644 index 00000000..ddd95d8e --- /dev/null +++ b/app/Views/install/cache_config.php @@ -0,0 +1,44 @@ +extend('install/_layout') ?> + +section('content') ?> + + 'flex flex-col max-w-sm w-full', +]) ?> + + +

3/4

+ +

+ + + lang('Install.form.cacheHandlerOptions.file'), + 'redis' => lang('Install.form.cacheHandlerOptions.redis'), + 'memcached' => lang('Install.form.cacheHandlerOptions.memcached'), + ], + old('cache_handler', 'file'), + [ + 'id' => 'cache_handler', + 'name' => 'cache_handler', + 'class' => 'form-select mb-6', + 'value' => config('Database')->default['DBPrefix'], + ] +) ?> + + 'primary'], + ['type' => 'submit', 'class' => 'self-end'] +) ?> + + + +endSection() ?> diff --git a/app/Views/install/create_superadmin.php b/app/Views/install/create_superadmin.php new file mode 100644 index 00000000..6a560773 --- /dev/null +++ b/app/Views/install/create_superadmin.php @@ -0,0 +1,52 @@ +extend('install/_layout') ?> + +section('content') ?> + + 'flex flex-col max-w-sm w-full', +]) ?> + + +

4/4

+ + + 'email', + 'name' => 'email', + 'class' => 'form-input mb-4', + 'type' => 'email', + 'required' => 'required', + 'value' => old('email'), +]) ?> + + + 'username', + 'name' => 'username', + 'class' => 'form-input mb-4', + 'required' => 'required', + 'value' => old('username'), +]) ?> + + + 'password', + 'name' => 'password', + 'class' => 'form-input mb-4', + 'type' => 'password', + 'required' => 'required', + 'autocomplete' => 'new-password', +]) ?> + + 'primary'], + ['type' => 'submit', 'class' => 'self-end'] +) ?> + + + +endSection() ?> diff --git a/app/Views/install/database_config.php b/app/Views/install/database_config.php new file mode 100644 index 00000000..16941957 --- /dev/null +++ b/app/Views/install/database_config.php @@ -0,0 +1,80 @@ +extend('install/_layout') ?> + +section('content') ?> + + 'flex flex-col max-w-sm w-full', + 'autocomplete' => 'off', +]) ?> + + +

2/4

+ +

+ + + 'db_hostname', + 'name' => 'db_hostname', + 'class' => 'form-input mb-4', + 'value' => old('db_hostname', config('Database')->default['hostname']), + 'required' => 'required', +]) ?> + + + 'db_name', + 'name' => 'db_name', + 'class' => 'form-input mb-4', + 'value' => old('db_name', config('Database')->default['database']), + 'required' => 'required', +]) ?> + + + 'db_username', + 'name' => 'db_username', + 'class' => 'form-input mb-4', + 'value' => old('db_username', config('Database')->default['username']), + 'required' => 'required', + 'autocomplete' => 'off', +]) ?> + + + 'db_password', + 'name' => 'db_password', + 'class' => 'form-input mb-4', + 'value' => old('db_password', config('Database')->default['password']), + 'type' => 'password', + 'required' => 'required', + 'autocomplete' => 'off', +]) ?> + + + 'db_prefix', + 'name' => 'db_prefix', + 'class' => 'form-input mb-6', + 'value' => old('db_prefix', config('Database')->default['DBPrefix']), +]) ?> + + 'primary'], + ['type' => 'submit', 'class' => 'self-end'] +) ?> + + + +endSection() ?> diff --git a/app/Views/install/env.php b/app/Views/install/env.php deleted file mode 100644 index 25db9b5d..00000000 --- a/app/Views/install/env.php +++ /dev/null @@ -1,98 +0,0 @@ -extend('install/_layout') ?> - -section('content') ?> - - 'flex flex-col max-w-sm mx-auto', -]) ?> - - - 'flex flex-col mb-6']) ?> - - - 'hostname', - 'name' => 'hostname', - 'class' => 'form-input mb-4', - 'value' => config('App')->baseURL, - 'required' => 'required', - ]) ?> - - - 'admin_gateway', - 'name' => 'admin_gateway', - 'class' => 'form-input mb-4', - 'value' => config('App')->adminGateway, - 'required' => 'required', - ]) ?> - - - 'auth_gateway', - 'name' => 'auth_gateway', - 'class' => 'form-input', - 'value' => config('App')->authGateway, - 'required' => 'required', - ]) ?> - - - 'flex flex-col mb-6']) ?> - - - 'db_hostname', - 'name' => 'db_hostname', - 'class' => 'form-input mb-4', - 'value' => config('Database')->default['hostname'], - 'required' => 'required', - ]) ?> - - - 'db_name', - 'name' => 'db_name', - 'class' => 'form-input mb-4', - 'value' => config('Database')->default['database'], - 'required' => 'required', - ]) ?> - - - 'db_username', - 'name' => 'db_username', - 'class' => 'form-input mb-4', - 'value' => config('Database')->default['username'], - 'required' => 'required', - ]) ?> - - - 'db_password', - 'name' => 'db_password', - 'class' => 'form-input mb-4', - 'value' => config('Database')->default['password'], - 'required' => 'required', - ]) ?> - - - 'db_prefix', - 'name' => 'db_prefix', - 'class' => 'form-input', - 'value' => config('Database')->default['DBPrefix'], - ]) ?> - - - 'primary'], - ['type' => 'submit', 'class' => 'self-end'] -) ?> - - - -endSection() ?> diff --git a/app/Views/install/error.php b/app/Views/install/error.php deleted file mode 100644 index b4f8bf0a..00000000 --- a/app/Views/install/error.php +++ /dev/null @@ -1,9 +0,0 @@ -extend('install/_layout') ?> - -section('content') ?> - -
- $error]) ?> -
- -endSection() ?> diff --git a/app/Views/install/instance_config.php b/app/Views/install/instance_config.php new file mode 100644 index 00000000..19416b9f --- /dev/null +++ b/app/Views/install/instance_config.php @@ -0,0 +1,59 @@ +extend('install/_layout') ?> + +section('content') ?> + +
+ + +

1/4

+ + 'hostname', + 'name' => 'hostname', + 'class' => 'form-input mb-4', + 'value' => old('hostname', set_value(host_url(), config('App')->baseURL)), + 'required' => 'required', +]) ?> + + + 'admin_gateway', + 'name' => 'admin_gateway', + 'class' => 'form-input mb-4', + 'value' => old('admin_gateway', config('App')->adminGateway), + 'required' => 'required', +]) ?> + + + 'auth_gateway', + 'name' => 'auth_gateway', + 'class' => 'form-input mb-6', + 'value' => old('auth_gateway', config('App')->authGateway), + 'required' => 'required', +]) ?> + + 'primary'], + ['type' => 'submit', 'class' => 'self-end'] +) ?> + + + +endSection() ?> diff --git a/app/Views/install/manual_config.php b/app/Views/install/manual_config.php new file mode 100644 index 00000000..446eee86 --- /dev/null +++ b/app/Views/install/manual_config.php @@ -0,0 +1,14 @@ +extend('install/_layout') ?> + +section('content') ?> + +

+
+ +
+

+ +endSection() +?> diff --git a/app/Views/install/superadmin.php b/app/Views/install/superadmin.php deleted file mode 100644 index 6b32c32c..00000000 --- a/app/Views/install/superadmin.php +++ /dev/null @@ -1,53 +0,0 @@ -extend('install/_layout') ?> - -section('content') ?> - - 'flex flex-col max-w-sm mx-auto', -]) ?> - - - 'flex flex-col mb-6']) ?> - - - 'email', - 'name' => 'email', - 'class' => 'form-input mb-4', - 'type' => 'email', - 'required' => 'required', - 'value' => old('email'), - ]) ?> - - - 'username', - 'name' => 'username', - 'class' => 'form-input mb-4', - 'required' => 'required', - 'value' => old('username'), - ]) ?> - - - 'password', - 'name' => 'password', - 'class' => 'form-input mb-4', - 'type' => 'password', - 'required' => 'required', - 'autocomplete' => 'new-password', - ]) ?> - - - 'primary'], - ['type' => 'submit', 'class' => 'self-end'] -) ?> - - - -endSection() ?>