mirror of
https://github.com/koel/koel
synced 2024-11-10 06:34:14 +00:00
Non interactive koel:init (#886)
* 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
This commit is contained in:
parent
e554448a3e
commit
7ba295efad
8 changed files with 98 additions and 36 deletions
13
.env.example
13
.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)
|
||||
|
@ -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.
|
||||
|
|
|
@ -49,6 +49,10 @@ class InitCommand extends Command
|
|||
$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();
|
||||
|
@ -121,24 +125,16 @@ class InitCommand extends Command
|
|||
]);
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -154,16 +150,22 @@ class InitCommand extends Command
|
|||
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;
|
||||
|
@ -216,7 +218,7 @@ class InitCommand extends Command
|
|||
$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();
|
||||
}
|
||||
}
|
||||
|
@ -236,4 +238,50 @@ class InitCommand extends Command
|
|||
$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));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Repositories;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Contracts\Auth\Guard;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Collection;
|
||||
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,13 +5,13 @@ namespace App\Services;
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use App\Repositories\ArtistRepository;
|
|||
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;
|
||||
|
||||
|
@ -58,7 +58,7 @@ class MediaSyncService
|
|||
HelperService $helperService,
|
||||
FileSynchronizer $fileSynchronizer,
|
||||
Finder $finder,
|
||||
Logger $logger
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->mediaMetadataService = $mediaMetadataService;
|
||||
$this->songRepository = $songRepository;
|
||||
|
|
|
@ -17,6 +17,8 @@ return [
|
|||
'password' => env('ADMIN_PASSWORD'),
|
||||
],
|
||||
|
||||
'media_path' => env('MEDIA_PATH'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Sync Options
|
||||
|
|
|
@ -5,9 +5,6 @@ namespace Tests\Integration\Repositories;
|
|||
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
|
||||
|
@ -17,11 +14,6 @@ class SongRepositoryTest extends TestCase
|
|||
*/
|
||||
private $helperService;
|
||||
|
||||
/**
|
||||
* @var Guard|MockInterface
|
||||
*/
|
||||
private $auth;
|
||||
|
||||
/**
|
||||
* @var SongRepository
|
||||
*/
|
||||
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue