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
This commit is contained in:
Yassine Doghri 2020-10-08 16:38:30 +00:00
parent 35f633b4c7
commit 769ea469c9
27 changed files with 674 additions and 296 deletions

27
.env.example Normal file
View File

@ -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"

View File

@ -5,7 +5,9 @@
+ public/***
+ vendor/***
+ writable/***
+ .env.example
+ DEPENDENCIES.md
+ LICENSE
+ README.md
+ INSTALL.md
- **

73
INSTALL.md Normal file
View File

@ -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 havent 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.

View File

@ -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',

View File

@ -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',
];

View File

@ -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, [

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -797,6 +797,9 @@ class CategorySeeder extends Seeder
],
];
$this->db->table('categories')->insertBatch($data);
$this->db
->table('categories')
->ignore(true)
->insertBatch($data);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,22 @@
<?php
if (!function_exists('host_url')) {
/**
* Return the host URL to use in views
*
* @return string|false
*/
function host_url()
{
if (isset($_SERVER['host'])) {
$protocol =
(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ||
$_SERVER['SERVER_PORT'] == 443
? 'https://'
: 'http://';
return $protocol + $_SERVER['host'];
}
return false;
}
}

View File

@ -7,18 +7,39 @@
*/
return [
'manual_config' => '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' =>
'<strong>An error occurred during install</strong><br/> {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.',
],
];

View File

@ -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/).',
];

View File

@ -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;
}
//--------------------------------------------------------------------
}

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M16.172 11l-5.364-5.364 1.414-1.414L20 12l-7.778 7.778-1.414-1.414L16.172 13H4v-2z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 234 B

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M10 15.172l9.192-9.193 1.415 1.414L10 18l-6.364-6.364 1.414-1.414z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 218 B

View File

@ -0,0 +1,3 @@
import Tooltip from "./modules/Tooltip";
Tooltip();

View File

@ -8,6 +8,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="shortcut icon" type="image/png" href="/favicon.ico" />
<link rel="stylesheet" href="/assets/index.css"/>
<script src="/assets/install.js" type="module" defer></script>
</head>
<body class="flex flex-col min-h-screen mx-auto">
@ -16,7 +17,7 @@
Castopod installer
</div>
</header>
<main class="container flex-1 px-4 py-10 mx-auto">
<main class="container flex flex-col items-center justify-center flex-1 px-4 py-10 mx-auto">
<?= view('_message_block') ?>
<?= $this->renderSection('content') ?>
</main>

View File

@ -0,0 +1,44 @@
<?= $this->extend('install/_layout') ?>
<?= $this->section('content') ?>
<?= form_open(route_to('cache-config'), [
'class' => 'flex flex-col max-w-sm w-full',
]) ?>
<?= csrf_field() ?>
<h1 class="mb-4 text-xl"><span class="inline-flex items-center justify-center w-12 h-12 mr-2 text-sm font-semibold tracking-wider text-green-700 border-4 border-green-500 rounded-full">3/4</span><?= lang(
'Install.form.cache_config'
) ?></h1>
<p class="mb-4 text-sm text-gray-600"><?= lang(
'Install.form.cache_config_hint'
) ?></p>
<?= form_label(lang('Install.form.cache_handler'), 'db_prefix') ?>
<?= form_dropdown(
'cache_handler',
[
'file' => 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'],
]
) ?>
<?= button(
lang('Install.form.next') . icon('arrow-right', 'ml-2'),
null,
['variant' => 'primary'],
['type' => 'submit', 'class' => 'self-end']
) ?>
<?= form_close() ?>
<?= $this->endSection() ?>

View File

@ -0,0 +1,52 @@
<?= $this->extend('install/_layout') ?>
<?= $this->section('content') ?>
<?= form_open(route_to('create-superadmin'), [
'class' => 'flex flex-col max-w-sm w-full',
]) ?>
<?= csrf_field() ?>
<h1 class="mb-4 text-xl"><span class="inline-flex items-center justify-center w-12 h-12 mr-2 text-sm font-semibold tracking-wider text-green-700 border-4 border-green-500 rounded-full">4/4</span><?= lang(
'Install.form.create_superadmin'
) ?></h1>
<?= form_label(lang('Install.form.email'), 'email') ?>
<?= form_input([
'id' => 'email',
'name' => 'email',
'class' => 'form-input mb-4',
'type' => 'email',
'required' => 'required',
'value' => old('email'),
]) ?>
<?= form_label(lang('Install.form.username'), 'username') ?>
<?= form_input([
'id' => 'username',
'name' => 'username',
'class' => 'form-input mb-4',
'required' => 'required',
'value' => old('username'),
]) ?>
<?= form_label(lang('Install.form.password'), 'password') ?>
<?= form_input([
'id' => 'password',
'name' => 'password',
'class' => 'form-input mb-4',
'type' => 'password',
'required' => 'required',
'autocomplete' => 'new-password',
]) ?>
<?= button(
icon('check', 'mr-2') . lang('Install.form.submit'),
null,
['variant' => 'primary'],
['type' => 'submit', 'class' => 'self-end']
) ?>
<?= form_close() ?>
<?= $this->endSection() ?>

View File

@ -0,0 +1,80 @@
<?= $this->extend('install/_layout') ?>
<?= $this->section('content') ?>
<?= form_open(route_to('database-config'), [
'class' => 'flex flex-col max-w-sm w-full',
'autocomplete' => 'off',
]) ?>
<?= csrf_field() ?>
<h1 class="mb-2 text-xl"><span class="inline-flex items-center justify-center w-12 h-12 mr-2 text-sm font-semibold tracking-wider text-green-700 border-4 border-green-500 rounded-full">2/4</span><?= lang(
'Install.form.database_config'
) ?></h1>
<p class="mb-4 text-sm text-gray-600"><?= lang(
'Install.form.database_config_hint'
) ?></p>
<?= form_label(lang('Install.form.db_hostname'), 'db_hostname') ?>
<?= form_input([
'id' => 'db_hostname',
'name' => 'db_hostname',
'class' => 'form-input mb-4',
'value' => old('db_hostname', config('Database')->default['hostname']),
'required' => 'required',
]) ?>
<?= form_label(lang('Install.form.db_name'), 'db_name') ?>
<?= form_input([
'id' => 'db_name',
'name' => 'db_name',
'class' => 'form-input mb-4',
'value' => old('db_name', config('Database')->default['database']),
'required' => 'required',
]) ?>
<?= form_label(lang('Install.form.db_username'), 'db_username') ?>
<?= form_input([
'id' => 'db_username',
'name' => 'db_username',
'class' => 'form-input mb-4',
'value' => old('db_username', config('Database')->default['username']),
'required' => 'required',
'autocomplete' => 'off',
]) ?>
<?= form_label(lang('Install.form.db_password'), 'db_password') ?>
<?= form_input([
'id' => 'db_password',
'name' => 'db_password',
'class' => 'form-input mb-4',
'value' => old('db_password', config('Database')->default['password']),
'type' => 'password',
'required' => 'required',
'autocomplete' => 'off',
]) ?>
<?= form_label(
lang('Install.form.db_prefix'),
'db_prefix',
[],
lang('Install.form.db_prefix_hint')
) ?>
<?= form_input([
'id' => 'db_prefix',
'name' => 'db_prefix',
'class' => 'form-input mb-6',
'value' => old('db_prefix', config('Database')->default['DBPrefix']),
]) ?>
<?= button(
lang('Install.form.next') . icon('arrow-right', 'ml-2'),
null,
['variant' => 'primary'],
['type' => 'submit', 'class' => 'self-end']
) ?>
<?= form_close() ?>
<?= $this->endSection() ?>

View File

@ -1,98 +0,0 @@
<?= $this->extend('install/_layout') ?>
<?= $this->section('content') ?>
<?= form_open(route_to('generate-env'), [
'class' => 'flex flex-col max-w-sm mx-auto',
]) ?>
<?= csrf_field() ?>
<?= form_fieldset('', ['class' => 'flex flex-col mb-6']) ?>
<legend class="mb-4 text-xl"><?= lang(
'Install.form.castopod_config'
) ?></legend>
<?= form_label(lang('Install.form.hostname'), 'hostname') ?>
<?= form_input([
'id' => 'hostname',
'name' => 'hostname',
'class' => 'form-input mb-4',
'value' => config('App')->baseURL,
'required' => 'required',
]) ?>
<?= form_label(lang('Install.form.admin_gateway'), 'admin_gateway') ?>
<?= form_input([
'id' => 'admin_gateway',
'name' => 'admin_gateway',
'class' => 'form-input mb-4',
'value' => config('App')->adminGateway,
'required' => 'required',
]) ?>
<?= form_label(lang('Install.form.auth_gateway'), 'auth_gateway') ?>
<?= form_input([
'id' => 'auth_gateway',
'name' => 'auth_gateway',
'class' => 'form-input',
'value' => config('App')->authGateway,
'required' => 'required',
]) ?>
<?= form_fieldset_close() ?>
<?= form_fieldset('', ['class' => 'flex flex-col mb-6']) ?>
<legend class="mb-4 text-xl"><?= lang('Install.form.db_config') ?></legend>
<?= form_label(lang('Install.form.db_hostname'), 'db_hostname') ?>
<?= form_input([
'id' => 'db_hostname',
'name' => 'db_hostname',
'class' => 'form-input mb-4',
'value' => config('Database')->default['hostname'],
'required' => 'required',
]) ?>
<?= form_label(lang('Install.form.db_name'), 'db_name') ?>
<?= form_input([
'id' => 'db_name',
'name' => 'db_name',
'class' => 'form-input mb-4',
'value' => config('Database')->default['database'],
'required' => 'required',
]) ?>
<?= form_label(lang('Install.form.db_username'), 'db_username') ?>
<?= form_input([
'id' => 'db_username',
'name' => 'db_username',
'class' => 'form-input mb-4',
'value' => config('Database')->default['username'],
'required' => 'required',
]) ?>
<?= form_label(lang('Install.form.db_password'), 'db_password') ?>
<?= form_input([
'id' => 'db_password',
'name' => 'db_password',
'class' => 'form-input mb-4',
'value' => config('Database')->default['password'],
'required' => 'required',
]) ?>
<?= form_label(lang('Install.form.db_prefix'), 'db_prefix') ?>
<?= form_input([
'id' => 'db_prefix',
'name' => 'db_prefix',
'class' => 'form-input',
'value' => config('Database')->default['DBPrefix'],
]) ?>
<?= form_fieldset_close() ?>
<?= button(
lang('Install.form.submit_install'),
null,
['variant' => 'primary'],
['type' => 'submit', 'class' => 'self-end']
) ?>
<?= form_close() ?>
<?= $this->endSection() ?>

View File

@ -1,9 +0,0 @@
<?= $this->extend('install/_layout') ?>
<?= $this->section('content') ?>
<div class="px-4 py-2 mb-4 font-semibold text-red-900 bg-red-200 border border-red-700">
<?= lang('Install.messages.error', ['message' => $error]) ?>
</div>
<?= $this->endSection() ?>

View File

@ -0,0 +1,59 @@
<?= $this->extend('install/_layout') ?>
<?= $this->section('content') ?>
<form action="<?= '/' .
config('App')->installGateway .
'/instance-config' ?>" class="flex flex-col w-full max-w-sm" method="post" accept-charset="utf-8">
<?= csrf_field() ?>
<h1 class="mb-4 text-xl"><span class="inline-flex items-center justify-center w-12 h-12 mr-2 text-sm font-semibold tracking-wider text-green-700 border-4 border-green-500 rounded-full">1/4</span><?= lang(
'Install.form.instance_config'
) ?></h1>
<?= form_label(lang('Install.form.hostname'), 'hostname') ?>
<?= form_input([
'id' => 'hostname',
'name' => 'hostname',
'class' => 'form-input mb-4',
'value' => old('hostname', set_value(host_url(), config('App')->baseURL)),
'required' => 'required',
]) ?>
<?= form_label(
lang('Install.form.admin_gateway'),
'admin_gateway',
[],
lang('Install.form.admin_gateway_hint')
) ?>
<?= form_input([
'id' => 'admin_gateway',
'name' => 'admin_gateway',
'class' => 'form-input mb-4',
'value' => old('admin_gateway', config('App')->adminGateway),
'required' => 'required',
]) ?>
<?= form_label(
lang('Install.form.auth_gateway'),
'auth_gateway',
[],
lang('Install.form.auth_gateway_hint')
) ?>
<?= form_input([
'id' => 'auth_gateway',
'name' => 'auth_gateway',
'class' => 'form-input mb-6',
'value' => old('auth_gateway', config('App')->authGateway),
'required' => 'required',
]) ?>
<?= button(
lang('Install.form.next') . icon('arrow-right', 'ml-2'),
null,
['variant' => 'primary'],
['type' => 'submit', 'class' => 'self-end']
) ?>
<?= form_close() ?>
<?= $this->endSection() ?>

View File

@ -0,0 +1,14 @@
<?= $this->extend('install/_layout') ?>
<?= $this->section('content') ?>
<h1 class="mb-2 text-xl font-bold"><?= lang('Install.manual_config') ?></h1>
<div class="inline-flex items-baseline max-w-2xl px-4 py-2 mb-4 font-semibold text-red-900 bg-red-200 border border-red-700">
<?= icon('alert', 'mr-2 flex-shrink-0') . lang('Install.messages.writeError') ?>
</div>
<p class="mb-4 font-semibold text-gray-600"><?= lang(
'Install.manual_config_subtitle'
) ?></p>
<?= $this->endSection()
?>

View File

@ -1,53 +0,0 @@
<?= $this->extend('install/_layout') ?>
<?= $this->section('content') ?>
<?= form_open(route_to('create-superadmin'), [
'class' => 'flex flex-col max-w-sm mx-auto',
]) ?>
<?= csrf_field() ?>
<?= form_fieldset('', ['class' => 'flex flex-col mb-6']) ?>
<legend class="mb-4 text-xl"><?= lang(
'Install.form.create_superadmin'
) ?></legend>
<?= form_label(lang('Install.form.email'), 'email') ?>
<?= form_input([
'id' => 'email',
'name' => 'email',
'class' => 'form-input mb-4',
'type' => 'email',
'required' => 'required',
'value' => old('email'),
]) ?>
<?= form_label(lang('Install.form.username'), 'username') ?>
<?= form_input([
'id' => 'username',
'name' => 'username',
'class' => 'form-input mb-4',
'required' => 'required',
'value' => old('username'),
]) ?>
<?= form_label(lang('Install.form.password'), 'password') ?>
<?= form_input([
'id' => 'password',
'name' => 'password',
'class' => 'form-input mb-4',
'type' => 'password',
'required' => 'required',
'autocomplete' => 'new-password',
]) ?>
<?= form_fieldset_close() ?>
<?= button(
lang('Install.form.submit_create_superadmin'),
null,
['variant' => 'primary'],
['type' => 'submit', 'class' => 'self-end']
) ?>
<?= form_close() ?>
<?= $this->endSection() ?>