mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-29 00:01:21 +00:00
346 lines
12 KiB
PHP
346 lines
12 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Models\User;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\File;
|
|
use Illuminate\Support\Facades\Process;
|
|
|
|
class InstallNntmux extends Command
|
|
{
|
|
/**
|
|
* The name and signature of the console command.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $signature = 'nntmux:install {--y|yes : Skip confirmation prompts and proceed with installation}';
|
|
|
|
/**
|
|
* The console command description.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $description = 'Install NNTmux';
|
|
|
|
/**
|
|
* Create a new command instance.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function __construct()
|
|
{
|
|
parent::__construct();
|
|
}
|
|
|
|
public function handle(): void
|
|
{
|
|
$yesMode = $this->option('yes');
|
|
if (File::exists(base_path().'/_install/install.lock')) {
|
|
if ($yesMode) {
|
|
$this->info('Install is locked. The file "install.lock" is present. Use interactive mode to remove it.');
|
|
exit;
|
|
} else {
|
|
if ($this->confirm('Install is locked. Do you want to remove the "install.lock" file to continue?')) {
|
|
$this->info('Removing install.lock file so we can continue with install process...');
|
|
$remove = Process::timeout(600)->run('rm _install/install.lock');
|
|
echo $remove->output();
|
|
echo $remove->errorOutput();
|
|
} else {
|
|
$this->info('Installation aborted. The file "install.lock" was not removed.');
|
|
exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (! $yesMode) {
|
|
if (! $this->confirm('Are you sure you want to install NNTmux? This will wipe your database!!')) {
|
|
$this->info('Installation aborted by user.');
|
|
exit;
|
|
}
|
|
}
|
|
|
|
// Ask for database type and configure accordingly
|
|
$this->configureDatabaseDriver($yesMode);
|
|
|
|
$this->info('Migrating tables and seeding them with initial data');
|
|
if (config('app.env') !== 'production') {
|
|
$this->call('migrate:fresh', ['--seed' => true]);
|
|
} else {
|
|
$this->call('migrate:fresh', ['--force' => true, '--seed' => true]);
|
|
}
|
|
|
|
$paths = $this->updatePaths();
|
|
if ($paths !== false) {
|
|
$this->info('Paths checked successfully');
|
|
}
|
|
|
|
$this->setupManticoreConfig();
|
|
|
|
if ($this->addAdminUser()) {
|
|
File::put(base_path().'/_install/install.lock', 'application install locked on '.now());
|
|
$this->info('Generating application key');
|
|
$this->call('key:generate', ['--force' => true]);
|
|
$this->info('NNTmux installation completed successfully');
|
|
exit();
|
|
}
|
|
|
|
$this->error('NNTmux installation failed. Fix reported problems and try again');
|
|
|
|
}
|
|
|
|
/**
|
|
* Ask user for database type and configure the driver and schema accordingly.
|
|
*/
|
|
private function configureDatabaseDriver(bool $yesMode): void
|
|
{
|
|
$currentDriver = config('database.default', 'mysql');
|
|
|
|
if ($yesMode) {
|
|
$this->info("Using current database driver: {$currentDriver}");
|
|
$dbType = $currentDriver;
|
|
} else {
|
|
$dbType = $this->choice(
|
|
'Which database are you using?',
|
|
['mariadb', 'mysql'],
|
|
$currentDriver === 'mariadb' ? 0 : 1
|
|
);
|
|
}
|
|
|
|
$this->info("Configuring database driver for: {$dbType}");
|
|
|
|
// Update .env file with the selected database driver
|
|
$this->updateEnvFile('DB_CONNECTION', $dbType);
|
|
|
|
// Handle schema file - ensure the correct schema file exists for the selected driver
|
|
$this->ensureSchemaFileExists($dbType);
|
|
|
|
// Clear config cache to ensure the new driver is used
|
|
$this->call('config:clear');
|
|
|
|
// Reload the configuration
|
|
config(['database.default' => $dbType]);
|
|
}
|
|
|
|
/**
|
|
* Update a value in the .env file.
|
|
*/
|
|
private function updateEnvFile(string $key, string $value): void
|
|
{
|
|
$envPath = base_path('.env');
|
|
|
|
if (! File::exists($envPath)) {
|
|
$this->error('.env file not found!');
|
|
|
|
return;
|
|
}
|
|
|
|
$envContent = File::get($envPath);
|
|
|
|
// Check if the key exists in the .env file
|
|
if (preg_match("/^{$key}=.*/m", $envContent)) {
|
|
// Replace the existing value
|
|
$envContent = preg_replace("/^{$key}=.*/m", "{$key}={$value}", $envContent);
|
|
} else {
|
|
// Add the key if it doesn't exist
|
|
$envContent .= "\n{$key}={$value}";
|
|
}
|
|
|
|
File::put($envPath, $envContent);
|
|
$this->info("Updated .env: {$key}={$value}");
|
|
}
|
|
|
|
/**
|
|
* Ensure the schema file exists for the selected database driver.
|
|
* Laravel looks for {connection}-schema.sql in database/schema/
|
|
*/
|
|
private function ensureSchemaFileExists(string $dbType): void
|
|
{
|
|
$schemaDir = database_path('schema');
|
|
$targetSchema = "{$schemaDir}/{$dbType}-schema.sql";
|
|
$mariadbSchema = "{$schemaDir}/mariadb-schema.sql";
|
|
|
|
// If the target schema already exists, we're good
|
|
if (File::exists($targetSchema)) {
|
|
$this->info("Schema file exists: {$dbType}-schema.sql");
|
|
|
|
return;
|
|
}
|
|
|
|
// For MySQL, we can use the MariaDB schema as they are compatible
|
|
if ($dbType === 'mysql' && File::exists($mariadbSchema)) {
|
|
$this->info('Creating MySQL schema file from MariaDB schema (they are compatible)...');
|
|
File::copy($mariadbSchema, $targetSchema);
|
|
$this->info("Created: {$dbType}-schema.sql");
|
|
|
|
return;
|
|
}
|
|
|
|
$this->warn("Schema file not found: {$dbType}-schema.sql");
|
|
$this->warn('The migration will create tables from scratch.');
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*
|
|
* @throws \Exception
|
|
* @throws \RuntimeException
|
|
*/
|
|
private function updatePaths()
|
|
{
|
|
$covers_path = config('nntmux_settings.covers_path');
|
|
$nzb_path = config('nntmux_settings.path_to_nzbs');
|
|
$zip_path = config('nntmux_settings.tmp_unzip_path');
|
|
$unrar_path = config('nntmux.tmp_unrar_path');
|
|
|
|
// Validate that all required paths are configured
|
|
if (empty($nzb_path)) {
|
|
$this->warn('NZB path (nntmux_settings.path_to_nzbs) is not configured. Please check your .env or config files.');
|
|
|
|
return false;
|
|
}
|
|
|
|
if (empty($unrar_path)) {
|
|
$this->warn('Unrar path (nntmux.tmp_unrar_path) is not configured. Please check your .env or config files.');
|
|
|
|
return false;
|
|
}
|
|
|
|
if (empty($covers_path)) {
|
|
$this->warn('Covers path (nntmux_settings.covers_path) is not configured. Please check your .env or config files.');
|
|
|
|
return false;
|
|
}
|
|
|
|
if (empty($zip_path)) {
|
|
$this->warn('Unzip path (nntmux_settings.tmp_unzip_path) is not configured. Please check your .env or config files.');
|
|
|
|
return false;
|
|
}
|
|
|
|
if (! File::isWritable($nzb_path)) {
|
|
$this->warn($nzb_path.' is not writable. Please fix folder permissions');
|
|
|
|
return false;
|
|
}
|
|
|
|
if (! file_exists($unrar_path)) {
|
|
$this->info('Creating missing '.$unrar_path.' folder');
|
|
if (! @File::makeDirectory($unrar_path) && ! File::isDirectory($unrar_path)) {
|
|
throw new \RuntimeException('Unable to create '.$unrar_path.' folder');
|
|
}
|
|
$this->info('Folder '.$unrar_path.' successfully created');
|
|
}
|
|
|
|
if (! is_writable($unrar_path)) {
|
|
$this->warn($unrar_path.' is not writable. Please fix folder permissions');
|
|
|
|
return false;
|
|
}
|
|
|
|
if (! File::isWritable($covers_path)) {
|
|
$this->warn($covers_path.' is not writable. Please fix folder permissions');
|
|
|
|
return false;
|
|
}
|
|
|
|
if (! File::isWritable($zip_path)) {
|
|
$this->warn($zip_path.' is not writable. Please fix folder permissions');
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private function addAdminUser(): bool
|
|
{
|
|
if (config('nntmux.admin_username') === '' || config('nntmux.admin_password') === '' || config('nntmux.admin_email') === '') {
|
|
$this->error('Admin user data cannot be empty! Please edit .env file and fill in admin user details and run this script again!');
|
|
exit();
|
|
}
|
|
|
|
$this->info('Adding admin user to database');
|
|
try {
|
|
User::add(config('nntmux.admin_username'), config('nntmux.admin_password'), config('nntmux.admin_email'), 2);
|
|
User::where('username', config('nntmux.admin_username'))->first()?->markEmailAsVerified();
|
|
} catch (\Throwable $e) {
|
|
echo $e->getMessage();
|
|
$this->error('Unable to add admin user!');
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Setup Manticore configuration by creating a symlink from the system config location
|
|
* to the project's config file (for native installations, not Docker).
|
|
*/
|
|
private function setupManticoreConfig(): void
|
|
{
|
|
$sourceConfig = base_path('config/manticore.conf');
|
|
$targetConfig = '/etc/manticoresearch/manticore.conf';
|
|
|
|
// Skip if running in Docker (config is mounted via docker-compose)
|
|
if (app()->runningInConsole() && getenv('LARAVEL_SAIL')) {
|
|
return;
|
|
}
|
|
|
|
if (! File::exists($sourceConfig)) {
|
|
return;
|
|
}
|
|
|
|
// Check if target directory exists
|
|
if (! File::isDirectory('/etc/manticoresearch')) {
|
|
$this->warn('Manticore config directory /etc/manticoresearch does not exist. Is Manticore installed?');
|
|
|
|
return;
|
|
}
|
|
|
|
// Check if symlink or file already exists
|
|
if (File::exists($targetConfig) || is_link($targetConfig)) {
|
|
if (is_link($targetConfig) && readlink($targetConfig) === $sourceConfig) {
|
|
$this->info('Manticore config symlink already exists and points to correct location.');
|
|
|
|
return;
|
|
}
|
|
|
|
$this->warn("Manticore config already exists at {$targetConfig}.");
|
|
$this->warn("To use the project's config, manually run: sudo ln -sf {$sourceConfig} {$targetConfig}");
|
|
|
|
return;
|
|
}
|
|
|
|
// Check if we can write to the target directory without sudo
|
|
$needsSudo = ! is_writable('/etc/manticoresearch');
|
|
|
|
if ($needsSudo) {
|
|
$this->info('Creating symlink for Manticore configuration requires sudo privileges.');
|
|
|
|
if (! $this->confirm('Do you want to create the symlink now? You will be prompted for your password.')) {
|
|
$this->warn("Skipping symlink creation. To create it manually, run: sudo ln -s {$sourceConfig} {$targetConfig}");
|
|
|
|
return;
|
|
}
|
|
|
|
// Use TTY mode to allow interactive sudo password prompt
|
|
$result = Process::forever()->tty()->run("sudo ln -s {$sourceConfig} {$targetConfig}");
|
|
} else {
|
|
$this->info('Creating symlink for Manticore configuration...');
|
|
$result = Process::run("ln -s {$sourceConfig} {$targetConfig}");
|
|
}
|
|
|
|
if ($result->successful()) {
|
|
$this->info("Manticore config symlink created: {$targetConfig} -> {$sourceConfig}");
|
|
} else {
|
|
$this->warn('Could not create Manticore config symlink.');
|
|
$this->warn($result->errorOutput());
|
|
$this->warn("Please run manually: sudo ln -s {$sourceConfig} {$targetConfig}");
|
|
}
|
|
}
|
|
}
|