Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Create Install Command #118

Draft
wants to merge 40 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
0cb1cae
Compile Assets
AlexJump24 Oct 13, 2024
3a983bb
Compile Assets
AlexJump24 Oct 13, 2024
b2f527a
Initial work on the install command to save app settings
AlexJump24 Oct 13, 2024
c21b990
Minor tweaks
AlexJump24 Oct 14, 2024
299e1d9
Add config item ready to set up configuring own database connection f…
AlexJump24 Oct 14, 2024
c32bc4f
Move items out of testbech to install command itself.
AlexJump24 Oct 14, 2024
2fc0720
move migrations higher in chain at least for now
AlexJump24 Oct 14, 2024
a42fea2
Compile Assets
AlexJump24 Oct 14, 2024
760bd43
update
AlexJump24 Oct 15, 2024
c08c450
Write to env file if prompted for configuration
AlexJump24 Oct 16, 2024
a3aeab4
tidy up
AlexJump24 Oct 17, 2024
44c3478
changes from feedback
AlexJump24 Oct 17, 2024
6b153a1
fix pipeline issue
AlexJump24 Oct 17, 2024
0305ec8
Pipeline before passed oddly so not needed
AlexJump24 Oct 18, 2024
44bb282
feedback + tidy up
AlexJump24 Oct 18, 2024
f001b7a
Compile Assets
AlexJump24 Oct 13, 2024
0971029
Initial work on the install command to save app settings
AlexJump24 Oct 13, 2024
fe389a3
Minor tweaks
AlexJump24 Oct 14, 2024
67769aa
Add config item ready to set up configuring own database connection f…
AlexJump24 Oct 14, 2024
419249e
Move items out of testbech to install command itself.
AlexJump24 Oct 14, 2024
612e94e
move migrations higher in chain at least for now
AlexJump24 Oct 14, 2024
5f87f58
Compile Assets
AlexJump24 Oct 14, 2024
9d2411b
update
AlexJump24 Oct 15, 2024
d5c85b4
Write to env file if prompted for configuration
AlexJump24 Oct 16, 2024
4331b0d
tidy up
AlexJump24 Oct 17, 2024
c8baa93
changes from feedback
AlexJump24 Oct 17, 2024
9d7bc2f
fix pipeline issue
AlexJump24 Oct 17, 2024
da9f994
Pipeline before passed oddly so not needed
AlexJump24 Oct 18, 2024
c628bc5
feedback + tidy up
AlexJump24 Oct 18, 2024
fd95eba
Merge branch 'main' into feature/issue-104-install-command
jbrooksuk Nov 30, 2024
e84e1c7
Merge branch 'feature/issue-104-install-command' of https://github.co…
AlexJump24 Dec 27, 2024
3aea0e0
Merge branch 'main' into feature/issue-104-install-command
AlexJump24 Dec 27, 2024
7aecf3e
Merge branch 'main' of https://github.com/AlexJump24/core
AlexJump24 Dec 27, 2024
fccf60f
Merge branch 'main' into feature/issue-104-install-command
AlexJump24 Dec 27, 2024
af31e0a
Add optional create user command
AlexJump24 Dec 27, 2024
790926c
change default
AlexJump24 Dec 27, 2024
4ff5de2
fix stan issues
AlexJump24 Dec 27, 2024
4184a49
Prevent user table being truncated on install in another Laravel project
AlexJump24 Dec 29, 2024
470e982
Change order of things to prevent issues with database connection
AlexJump24 Dec 29, 2024
1c0c332
Add trait to models to overload connection name to use config value f…
AlexJump24 Jan 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"illuminate/events": "^11.23.0",
"illuminate/queue": "^11.23.0",
"illuminate/support": "^11.23.0",
"laravel/prompts": "^0.1",
"nesbot/carbon": "^2.70",
"spatie/laravel-data": "^4.11",
"spatie/laravel-query-builder": "^5.5",
Expand Down
10 changes: 10 additions & 0 deletions config/cachet.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,14 @@
|
*/
'docker' => env('CACHET_DOCKER', false),

/*
|--------------------------------------------------------------------------
| Cachet Database Connection
|--------------------------------------------------------------------------
|
| Support using an alternative database connection, defaults to default connecton of application
|
*/
'database_connection' => env('CACHET_DB_CONNECTION', config('database.default')),
];
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,15 @@ public function up(): void
rescue(fn () => $this->migrator->add('app.install_id', Str::random(40)));
rescue(fn () => $this->migrator->add('app.name', 'Cachet'));
rescue(fn () => $this->migrator->add('app.domain'));
rescue(fn () => $this->migrator->add('app.about'));
rescue(fn () => $this->migrator->add('app.about', <<<'ABOUT'
Cachet is a **beautiful** and **powerful** open-source status page system.

To access the [dashboard](/dashboard), use the following credentials:
- `[email protected]`
- `test123`

Please [consider sponsoring](https://github.com/cachethq/cachet?sponsor=1) the continued development of Cachet.
ABOUT));
rescue(fn () => $this->migrator->add('app.timezone', 'UTC'));
rescue(fn () => $this->migrator->add('app.locale', 'en'));
rescue(fn () => $this->migrator->add('app.incident_days', 7));
Expand Down
48 changes: 9 additions & 39 deletions database/seeders/DatabaseSeeder.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,33 +31,24 @@ class DatabaseSeeder extends Seeder
*/
public function run(): void
{
DB::table('users')->truncate();
DB::table('incidents')->truncate();
DB::table('components')->truncate();
DB::table('component_groups')->truncate();
DB::table('schedules')->truncate();
DB::table('metrics')->truncate();
DB::table('metric_points')->truncate();
DB::table('updates')->truncate();

/** @var \Illuminate\Foundation\Auth\User $userModel */
$userModel = config('cachet.user_model');

$user = $userModel::create([
$user = $userModel::query()->firstOrCreate([
'name' => 'Cachet Demo',
'email' => '[email protected]',
'password' => bcrypt('test123'),
'email_verified_at' => now(),
]);

Schedule::create([
Schedule::query()->firstOrCreate([
'name' => 'Documentation Maintenance',
'message' => 'We will be conducting maintenance on our documentation servers. Documentation may not be available during this time.',
'scheduled_at' => now()->subHours(12)->subMinutes(45),
'completed_at' => now()->subHours(12),
]);

tap(Schedule::create([
tap(Schedule::query()->firstOrCreate([
'name' => 'Documentation Maintenance',
'message' => 'We will be conducting maintenance on our documentation servers. You may experience degraded performance during this time.',
'scheduled_at' => now()->addHours(24),
Expand All @@ -76,7 +67,7 @@ public function run(): void
$schedule->updates()->save($update);
});

$componentGroup = ComponentGroup::create([
$componentGroup = ComponentGroup::query()->firstOrCreate([
'name' => 'Cachet',
'collapsed' => ComponentGroupVisibilityEnum::expanded,
'visible' => ResourceVisibilityEnum::guest,
Expand Down Expand Up @@ -104,14 +95,14 @@ public function run(): void
],
]);

Component::create([
Component::query()->firstOrCreate([
'name' => 'Laravel Artisan Cheatsheet',
'description' => 'The Laravel Artisan Cheatsheet.',
'link' => 'https://artisan.page',
'status' => ComponentStatusEnum::operational,
]);

$metric = Metric::create([
$metric = Metric::query()->firstOrCreate([
'name' => 'Cachet API Requests',
'suffix' => 'req/s',
'description' => 'The number of requests to the Cachet API.',
Expand All @@ -127,7 +118,7 @@ public function run(): void
'created_at' => now()->subMinutes(random_int(0, $i * 60)),
]));

tap(Incident::create([
tap(Incident::query()->firstOrCreate([
'name' => 'DNS Provider Outage',
'message' => 'We\'re investigating an issue with our DNS provider causing the site to be offline.',
'status' => IncidentStatusEnum::fixed,
Expand Down Expand Up @@ -164,7 +155,7 @@ public function run(): void
$incident->updates()->save($update);
});

$incident = Incident::create([
$incident = Incident::query()->firstOrCreate([
'name' => 'Documentation Performance Degradation',
'message' => 'We\'re investigating an issue with our documentation causing the site to be slow.',
'status' => IncidentStatusEnum::fixed,
Expand All @@ -191,34 +182,13 @@ public function run(): void

$incident->updates()->save($update);

IncidentTemplate::create([
IncidentTemplate::query()->firstOrCreate([
'name' => 'Third-Party Service Outage',
'slug' => 'third-party-service-outage',
'template' => 'We\'re investigating an issue with a third-party provider ({{ name }}) causing our services to be offline.',
'engine' => IncidentTemplateEngineEnum::twig,
]);

$appSettings = app(AppSettings::class);
$appSettings->name = 'Cachet v3.x Demo';
$appSettings->about = <<<'ABOUT'
Cachet is a **beautiful** and **powerful** open-source status page system.

To access the [dashboard](/dashboard), use the following credentials:
- `[email protected]`
- `test123`

Please [consider sponsoring](https://github.com/cachethq/cachet?sponsor=1) the continued development of Cachet.
ABOUT;
$appSettings->show_support = true;
$appSettings->timezone = 'UTC';
$appSettings->show_timezone = false;
$appSettings->only_disrupted_days = false;
$appSettings->incident_days = 7;
$appSettings->refresh_rate = null;
$appSettings->dashboard_login_link = true;
$appSettings->major_outage_threshold = 25;
$appSettings->save();

$customizationSettings = app(CustomizationSettings::class);
$customizationSettings->header = <<<'HTML'
<script src="https://cdn.usefathom.com/script.js" data-site="NQKCLYJJ" defer></script>
Expand Down
18 changes: 0 additions & 18 deletions public/build/assets/cachet-DCZQ8JcZ.js

This file was deleted.

1 change: 1 addition & 0 deletions public/build/assets/cachet.30cb9e62.css

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions public/build/assets/cachet.6122e927.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions public/build/assets/theme.8251fad4.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/build/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
"src": "resources/js/cachet.js",
"isEntry": true
}
}
}
1 change: 1 addition & 0 deletions src/CachetCoreServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ private function registerCommands(): void
{
if ($this->app->runningInConsole()) {
$this->commands([
Commands\InstallCommand::class,
Commands\MakeUserCommand::class,
Commands\SendBeaconCommand::class,
Commands\VersionCommand::class,
Expand Down
168 changes: 168 additions & 0 deletions src/Commands/InstallCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
<?php

namespace Cachet\Commands;

use Cachet\Database\Seeders\DatabaseSeeder;
use Cachet\Settings\AppSettings;
use Cachet\Settings\Attributes\Description;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Sleep;
use Illuminate\Support\Str;
use ReflectionClass;
use ReflectionProperty;
use function Laravel\Prompts\confirm;
use function Laravel\Prompts\info;
use function Laravel\Prompts\intro;
use function Laravel\Prompts\text;

class InstallCommand extends Command
{
protected $name = 'cachet:install';

protected $description = 'Install Cachet';

public function handle(AppSettings $settings): int
{
intro('Welcome to the Cachet installer!');

$configureCachet = false;

if (confirm('Do you want to configure Cachet before installing?', true)) {
info('Configuring Cachet...');
$this->configureEnvironmentSettings();
$configureCachet = true;
}

Sleep::for(2)->seconds();

$this->call('migrate', ['--database' => config('cachet.database_connection')]);

if ($configureCachet) {
$settings = $this->configureDatabaseSettings($settings);
}

if (confirm('Do you wish to seed any sample data?', true)) {
$this->call(
'db:seed',
[
'class' => DatabaseSeeder::class,
'--database' => config('cachet.database_connection')
]
);
}

if (confirm('Do you want to create a new user?', false)) {
$this->call('cachet:make:user');
}

info('Installing Cachet...');

$this->call('filament:assets');

$settings->save();

info('Cachet is installed ⚡');

return Command::SUCCESS;
}

protected function configureEnvironmentSettings(): void
{
$path = text(
'Which path do you want Cachet to be accessible from?',
default: config('cachet.path')
);

$title = text(
'What will the title of your status page be?',
default: config('cachet.title')
);

$connection = text(
'Which database connection do you wish to use for Cachet?',
default: config('cachet.database_connection')
);

$beacon = confirm(
'Do you wish to send anonymous data to cachet to help us understand how Cachet is used?',
default: config('cachet.beacon')
);

// Override default connection to Laravel Settings saves to correct connection
app('db')->setDefaultConnection(config('cachet.database_connection'));

$this->writeEnv([
'CACHET_PATH' => $path,
'CACHET_TITLE' => $title,
'CACHET_DB_CONNECTION' => $connection,
'CACHET_BEACON' => $beacon,
]);
}

protected function writeEnv(array $values): void
{
$environmentFile = app()->environmentFile();
$environmentPath = app()->environmentPath();
$fullPath = $environmentPath . '/' . $environmentFile;

$envFileContents = File::lines($fullPath)->collect();

foreach ($values as $key => $value) {
$existingKey = $envFileContents->search(function ($line) use ($key) {
return Str::contains($line, $key, true);
});

$value = match (true) {
Str::contains($value, ' ') => Str::wrap($value,'"'),
$value === true => 'true',
$value === false => 'false',
default => $value
};

if ($existingKey === false) {
$envFileContents->push($key . '=' . $value);
} else {
$envFileContents->put($existingKey, $key . '=' . $value);
}
}

File::put($fullPath, $envFileContents->implode("\n"));
}

protected function configureDatabaseSettings(AppSettings $settings): AppSettings
{
collect(
(new ReflectionClass($settings))->getProperties(ReflectionProperty::IS_PUBLIC)
)
->filter(fn (ReflectionProperty $property) => array_key_exists($property->getName(), $settings->installable()) )
->each(function (ReflectionProperty $property) use ($settings) {
$descriptionAttribute = $property->getAttributes(Description::class);

if (empty($descriptionAttribute)) {
return;
}

$descriptionAttributeClass = $descriptionAttribute[0]->newInstance();
$default = $descriptionAttributeClass->default();
$required = $descriptionAttributeClass->required();

if ($required === false) {
return;
}

/** @var \ReflectionNamedType $namedType */
$namedType = $property->getType();

$value = match($namedType->getName()) {
'bool' => confirm($default ?: $property->getName()),
default => text($default ?: $property->getName(), default: $property->getDefaultValue() ?: '', required: true),
};

$settings->{$property->getName()} = $value;
})
->pluck('name');

return $settings;
}
}
3 changes: 3 additions & 0 deletions src/Models/Component.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Cachet\Events\Components\ComponentCreated;
use Cachet\Events\Components\ComponentDeleted;
use Cachet\Events\Components\ComponentUpdated;
use Cachet\Models\Concerns\ManagesConnections;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\Factory;
Expand Down Expand Up @@ -42,6 +43,8 @@ class Component extends Model
/** @use HasFactory<ComponentFactory> */
use HasFactory;

use ManagesConnections;

use SoftDeletes;

/** @var array<string, string> */
Expand Down
3 changes: 3 additions & 0 deletions src/Models/ComponentGroup.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Cachet\Database\Factories\ComponentGroupFactory;
use Cachet\Enums\ComponentGroupVisibilityEnum;
use Cachet\Enums\ResourceVisibilityEnum;
use Cachet\Models\Concerns\ManagesConnections;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
Expand All @@ -30,6 +31,8 @@ class ComponentGroup extends Model
/** @use HasFactory<ComponentGroupFactory> */
use HasFactory;

use ManagesConnections;

use HasVisibility;

/** @var array<string, string> */
Expand Down
11 changes: 11 additions & 0 deletions src/Models/Concerns/ManagesConnections.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Cachet\Models\Concerns;

trait ManagesConnections
{
public function getConnectionName(): string
{
return config('cachet.database_connection');
}
}
Loading
Loading