Skip to content

Commit

Permalink
Non interactive koel:init (koel#886)
Browse files Browse the repository at this point in the history
* Use ADMIN_* variables if available to create the admin account

* Add APP_MEDIA_PATH for media directory

* Use the standard --no-interaction flag to koel:init

* Undo variable aligment and code formatting

* Prefer early return over else, add new line before return statements

* Some fixes
  • Loading branch information
javier-lopez authored and phanan committed Jan 1, 2019
1 parent 5623ac3 commit d265fbc
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 36 deletions.
13 changes: 13 additions & 0 deletions .env.example
@@ -1,3 +1,5 @@
APP_NAME=Koel

# Database connection name, which corresponds to the database driver.
# Possible values are:
# mysql (MySQL/MariaDB - default)
Expand All @@ -16,6 +18,17 @@ APP_KEY=
# Another random 32-char string. You can leave this empty if use php artisan koel:init.
JWT_SECRET=

# Credentials and other info to be used when Koel is installed in non-interactive mode
# (php artisan koel:init --no-interaction)
# By default (interactive mode), Koel will still prompt for these information during installation,
# but provide the values here as the defaults (except ADMIN_PASSWORD, for security reason).
ADMIN_NAME="Koel Admin"
ADMIN_EMAIL=admin@koel.com
ADMIN_PASSWORD=SoSecureMuchWow
# The ABSOLUTE path to your media. This value can always be changed later via the web interface.
MEDIA_PATH=


# By default, Koel ignores dot files and folders. This greatly improves performance if your media
# root have folders like .git or .cache. If by any chance your media files are under a dot folder,
# set the following setting to false.
Expand Down
84 changes: 66 additions & 18 deletions app/Console/Commands/InitCommand.php
Expand Up @@ -49,6 +49,10 @@ public function handle(): void
$this->comment('Remember, you can always install/upgrade manually following the guide here:');
$this->info('📙 '.config('koel.misc.docs_url').PHP_EOL);

if ($this->inNoInteractionMode()) {
$this->info('Running in no-interaction mode');
}

$this->maybeGenerateAppKey();
$this->maybeGenerateJwtSecret();
$this->maybeSetUpDatabase();
Expand Down Expand Up @@ -121,24 +125,16 @@ private function setUpDatabase(): void
]);
}

private function inNoInteractionMode(): bool
{
return (bool) $this->option('no-interaction');
}

private function setUpAdminAccount(): void
{
$this->info("Let's create the admin account.");
$name = $this->ask('Your name');
$email = $this->ask('Your email address');
$passwordConfirmed = false;
$password = null;

while (!$passwordConfirmed) {
$password = $this->secret('Your desired password');
$confirmation = $this->secret('Again, just to make sure');

if ($confirmation !== $password) {
$this->error('That doesn\'t match. Let\'s try again.');
} else {
$passwordConfirmed = true;
}
}
[$name, $email, $password] = $this->gatherAdminAccountCredentials();

User::create([
'name' => $name,
Expand All @@ -154,16 +150,22 @@ private function maybeSetMediaPath(): void
return;
}

if ($this->inNoInteractionMode()) {
$this->setMediaPathFromEnvFile();

return;
}

$this->info('The absolute path to your media directory. If this is skipped (left blank) now, you can set it later via the web interface.');

while (true) {
$path = $this->ask('Media path', false);
$path = $this->ask('Media path', config('koel.media_path'));

if ($path === false) {
if (!$path) {
return;
}

if (is_dir($path) && is_readable($path)) {
if ($this->isValidMediaPath($path)) {
Setting::set('media_path', $path);

return;
Expand Down Expand Up @@ -216,7 +218,7 @@ private function maybeSetUpDatabase(): void
$dbSetUp = true;
} catch (Exception $e) {
$this->error($e->getMessage());
$this->warn(PHP_EOL.'Koel cannot connect to the database. Let\'s set it up.');
$this->warn(PHP_EOL."Koel cannot connect to the database. Let's set it up.");
$this->setUpDatabase();
}
}
Expand All @@ -236,4 +238,50 @@ private function compileFrontEndAssets(): void
$this->info('Compiling front-end stuff');
system('yarn install');
}

/** @return array<string> */
private function gatherAdminAccountCredentials(): array
{
if ($this->inNoInteractionMode()) {
return [config('koel.admin.name'), config('koel.admin.email'), config('koel.admin.password')];
}

$name = $this->ask('Your name', config('koel.admin.name'));
$email = $this->ask('Your email address', config('koel.admin.email'));
$passwordConfirmed = false;
$password = null;

while (!$passwordConfirmed) {
$password = $this->secret('Your desired password');
$confirmation = $this->secret('Again, just to make sure');

if ($confirmation !== $password) {
$this->error("That doesn't match. Let's try again.");
} else {
$passwordConfirmed = true;
}
}

return [$name, $email, $password];
}

private function isValidMediaPath(string $path): bool
{
return is_dir($path) && is_readable($path);
}

private function setMediaPathFromEnvFile(): void
{
with(config('koel.media_path'), function (?string $path): void {
if (!$path) {
return;
}

if ($this->isValidMediaPath($path)) {
Setting::set('media_path', $path);
} else {
$this->warn(sprintf('The path %s does not exist or not readable. Skipping.', $path));
}
});
}
}
12 changes: 10 additions & 2 deletions app/Repositories/AbstractRepository.php
Expand Up @@ -2,6 +2,7 @@

namespace App\Repositories;

use Exception;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
Expand All @@ -10,14 +11,21 @@ abstract class AbstractRepository implements RepositoryInterface
{
/** @var Model */
protected $model;

/** @var Guard */
protected $auth;

abstract public function getModelClass(): string;

public function __construct(Guard $auth)
public function __construct()
{
$this->model = app($this->getModelClass());
$this->auth = $auth;

// This instantiation may fail during a console command if e.g. APP_KEY is empty,
// rendering the whole installation failing.
try {
$this->auth = app(Guard::class);
} catch (Exception $e) {}
}

public function getOneById($id): ?Model
Expand Down
4 changes: 2 additions & 2 deletions app/Repositories/SongRepository.php
Expand Up @@ -10,9 +10,9 @@ class SongRepository extends AbstractRepository
{
private $helperService;

public function __construct(Guard $auth, HelperService $helperService)
public function __construct(HelperService $helperService)
{
parent::__construct($auth);
parent::__construct();
$this->helperService = $helperService;
}

Expand Down
4 changes: 2 additions & 2 deletions app/Services/MediaMetadataService.php
Expand Up @@ -5,13 +5,13 @@
use App\Models\Album;
use App\Models\Artist;
use Exception;
use Illuminate\Log\Logger;
use Psr\Log\LoggerInterface;

class MediaMetadataService
{
private $logger;

public function __construct(Logger $logger)
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
Expand Down
4 changes: 2 additions & 2 deletions app/Services/MediaSyncService.php
Expand Up @@ -14,7 +14,7 @@
use App\Repositories\SettingRepository;
use App\Repositories\SongRepository;
use Exception;
use Illuminate\Log\Logger;
use Psr\Log\LoggerInterface;
use SplFileInfo;
use Symfony\Component\Finder\Finder;

Expand Down Expand Up @@ -58,7 +58,7 @@ public function __construct(
HelperService $helperService,
FileSynchronizer $fileSynchronizer,
Finder $finder,
Logger $logger
LoggerInterface $logger
) {
$this->mediaMetadataService = $mediaMetadataService;
$this->songRepository = $songRepository;
Expand Down
2 changes: 2 additions & 0 deletions config/koel.php
Expand Up @@ -17,6 +17,8 @@
'password' => env('ADMIN_PASSWORD'),
],

'media_path' => env('MEDIA_PATH'),

/*
|--------------------------------------------------------------------------
| Sync Options
Expand Down
11 changes: 1 addition & 10 deletions tests/Integration/Repositories/SongRepositoryTest.php
Expand Up @@ -5,9 +5,6 @@
use App\Models\Song;
use App\Repositories\SongRepository;
use App\Services\HelperService;
use Illuminate\Contracts\Auth\Guard;
use Mockery;
use Mockery\MockInterface;
use Tests\TestCase;

class SongRepositoryTest extends TestCase
Expand All @@ -17,11 +14,6 @@ class SongRepositoryTest extends TestCase
*/
private $helperService;

/**
* @var Guard|MockInterface
*/
private $auth;

/**
* @var SongRepository
*/
Expand All @@ -30,9 +22,8 @@ class SongRepositoryTest extends TestCase
public function setUp()
{
parent::setUp();
$this->auth = Mockery::mock(Guard::class);
$this->helperService = new HelperService();
$this->songRepository = new SongRepository($this->auth, $this->helperService);
$this->songRepository = new SongRepository($this->helperService);
}

public function testGetOneByPath(): void
Expand Down

0 comments on commit d265fbc

Please sign in to comment.