mirror of
https://github.com/vitodeploy/vito.git
synced 2025-07-01 05:56:16 +00:00
Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
8665435bc4 | |||
0ec6a9dea2 | |||
bdfda05398 | |||
919cdc6892 | |||
902548e463 | |||
2462b31f3b | |||
e997d0deea | |||
f06b8f7d20 | |||
f120a570e8 | |||
2d7f225ff2 | |||
31bd146239 | |||
10a6bb57a8 |
@ -26,8 +26,8 @@ REDIS_PASSWORD=null
|
|||||||
REDIS_PORT=6379
|
REDIS_PORT=6379
|
||||||
|
|
||||||
MAIL_MAILER=smtp
|
MAIL_MAILER=smtp
|
||||||
MAIL_HOST=mailhog
|
MAIL_HOST=
|
||||||
MAIL_PORT=1025
|
MAIL_PORT=
|
||||||
MAIL_USERNAME=null
|
MAIL_USERNAME=null
|
||||||
MAIL_PASSWORD=null
|
MAIL_PASSWORD=null
|
||||||
MAIL_ENCRYPTION=null
|
MAIL_ENCRYPTION=null
|
||||||
|
@ -26,8 +26,8 @@ REDIS_PASSWORD=null
|
|||||||
REDIS_PORT=6379
|
REDIS_PORT=6379
|
||||||
|
|
||||||
MAIL_MAILER=smtp
|
MAIL_MAILER=smtp
|
||||||
MAIL_HOST=mailhog
|
MAIL_HOST=
|
||||||
MAIL_PORT=1025
|
MAIL_PORT=
|
||||||
MAIL_USERNAME=null
|
MAIL_USERNAME=null
|
||||||
MAIL_PASSWORD=null
|
MAIL_PASSWORD=null
|
||||||
MAIL_ENCRYPTION=null
|
MAIL_ENCRYPTION=null
|
||||||
|
@ -26,8 +26,8 @@ REDIS_PASSWORD=null
|
|||||||
REDIS_PORT=6379
|
REDIS_PORT=6379
|
||||||
|
|
||||||
MAIL_MAILER=smtp
|
MAIL_MAILER=smtp
|
||||||
MAIL_HOST=mailhog
|
MAIL_HOST=
|
||||||
MAIL_PORT=1025
|
MAIL_PORT=
|
||||||
MAIL_USERNAME=null
|
MAIL_USERNAME=null
|
||||||
MAIL_PASSWORD=null
|
MAIL_PASSWORD=null
|
||||||
MAIL_ENCRYPTION=null
|
MAIL_ENCRYPTION=null
|
||||||
|
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: bug
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
12
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
12
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: feature
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
To request a feature or suggest an idea please add it to the feedback boards
|
||||||
|
|
||||||
|
https://features.vitodeploy.com/
|
@ -12,11 +12,11 @@ ## Documentation
|
|||||||
|
|
||||||
## Feedbacks
|
## Feedbacks
|
||||||
|
|
||||||
https://features.vitodeploy.com
|
https://vitodeploy.featurebase.app
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
https://https://features.vitodeploy.com/roadmap
|
https://vitodeploy.featurebase.app/roadmap
|
||||||
|
|
||||||
## Contribution
|
## Contribution
|
||||||
|
|
||||||
|
@ -21,16 +21,25 @@ public function add(User $user, array $input): void
|
|||||||
'label' => $input['label'],
|
'label' => $input['label'],
|
||||||
]);
|
]);
|
||||||
$this->validateType($channel, $input);
|
$this->validateType($channel, $input);
|
||||||
$channel->data = $channel->provider()->data($input);
|
$channel->data = $channel->provider()->createData($input);
|
||||||
$channel->save();
|
$channel->save();
|
||||||
|
|
||||||
if (! $channel->provider()->connect()) {
|
if (! $channel->provider()->connect()) {
|
||||||
$channel->delete();
|
$channel->delete();
|
||||||
|
|
||||||
|
if ($channel->provider === \App\Enums\NotificationChannel::EMAIL) {
|
||||||
|
throw ValidationException::withMessages([
|
||||||
|
'email' => __('Could not connect! Make sure you configured `.env` file correctly.'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
throw ValidationException::withMessages([
|
throw ValidationException::withMessages([
|
||||||
'provider' => __('Could not connect'),
|
'provider' => __('Could not connect'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$channel->connected = true;
|
||||||
|
$channel->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,7 +58,7 @@ protected function validate(array $input): void
|
|||||||
*/
|
*/
|
||||||
protected function validateType(NotificationChannel $channel, array $input): void
|
protected function validateType(NotificationChannel $channel, array $input): void
|
||||||
{
|
{
|
||||||
Validator::make($input, $channel->provider()->validationRules())
|
Validator::make($input, $channel->provider()->createRules($input))
|
||||||
->validate();
|
->validate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,12 @@ public function update(Service $service, string $ini): void
|
|||||||
{
|
{
|
||||||
$tmpName = Str::random(10).strtotime('now');
|
$tmpName = Str::random(10).strtotime('now');
|
||||||
try {
|
try {
|
||||||
Storage::disk('local')->put($tmpName, $ini);
|
/** @var \Illuminate\Filesystem\FilesystemAdapter $storageDisk */
|
||||||
|
$storageDisk = Storage::disk('local');
|
||||||
|
|
||||||
|
$storageDisk->put($tmpName, $ini);
|
||||||
$service->server->ssh('root')->upload(
|
$service->server->ssh('root')->upload(
|
||||||
Storage::disk('local')->path($tmpName),
|
$storageDisk->path($tmpName),
|
||||||
"/etc/php/$service->version/cli/php.ini"
|
"/etc/php/$service->version/cli/php.ini"
|
||||||
);
|
);
|
||||||
$this->deleteTempFile($tmpName);
|
$this->deleteTempFile($tmpName);
|
||||||
|
36
app/Actions/Projects/CreateProject.php
Normal file
36
app/Actions/Projects/CreateProject.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Projects;
|
||||||
|
|
||||||
|
use App\Models\Project;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
|
||||||
|
class CreateProject
|
||||||
|
{
|
||||||
|
public function create(User $user, array $input): Project
|
||||||
|
{
|
||||||
|
$this->validate($user, $input);
|
||||||
|
|
||||||
|
$project = new Project([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'name' => $input['name'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$project->save();
|
||||||
|
|
||||||
|
return $project;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function validate(User $user, array $input): void
|
||||||
|
{
|
||||||
|
Validator::make($input, [
|
||||||
|
'name' => [
|
||||||
|
'required',
|
||||||
|
'string',
|
||||||
|
'max:255',
|
||||||
|
'unique:projects,name,NULL,id,user_id,'.$user->id,
|
||||||
|
],
|
||||||
|
])->validate();
|
||||||
|
}
|
||||||
|
}
|
31
app/Actions/Projects/DeleteProject.php
Normal file
31
app/Actions/Projects/DeleteProject.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Projects;
|
||||||
|
|
||||||
|
use App\Models\Project;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Validation\ValidationException;
|
||||||
|
|
||||||
|
class DeleteProject
|
||||||
|
{
|
||||||
|
public function delete(User $user, int $projectId): void
|
||||||
|
{
|
||||||
|
/** @var Project $project */
|
||||||
|
$project = $user->projects()->findOrFail($projectId);
|
||||||
|
|
||||||
|
if ($user->projects()->count() === 1) {
|
||||||
|
throw ValidationException::withMessages([
|
||||||
|
'project' => __('Cannot delete the last project.'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->current_project_id == $project->id) {
|
||||||
|
/** @var Project $randomProject */
|
||||||
|
$randomProject = $user->projects()->where('id', '!=', $project->id)->first();
|
||||||
|
$user->current_project_id = $randomProject->id;
|
||||||
|
$user->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
$project->delete();
|
||||||
|
}
|
||||||
|
}
|
33
app/Actions/Projects/UpdateProject.php
Normal file
33
app/Actions/Projects/UpdateProject.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Projects;
|
||||||
|
|
||||||
|
use App\Models\Project;
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
class UpdateProject
|
||||||
|
{
|
||||||
|
public function update(Project $project, array $input): Project
|
||||||
|
{
|
||||||
|
$this->validate($project, $input);
|
||||||
|
|
||||||
|
$project->name = $input['name'];
|
||||||
|
|
||||||
|
$project->save();
|
||||||
|
|
||||||
|
return $project;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function validate(Project $project, array $input): void
|
||||||
|
{
|
||||||
|
Validator::make($input, [
|
||||||
|
'name' => [
|
||||||
|
'required',
|
||||||
|
'string',
|
||||||
|
'max:255',
|
||||||
|
Rule::unique('projects')->ignore($project->id),
|
||||||
|
],
|
||||||
|
])->validate();
|
||||||
|
}
|
||||||
|
}
|
@ -26,6 +26,7 @@ public function create(User $creator, array $input): Server
|
|||||||
$this->validateInputs($input);
|
$this->validateInputs($input);
|
||||||
|
|
||||||
$server = new Server([
|
$server = new Server([
|
||||||
|
'project_id' => $creator->currentProject->id,
|
||||||
'user_id' => $creator->id,
|
'user_id' => $creator->id,
|
||||||
'name' => $input['name'],
|
'name' => $input['name'],
|
||||||
'ssh_user' => config('core.server_providers_default_user')[$input['provider']][$input['os']],
|
'ssh_user' => config('core.server_providers_default_user')[$input['provider']][$input['os']],
|
||||||
@ -43,9 +44,8 @@ public function create(User $creator, array $input): Server
|
|||||||
'progress_step' => 'Initializing',
|
'progress_step' => 'Initializing',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
DB::beginTransaction();
|
||||||
try {
|
try {
|
||||||
DB::beginTransaction();
|
|
||||||
|
|
||||||
if ($server->provider != 'custom') {
|
if ($server->provider != 'custom') {
|
||||||
$server->provider_id = $input['server_provider'];
|
$server->provider_id = $input['server_provider'];
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,8 @@ public function create(Server $server, array $input): Site
|
|||||||
{
|
{
|
||||||
$this->validateInputs($server, $input);
|
$this->validateInputs($server, $input);
|
||||||
|
|
||||||
|
DB::beginTransaction();
|
||||||
try {
|
try {
|
||||||
DB::beginTransaction();
|
|
||||||
|
|
||||||
$site = new Site([
|
$site = new Site([
|
||||||
'server_id' => $server->id,
|
'server_id' => $server->id,
|
||||||
'type' => $input['type'],
|
'type' => $input['type'],
|
||||||
|
@ -2,9 +2,17 @@
|
|||||||
|
|
||||||
namespace App\Contracts;
|
namespace App\Contracts;
|
||||||
|
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
|
||||||
interface Notification
|
interface Notification
|
||||||
{
|
{
|
||||||
public function subject(): string;
|
public function rawText(): string;
|
||||||
|
|
||||||
public function message(bool $mail = false): mixed;
|
public function toMail(object $notifiable): MailMessage;
|
||||||
|
|
||||||
|
public function toSlack(object $notifiable): string;
|
||||||
|
|
||||||
|
public function toDiscord(object $notifiable): string;
|
||||||
|
|
||||||
|
public function toTelegram(object $notifiable): string;
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,13 @@
|
|||||||
|
|
||||||
interface NotificationChannel
|
interface NotificationChannel
|
||||||
{
|
{
|
||||||
public function validationRules(): array;
|
public function createRules(array $input): array;
|
||||||
|
|
||||||
public function data(array $input): array;
|
public function createData(array $input): array;
|
||||||
|
|
||||||
|
public function data(): array;
|
||||||
|
|
||||||
public function connect(): bool;
|
public function connect(): bool;
|
||||||
|
|
||||||
public function sendMessage(string $subject, string $text): void;
|
public function send(object $notifiable, Notification $notification): void;
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,9 @@ interface Webserver
|
|||||||
{
|
{
|
||||||
public function createVHost(Site $site): void;
|
public function createVHost(Site $site): void;
|
||||||
|
|
||||||
public function updateVHost(Site $site): void;
|
public function updateVHost(Site $site, bool $noSSL = false, ?string $vhost = null): void;
|
||||||
|
|
||||||
|
public function getVHost(Site $site): string;
|
||||||
|
|
||||||
public function deleteSite(Site $site): void;
|
public function deleteSite(Site $site): void;
|
||||||
|
|
||||||
|
@ -11,4 +11,6 @@ final class NotificationChannel extends Enum
|
|||||||
const SLACK = 'slack';
|
const SLACK = 'slack';
|
||||||
|
|
||||||
const DISCORD = 'discord';
|
const DISCORD = 'discord';
|
||||||
|
|
||||||
|
const TELEGRAM = 'telegram';
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ final class SiteType extends Enum
|
|||||||
{
|
{
|
||||||
const PHP = 'php';
|
const PHP = 'php';
|
||||||
|
|
||||||
|
const PHP_BLANK = 'php-blank';
|
||||||
|
|
||||||
const LARAVEL = 'laravel';
|
const LARAVEL = 'laravel';
|
||||||
|
|
||||||
const WORDPRESS = 'wordpress';
|
const WORDPRESS = 'wordpress';
|
||||||
|
17
app/Facades/Notifier.php
Normal file
17
app/Facades/Notifier.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Facades;
|
||||||
|
|
||||||
|
use App\Contracts\Notification;
|
||||||
|
use Illuminate\Support\Facades\Facade;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method static void send(object $notifiable, Notification $notification)
|
||||||
|
*/
|
||||||
|
class Notifier extends Facade
|
||||||
|
{
|
||||||
|
protected static function getFacadeAccessor(): string
|
||||||
|
{
|
||||||
|
return 'notifier';
|
||||||
|
}
|
||||||
|
}
|
19
app/Helpers/Notifier.php
Normal file
19
app/Helpers/Notifier.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Helpers;
|
||||||
|
|
||||||
|
use App\Contracts\Notification;
|
||||||
|
use App\Models\NotificationChannel;
|
||||||
|
|
||||||
|
class Notifier
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* In the future we can send notifications based on the notifiable instance
|
||||||
|
* For example, If it was a server then we will send the channels specified by that server
|
||||||
|
* For now, we will send all channels.
|
||||||
|
*/
|
||||||
|
public function send(object $notifiable, Notification $notification): void
|
||||||
|
{
|
||||||
|
NotificationChannel::notifyAll($notification);
|
||||||
|
}
|
||||||
|
}
|
@ -3,8 +3,11 @@
|
|||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Exceptions\SourceControlIsNotConnected;
|
use App\Exceptions\SourceControlIsNotConnected;
|
||||||
|
use App\Facades\Notifier;
|
||||||
use App\Models\GitHook;
|
use App\Models\GitHook;
|
||||||
|
use App\Notifications\SourceControlDisconnected;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
|
||||||
class GitHookController extends Controller
|
class GitHookController extends Controller
|
||||||
@ -25,7 +28,7 @@ public function __invoke(Request $request)
|
|||||||
try {
|
try {
|
||||||
$gitHook->site->deploy();
|
$gitHook->site->deploy();
|
||||||
} catch (SourceControlIsNotConnected) {
|
} catch (SourceControlIsNotConnected) {
|
||||||
// TODO: send notification
|
Notifier::send($gitHook->sourceControl, new SourceControlDisconnected($gitHook->sourceControl));
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
Log::error('git-hook-exception', (array) $e);
|
Log::error('git-hook-exception', (array) $e);
|
||||||
}
|
}
|
||||||
|
29
app/Http/Controllers/ProjectController.php
Normal file
29
app/Http/Controllers/ProjectController.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\Project;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
|
||||||
|
class ProjectController extends Controller
|
||||||
|
{
|
||||||
|
public function index(): View
|
||||||
|
{
|
||||||
|
return view('projects.index');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function switch($projectId)
|
||||||
|
{
|
||||||
|
/** @var User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
/** @var Project $project */
|
||||||
|
$project = $user->projects()->findOrFail($projectId);
|
||||||
|
|
||||||
|
$user->current_project_id = $project->id;
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
return redirect()->route('servers');
|
||||||
|
}
|
||||||
|
}
|
19
app/Http/Livewire/Application/PhpBlankApp.php
Normal file
19
app/Http/Livewire/Application/PhpBlankApp.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Application;
|
||||||
|
|
||||||
|
use App\Models\Site;
|
||||||
|
use App\Traits\RefreshComponentOnBroadcast;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class PhpBlankApp extends Component
|
||||||
|
{
|
||||||
|
use RefreshComponentOnBroadcast;
|
||||||
|
|
||||||
|
public Site $site;
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.application.php-blank-app');
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,10 @@ class AddChannel extends Component
|
|||||||
|
|
||||||
public string $email;
|
public string $email;
|
||||||
|
|
||||||
|
public string $bot_token;
|
||||||
|
|
||||||
|
public string $chat_id;
|
||||||
|
|
||||||
public function add(): void
|
public function add(): void
|
||||||
{
|
{
|
||||||
app(\App\Actions\NotificationChannels\AddChannel::class)->add(
|
app(\App\Actions\NotificationChannels\AddChannel::class)->add(
|
||||||
|
37
app/Http/Livewire/Projects/CreateProject.php
Normal file
37
app/Http/Livewire/Projects/CreateProject.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Projects;
|
||||||
|
|
||||||
|
use App\Traits\HasToast;
|
||||||
|
use App\Traits\RefreshComponentOnBroadcast;
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class CreateProject extends Component
|
||||||
|
{
|
||||||
|
use HasToast;
|
||||||
|
use RefreshComponentOnBroadcast;
|
||||||
|
|
||||||
|
public bool $open = false;
|
||||||
|
|
||||||
|
public array $inputs = [];
|
||||||
|
|
||||||
|
public function create(): void
|
||||||
|
{
|
||||||
|
app(\App\Actions\Projects\CreateProject::class)
|
||||||
|
->create(auth()->user(), $this->inputs);
|
||||||
|
|
||||||
|
$this->emitTo(ProjectsList::class, '$refresh');
|
||||||
|
|
||||||
|
$this->dispatchBrowserEvent('created', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render(): View
|
||||||
|
{
|
||||||
|
if (request()->query('create')) {
|
||||||
|
$this->open = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('livewire.projects.create-project');
|
||||||
|
}
|
||||||
|
}
|
37
app/Http/Livewire/Projects/EditProject.php
Normal file
37
app/Http/Livewire/Projects/EditProject.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Projects;
|
||||||
|
|
||||||
|
use App\Actions\Projects\UpdateProject;
|
||||||
|
use App\Models\Project;
|
||||||
|
use App\Traits\RefreshComponentOnBroadcast;
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class EditProject extends Component
|
||||||
|
{
|
||||||
|
use RefreshComponentOnBroadcast;
|
||||||
|
|
||||||
|
public Project $project;
|
||||||
|
|
||||||
|
public array $inputs = [];
|
||||||
|
|
||||||
|
public function save(): void
|
||||||
|
{
|
||||||
|
app(UpdateProject::class)->update($this->project, $this->inputs);
|
||||||
|
|
||||||
|
$this->redirect(route('projects'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mount(): void
|
||||||
|
{
|
||||||
|
$this->inputs = [
|
||||||
|
'name' => $this->project->name,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render(): View
|
||||||
|
{
|
||||||
|
return view('livewire.projects.edit-project');
|
||||||
|
}
|
||||||
|
}
|
42
app/Http/Livewire/Projects/ProjectsList.php
Normal file
42
app/Http/Livewire/Projects/ProjectsList.php
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Projects;
|
||||||
|
|
||||||
|
use App\Actions\Projects\DeleteProject;
|
||||||
|
use App\Traits\HasToast;
|
||||||
|
use App\Traits\RefreshComponentOnBroadcast;
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Illuminate\Validation\ValidationException;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class ProjectsList extends Component
|
||||||
|
{
|
||||||
|
use HasToast;
|
||||||
|
use RefreshComponentOnBroadcast;
|
||||||
|
|
||||||
|
protected $listeners = [
|
||||||
|
'$refresh',
|
||||||
|
];
|
||||||
|
|
||||||
|
public int $deleteId;
|
||||||
|
|
||||||
|
public function delete(): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
app(DeleteProject::class)->delete(auth()->user(), $this->deleteId);
|
||||||
|
|
||||||
|
$this->redirect(route('projects'));
|
||||||
|
|
||||||
|
return;
|
||||||
|
} catch (ValidationException $e) {
|
||||||
|
$this->toast()->error($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render(): View
|
||||||
|
{
|
||||||
|
return view('livewire.projects.projects-list', [
|
||||||
|
'projects' => auth()->user()->projects()->orderByDesc('id')->get(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\Servers;
|
namespace App\Http\Livewire\Servers;
|
||||||
|
|
||||||
use App\Models\Server;
|
use App\Models\User;
|
||||||
use App\Traits\RefreshComponentOnBroadcast;
|
use App\Traits\RefreshComponentOnBroadcast;
|
||||||
use Illuminate\Contracts\View\View;
|
use Illuminate\Contracts\View\View;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
@ -13,8 +13,12 @@ class ServersList extends Component
|
|||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
{
|
{
|
||||||
|
/** @var User $user */
|
||||||
|
$user = auth()->user();
|
||||||
|
$servers = $user->currentProject->servers()->orderByDesc('created_at')->get();
|
||||||
|
|
||||||
return view('livewire.servers.servers-list', [
|
return view('livewire.servers.servers-list', [
|
||||||
'servers' => Server::all(),
|
'servers' => $servers,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
41
app/Http/Livewire/Sites/UpdateVHost.php
Normal file
41
app/Http/Livewire/Sites/UpdateVHost.php
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Sites;
|
||||||
|
|
||||||
|
use App\Models\Site;
|
||||||
|
use App\Traits\HasToast;
|
||||||
|
use App\Traits\RefreshComponentOnBroadcast;
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class UpdateVHost extends Component
|
||||||
|
{
|
||||||
|
use HasToast;
|
||||||
|
use RefreshComponentOnBroadcast;
|
||||||
|
|
||||||
|
public Site $site;
|
||||||
|
|
||||||
|
public string $vHost = 'Loading...';
|
||||||
|
|
||||||
|
public function loadVHost(): void
|
||||||
|
{
|
||||||
|
$this->vHost = $this->site->server->webserver()->handler()->getVHost($this->site);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->site->server->webserver()->handler()->updateVHost($this->site, false, $this->vHost);
|
||||||
|
|
||||||
|
$this->toast()->success('VHost updated successfully!');
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
$this->toast()->error($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render(): View
|
||||||
|
{
|
||||||
|
return view('livewire.sites.update-v-host');
|
||||||
|
}
|
||||||
|
}
|
@ -3,8 +3,10 @@
|
|||||||
namespace App\Jobs\Server;
|
namespace App\Jobs\Server;
|
||||||
|
|
||||||
use App\Events\Broadcast;
|
use App\Events\Broadcast;
|
||||||
|
use App\Facades\Notifier;
|
||||||
use App\Jobs\Job;
|
use App\Jobs\Job;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use App\Notifications\ServerDisconnected;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
|
||||||
class CheckConnection extends Job
|
class CheckConnection extends Job
|
||||||
@ -39,7 +41,7 @@ public function failed(): void
|
|||||||
{
|
{
|
||||||
$this->server->status = 'disconnected';
|
$this->server->status = 'disconnected';
|
||||||
$this->server->save();
|
$this->server->save();
|
||||||
/** @todo notify */
|
Notifier::send($this->server, new ServerDisconnected($this->server));
|
||||||
event(
|
event(
|
||||||
new Broadcast('server-status-failed', [
|
new Broadcast('server-status-failed', [
|
||||||
'server' => $this->server,
|
'server' => $this->server,
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Mail;
|
|
||||||
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
||||||
use Illuminate\Mail\Mailable;
|
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
|
|
||||||
class NotificationChannelMessage extends Mailable implements ShouldQueue
|
|
||||||
{
|
|
||||||
use Queueable, SerializesModels;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var mixed
|
|
||||||
*/
|
|
||||||
public $text;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new message instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct($subject, $text)
|
|
||||||
{
|
|
||||||
$this->subject = $subject;
|
|
||||||
$this->text = $text;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the message.
|
|
||||||
*
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function build()
|
|
||||||
{
|
|
||||||
if ($this->text instanceof MailMessage) {
|
|
||||||
return $this->markdown('vendor.notifications.email', $this->text->data());
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->markdown('emails.notification-channel-message', [
|
|
||||||
'subject' => $this->subject,
|
|
||||||
'text' => $this->text,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
25
app/Mail/NotificationMail.php
Normal file
25
app/Mail/NotificationMail.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Mail;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Mail\Mailable;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class NotificationMail extends Mailable
|
||||||
|
{
|
||||||
|
use Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public string $text;
|
||||||
|
|
||||||
|
public function __construct(string $subject, string $text)
|
||||||
|
{
|
||||||
|
$this->subject = $subject;
|
||||||
|
$this->text = $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function build(): self
|
||||||
|
{
|
||||||
|
return $this->html($this->text);
|
||||||
|
}
|
||||||
|
}
|
@ -69,8 +69,8 @@ public function deployHook(): void
|
|||||||
*/
|
*/
|
||||||
public function destroyHook(): void
|
public function destroyHook(): void
|
||||||
{
|
{
|
||||||
|
DB::beginTransaction();
|
||||||
try {
|
try {
|
||||||
DB::beginTransaction();
|
|
||||||
$this->sourceControl->provider()->destroyHook($this->site->repository, $this->hook_id);
|
$this->sourceControl->provider()->destroyHook($this->site->repository, $this->hook_id);
|
||||||
$this->delete();
|
$this->delete();
|
||||||
DB::commit();
|
DB::commit();
|
||||||
|
@ -2,19 +2,21 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Contracts\Notification;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Notifications\Notifiable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property string $provider
|
* @property int $id
|
||||||
* @property string $label
|
* @property string provider
|
||||||
* @property array $data
|
* @property array data
|
||||||
* @property bool $connected
|
* @property string label
|
||||||
* @property bool $is_default
|
* @property bool connected
|
||||||
* @property User $user
|
|
||||||
*/
|
*/
|
||||||
class NotificationChannel extends AbstractModel
|
class NotificationChannel extends AbstractModel
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
use Notifiable;
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'provider',
|
'provider',
|
||||||
@ -25,15 +27,24 @@ class NotificationChannel extends AbstractModel
|
|||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
'data' => 'json',
|
'project_id' => 'integer',
|
||||||
|
'data' => 'array',
|
||||||
'connected' => 'boolean',
|
'connected' => 'boolean',
|
||||||
'is_default' => 'boolean',
|
'is_default' => 'boolean',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function provider(): \App\Contracts\NotificationChannel
|
public function provider(): \App\Contracts\NotificationChannel
|
||||||
{
|
{
|
||||||
$provider = config('core.notification_channels_providers_class')[$this->provider];
|
$class = config('core.notification_channels_providers_class')[$this->provider];
|
||||||
|
|
||||||
return new $provider($this);
|
return new $class($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function notifyAll(Notification $notification): void
|
||||||
|
{
|
||||||
|
$channels = self::all();
|
||||||
|
foreach ($channels as $channel) {
|
||||||
|
$channel->notify($notification);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
56
app/Models/Project.php
Normal file
56
app/Models/Project.php
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property int $user_id
|
||||||
|
* @property string $name
|
||||||
|
* @property Carbon $created_at
|
||||||
|
* @property Carbon $updated_at
|
||||||
|
* @property User $user
|
||||||
|
* @property Collection<Server> $servers
|
||||||
|
* @property Collection<NotificationChannel> $notificationChannels
|
||||||
|
*/
|
||||||
|
class Project extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'user_id',
|
||||||
|
'name',
|
||||||
|
];
|
||||||
|
|
||||||
|
public static function boot(): void
|
||||||
|
{
|
||||||
|
parent::boot();
|
||||||
|
|
||||||
|
static::deleting(function (Project $project) {
|
||||||
|
$project->servers()->each(function (Server $server) {
|
||||||
|
$server->delete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function user(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function servers(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(Server::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function notificationChannels(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(NotificationChannel::class);
|
||||||
|
}
|
||||||
|
}
|
@ -4,20 +4,24 @@
|
|||||||
|
|
||||||
use App\Contracts\ServerType;
|
use App\Contracts\ServerType;
|
||||||
use App\Enums\ServerStatus;
|
use App\Enums\ServerStatus;
|
||||||
|
use App\Facades\Notifier;
|
||||||
use App\Facades\SSH;
|
use App\Facades\SSH;
|
||||||
use App\Jobs\Installation\Upgrade;
|
use App\Jobs\Installation\Upgrade;
|
||||||
use App\Jobs\Server\CheckConnection;
|
use App\Jobs\Server\CheckConnection;
|
||||||
use App\Jobs\Server\RebootServer;
|
use App\Jobs\Server\RebootServer;
|
||||||
|
use App\Notifications\ServerInstallationStarted;
|
||||||
use App\Support\Testing\SSHFake;
|
use App\Support\Testing\SSHFake;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
use Illuminate\Filesystem\FilesystemAdapter;
|
||||||
use Illuminate\Support\Facades\File;
|
use Illuminate\Support\Facades\File;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @property int $project_id
|
||||||
* @property int $user_id
|
* @property int $user_id
|
||||||
* @property string $name
|
* @property string $name
|
||||||
* @property string $ssh_user
|
* @property string $ssh_user
|
||||||
@ -38,6 +42,7 @@
|
|||||||
* @property int $security_updates
|
* @property int $security_updates
|
||||||
* @property int $progress
|
* @property int $progress
|
||||||
* @property string $progress_step
|
* @property string $progress_step
|
||||||
|
* @property Project $project
|
||||||
* @property User $creator
|
* @property User $creator
|
||||||
* @property ServerProvider $serverProvider
|
* @property ServerProvider $serverProvider
|
||||||
* @property ServerLog[] $logs
|
* @property ServerLog[] $logs
|
||||||
@ -59,6 +64,7 @@ class Server extends AbstractModel
|
|||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
|
'project_id',
|
||||||
'user_id',
|
'user_id',
|
||||||
'name',
|
'name',
|
||||||
'ssh_user',
|
'ssh_user',
|
||||||
@ -82,6 +88,7 @@ class Server extends AbstractModel
|
|||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
|
'project_id' => 'integer',
|
||||||
'user_id' => 'integer',
|
'user_id' => 'integer',
|
||||||
'type_data' => 'json',
|
'type_data' => 'json',
|
||||||
'port' => 'integer',
|
'port' => 'integer',
|
||||||
@ -106,7 +113,9 @@ public static function boot(): void
|
|||||||
$site->delete();
|
$site->delete();
|
||||||
});
|
});
|
||||||
$server->provider()->delete();
|
$server->provider()->delete();
|
||||||
$server->logs()->delete();
|
$server->logs()->each(function (ServerLog $log) {
|
||||||
|
$log->delete();
|
||||||
|
});
|
||||||
$server->services()->delete();
|
$server->services()->delete();
|
||||||
$server->databases()->delete();
|
$server->databases()->delete();
|
||||||
$server->databaseUsers()->delete();
|
$server->databaseUsers()->delete();
|
||||||
@ -125,6 +134,11 @@ public static function boot(): void
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function project(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Project::class, 'project_id');
|
||||||
|
}
|
||||||
|
|
||||||
public function creator(): BelongsTo
|
public function creator(): BelongsTo
|
||||||
{
|
{
|
||||||
return $this->belongsTo(User::class, 'user_id');
|
return $this->belongsTo(User::class, 'user_id');
|
||||||
@ -230,7 +244,7 @@ public function getServiceByUnit($unit): ?Service
|
|||||||
public function install(): void
|
public function install(): void
|
||||||
{
|
{
|
||||||
$this->type()->install();
|
$this->type()->install();
|
||||||
// $this->team->notify(new ServerInstallationStarted($this));
|
Notifier::send($this, new ServerInstallationStarted($this));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function ssh(?string $user = null): \App\Helpers\SSH|SSHFake
|
public function ssh(?string $user = null): \App\Helpers\SSH|SSHFake
|
||||||
@ -334,10 +348,13 @@ public function sshKey(): array
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @var FilesystemAdapter $storageDisk */
|
||||||
|
$storageDisk = Storage::disk(config('core.key_pairs_disk'));
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'public_key' => Str::replace("\n", '', Storage::disk(config('core.key_pairs_disk'))->get($this->id.'.pub')),
|
'public_key' => Str::replace("\n", '', Storage::disk(config('core.key_pairs_disk'))->get($this->id.'.pub')),
|
||||||
'public_key_path' => Storage::disk(config('core.key_pairs_disk'))->path($this->id.'.pub'),
|
'public_key_path' => $storageDisk->path($this->id.'.pub'),
|
||||||
'private_key_path' => Storage::disk(config('core.key_pairs_disk'))->path((string) $this->id),
|
'private_key_path' => $storageDisk->path((string) $this->id),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,17 @@ class ServerLog extends AbstractModel
|
|||||||
'site_id' => 'integer',
|
'site_id' => 'integer',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public static function boot(): void
|
||||||
|
{
|
||||||
|
parent::boot();
|
||||||
|
|
||||||
|
static::deleting(function (ServerLog $log) {
|
||||||
|
if (Storage::disk($log->disk)->exists($log->name)) {
|
||||||
|
Storage::disk($log->disk)->delete($log->name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public function getRouteKey(): string
|
public function getRouteKey(): string
|
||||||
{
|
{
|
||||||
return 'log';
|
return 'log';
|
||||||
|
@ -8,10 +8,13 @@
|
|||||||
use App\Enums\SslStatus;
|
use App\Enums\SslStatus;
|
||||||
use App\Events\Broadcast;
|
use App\Events\Broadcast;
|
||||||
use App\Exceptions\SourceControlIsNotConnected;
|
use App\Exceptions\SourceControlIsNotConnected;
|
||||||
|
use App\Facades\Notifier;
|
||||||
use App\Jobs\Site\ChangePHPVersion;
|
use App\Jobs\Site\ChangePHPVersion;
|
||||||
use App\Jobs\Site\Deploy;
|
use App\Jobs\Site\Deploy;
|
||||||
use App\Jobs\Site\DeployEnv;
|
use App\Jobs\Site\DeployEnv;
|
||||||
use App\Jobs\Site\UpdateBranch;
|
use App\Jobs\Site\UpdateBranch;
|
||||||
|
use App\Notifications\SiteInstallationFailed;
|
||||||
|
use App\Notifications\SiteInstallationSucceed;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
@ -354,8 +357,8 @@ public function enableAutoDeployment(): void
|
|||||||
throw new SourceControlIsNotConnected($this->source_control);
|
throw new SourceControlIsNotConnected($this->source_control);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DB::beginTransaction();
|
||||||
try {
|
try {
|
||||||
DB::beginTransaction();
|
|
||||||
$gitHook = new GitHook([
|
$gitHook = new GitHook([
|
||||||
'site_id' => $this->id,
|
'site_id' => $this->id,
|
||||||
'source_control_id' => $this->sourceControl()->id,
|
'source_control_id' => $this->sourceControl()->id,
|
||||||
@ -406,7 +409,7 @@ public function installationFinished(): void
|
|||||||
'site' => $this,
|
'site' => $this,
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
/** @todo notify */
|
Notifier::send($this, new SiteInstallationSucceed($this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -422,7 +425,7 @@ public function installationFailed(Throwable $e): void
|
|||||||
'site' => $this,
|
'site' => $this,
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
/** @todo notify */
|
Notifier::send($this, new SiteInstallationFailed($this));
|
||||||
Log::error('install-site-error', [
|
Log::error('install-site-error', [
|
||||||
'error' => (string) $e,
|
'error' => (string) $e,
|
||||||
]);
|
]);
|
||||||
|
@ -28,6 +28,9 @@
|
|||||||
* @property Collection $tokens
|
* @property Collection $tokens
|
||||||
* @property string $profile_photo_url
|
* @property string $profile_photo_url
|
||||||
* @property string $timezone
|
* @property string $timezone
|
||||||
|
* @property int $current_project_id
|
||||||
|
* @property Project $currentProject
|
||||||
|
* @property Collection<Project> $projects
|
||||||
*/
|
*/
|
||||||
class User extends Authenticatable
|
class User extends Authenticatable
|
||||||
{
|
{
|
||||||
@ -41,6 +44,7 @@ class User extends Authenticatable
|
|||||||
'email',
|
'email',
|
||||||
'password',
|
'password',
|
||||||
'timezone',
|
'timezone',
|
||||||
|
'current_project_id',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $hidden = [
|
protected $hidden = [
|
||||||
@ -53,6 +57,20 @@ class User extends Authenticatable
|
|||||||
protected $appends = [
|
protected $appends = [
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public static function boot(): void
|
||||||
|
{
|
||||||
|
parent::boot();
|
||||||
|
|
||||||
|
static::created(function (User $user) {
|
||||||
|
$user->createDefaultProject();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function servers(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(Server::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function sshKeys(): HasMany
|
public function sshKeys(): HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany(SshKey::class);
|
return $this->hasMany(SshKey::class);
|
||||||
@ -105,4 +123,36 @@ public function connectedSourceControls(): array
|
|||||||
|
|
||||||
return $connectedSourceControls;
|
return $connectedSourceControls;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function projects(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(Project::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function currentProject(): HasOne
|
||||||
|
{
|
||||||
|
return $this->HasOne(Project::class, 'id', 'current_project_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isMemberOfProject(Project $project): bool
|
||||||
|
{
|
||||||
|
return $project->user_id === $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createDefaultProject(): Project
|
||||||
|
{
|
||||||
|
$project = $this->projects()->first();
|
||||||
|
|
||||||
|
if (! $project) {
|
||||||
|
$project = new Project();
|
||||||
|
$project->user_id = $this->id;
|
||||||
|
$project->name = 'Default';
|
||||||
|
$project->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->current_project_id = $project->id;
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
return $project;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
13
app/NotificationChannels/AbstractNotificationChannel.php
Normal file
13
app/NotificationChannels/AbstractNotificationChannel.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\NotificationChannels;
|
||||||
|
|
||||||
|
use App\Contracts\NotificationChannel as NotificationChannelInterface;
|
||||||
|
use App\Models\NotificationChannel;
|
||||||
|
|
||||||
|
abstract class AbstractNotificationChannel implements NotificationChannelInterface
|
||||||
|
{
|
||||||
|
public function __construct(protected NotificationChannel $notificationChannel)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\NotificationChannels;
|
|
||||||
|
|
||||||
use App\Contracts\NotificationChannel as NotificationChannelContract;
|
|
||||||
use App\Models\NotificationChannel;
|
|
||||||
|
|
||||||
abstract class AbstractProvider implements NotificationChannelContract
|
|
||||||
{
|
|
||||||
protected NotificationChannel $notificationChannel;
|
|
||||||
|
|
||||||
public function __construct(NotificationChannel $notificationChannel)
|
|
||||||
{
|
|
||||||
$this->notificationChannel = $notificationChannel;
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,21 +2,34 @@
|
|||||||
|
|
||||||
namespace App\NotificationChannels;
|
namespace App\NotificationChannels;
|
||||||
|
|
||||||
|
use App\Contracts\Notification;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
class Discord extends AbstractProvider
|
class Discord extends AbstractNotificationChannel
|
||||||
{
|
{
|
||||||
public function validationRules(): array
|
public function channel(): string
|
||||||
|
{
|
||||||
|
return 'discord';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createRules(array $input): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'webhook_url' => 'required|url',
|
'webhook_url' => 'required|url',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function data(array $input): array
|
public function createData(array $input): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'webhook_url' => $input['webhook_url'],
|
'webhook_url' => $input['webhook_url'] ?? '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function data(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'webhook_url' => $this->notificationChannel->data['webhook_url'] ?? '',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,35 +37,37 @@ public function connect(): bool
|
|||||||
{
|
{
|
||||||
$connect = $this->checkConnection(
|
$connect = $this->checkConnection(
|
||||||
__('Congratulations! 🎉'),
|
__('Congratulations! 🎉'),
|
||||||
__("You've connected your Discord to Vito")."\n".
|
__("You've connected your Discord to :app", ['app' => config('app.name')])."\n".
|
||||||
__('Manage your notification channels')."\n".
|
__('Manage your notification channels')."\n".
|
||||||
route('notification-channels')
|
route('notification-channels')
|
||||||
);
|
);
|
||||||
|
|
||||||
if (! $connect) {
|
if (! $connect) {
|
||||||
|
$this->notificationChannel->delete();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->notificationChannel->connected = true;
|
||||||
|
$this->notificationChannel->save();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendMessage(string $subject, string $text): void
|
|
||||||
{
|
|
||||||
dispatch(function () use ($subject, $text) {
|
|
||||||
$data = $this->notificationChannel->data;
|
|
||||||
Http::post($data['webhook_url'], [
|
|
||||||
'content' => '*'.$subject.'*'."\n".$text,
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private function checkConnection(string $subject, string $text): bool
|
private function checkConnection(string $subject, string $text): bool
|
||||||
{
|
{
|
||||||
$data = $this->notificationChannel->data;
|
$connect = Http::post($this->data()['webhook_url'], [
|
||||||
$connect = Http::post($data['webhook_url'], [
|
|
||||||
'content' => '*'.$subject.'*'."\n".$text,
|
'content' => '*'.$subject.'*'."\n".$text,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $connect->ok();
|
return $connect->ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function send(object $notifiable, Notification $notification): void
|
||||||
|
{
|
||||||
|
$data = $this->notificationChannel->data;
|
||||||
|
Http::post($data['webhook_url'], [
|
||||||
|
'content' => $notification->toSlack($notifiable),
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,36 +2,56 @@
|
|||||||
|
|
||||||
namespace App\NotificationChannels;
|
namespace App\NotificationChannels;
|
||||||
|
|
||||||
use App\Mail\NotificationChannelMessage;
|
use App\Contracts\Notification;
|
||||||
|
use App\Mail\NotificationMail;
|
||||||
|
use App\Models\NotificationChannel;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
class Email extends AbstractProvider
|
class Email extends AbstractNotificationChannel
|
||||||
{
|
{
|
||||||
public function validationRules(): array
|
public function createRules(array $input): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'email' => 'required|email',
|
'email' => 'required|email',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function data(array $input): array
|
public function createData(array $input): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'email' => $input['email'],
|
'email' => $input['email'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function data(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'email' => $this->notificationChannel->data['email'] ?? '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
public function connect(): bool
|
public function connect(): bool
|
||||||
{
|
{
|
||||||
$this->notificationChannel->connected = true;
|
try {
|
||||||
$this->notificationChannel->save();
|
Mail::to($this->data()['email'])->send(
|
||||||
|
new NotificationMail('Test VitoDeploy', 'This is a test email!')
|
||||||
|
);
|
||||||
|
} catch (Throwable) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendMessage(string $subject, mixed $text): void
|
public function send(object $notifiable, Notification $notification): void
|
||||||
{
|
{
|
||||||
$data = $this->notificationChannel->data;
|
/** @var NotificationChannel $notifiable */
|
||||||
Mail::to($data['email'])->send(new NotificationChannelMessage($subject, $text));
|
$this->notificationChannel = $notifiable;
|
||||||
|
$message = $notification->toMail($notifiable);
|
||||||
|
|
||||||
|
Mail::to($this->data()['email'])->send(
|
||||||
|
new NotificationMail($message->subject, $message->render())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,21 +2,34 @@
|
|||||||
|
|
||||||
namespace App\NotificationChannels;
|
namespace App\NotificationChannels;
|
||||||
|
|
||||||
|
use App\Contracts\Notification;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
class Slack extends AbstractProvider
|
class Slack extends AbstractNotificationChannel
|
||||||
{
|
{
|
||||||
public function validationRules(): array
|
public function channel(): string
|
||||||
|
{
|
||||||
|
return 'slack';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createRules(array $input): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'webhook_url' => 'required|url',
|
'webhook_url' => 'required|url',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function data(array $input): array
|
public function createData(array $input): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'webhook_url' => $input['webhook_url'],
|
'webhook_url' => $input['webhook_url'] ?? '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function data(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'webhook_url' => $this->notificationChannel->data['webhook_url'] ?? '',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,35 +37,37 @@ public function connect(): bool
|
|||||||
{
|
{
|
||||||
$connect = $this->checkConnection(
|
$connect = $this->checkConnection(
|
||||||
__('Congratulations! 🎉'),
|
__('Congratulations! 🎉'),
|
||||||
__("You've connected your Slack to Vito")."\n".
|
__("You've connected your Slack to :app", ['app' => config('app.name')])."\n".
|
||||||
__('Manage your notification channels')."\n".
|
__('Manage your notification channels')."\n".
|
||||||
route('notification-channels')
|
route('notification-channels')
|
||||||
);
|
);
|
||||||
|
|
||||||
if (! $connect) {
|
if (! $connect) {
|
||||||
|
$this->notificationChannel->delete();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->notificationChannel->connected = true;
|
||||||
|
$this->notificationChannel->save();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendMessage(string $subject, string $text): void
|
|
||||||
{
|
|
||||||
dispatch(function () use ($subject, $text) {
|
|
||||||
$data = $this->notificationChannel->data;
|
|
||||||
Http::post($data['webhook_url'], [
|
|
||||||
'text' => '*'.$subject.'*'."\n".$text,
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private function checkConnection(string $subject, string $text): bool
|
private function checkConnection(string $subject, string $text): bool
|
||||||
{
|
{
|
||||||
$data = $this->notificationChannel->data;
|
$connect = Http::post($this->data()['webhook_url'], [
|
||||||
$connect = Http::post($data['webhook_url'], [
|
|
||||||
'text' => '*'.$subject.'*'."\n".$text,
|
'text' => '*'.$subject.'*'."\n".$text,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $connect->ok();
|
return $connect->ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function send(object $notifiable, Notification $notification): void
|
||||||
|
{
|
||||||
|
$data = $this->notificationChannel->data;
|
||||||
|
Http::post($data['webhook_url'], [
|
||||||
|
'text' => $notification->toSlack($notifiable),
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
67
app/NotificationChannels/Telegram.php
Normal file
67
app/NotificationChannels/Telegram.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\NotificationChannels;
|
||||||
|
|
||||||
|
use App\Contracts\Notification;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class Telegram extends AbstractNotificationChannel
|
||||||
|
{
|
||||||
|
protected string $apiUrl = 'https://api.telegram.org/bot';
|
||||||
|
|
||||||
|
public function channel(): string
|
||||||
|
{
|
||||||
|
return 'telegram';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createRules(array $input): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'bot_token' => 'required|string',
|
||||||
|
'chat_id' => 'required',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createData(array $input): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'bot_token' => $input['bot_token'],
|
||||||
|
'chat_id' => $input['chat_id'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function data(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'bot_token' => $this->notificationChannel->data['bot_token'] ?? '',
|
||||||
|
'chat_id' => $this->notificationChannel->data['chat_id'] ?? '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function connect(): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->sendToTelegram(__('Connected!'));
|
||||||
|
} catch (Throwable) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send(object $notifiable, Notification $notification): void
|
||||||
|
{
|
||||||
|
$this->sendToTelegram($notification->toTelegram($notifiable));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sendToTelegram(string $text): void
|
||||||
|
{
|
||||||
|
Http::post($this->apiUrl.$this->data()['bot_token'].'/sendMessage', [
|
||||||
|
'chat_id' => $this->data()['chat_id'],
|
||||||
|
'text' => $text,
|
||||||
|
'parse_mode' => 'markdown',
|
||||||
|
'disable_web_page_preview' => true,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
36
app/Notifications/AbstractNotification.php
Normal file
36
app/Notifications/AbstractNotification.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Notifications;
|
||||||
|
|
||||||
|
use App\Contracts\Notification as NotificationInterface;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Notification;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
abstract class AbstractNotification extends Notification implements NotificationInterface, ShouldQueue
|
||||||
|
{
|
||||||
|
use Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public function toMail(object $notifiable): MailMessage
|
||||||
|
{
|
||||||
|
return (new MailMessage())
|
||||||
|
->line($this->rawText());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toSlack(object $notifiable): string
|
||||||
|
{
|
||||||
|
return $this->rawText();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toDiscord(object $notifiable): string
|
||||||
|
{
|
||||||
|
return $this->rawText();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toTelegram(object $notifiable): string
|
||||||
|
{
|
||||||
|
return $this->rawText();
|
||||||
|
}
|
||||||
|
}
|
@ -2,12 +2,11 @@
|
|||||||
|
|
||||||
namespace App\Notifications;
|
namespace App\Notifications;
|
||||||
|
|
||||||
use App\Contracts\Notification;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
|
||||||
class FailedToDeleteServerFromProvider implements Notification
|
class FailedToDeleteServerFromProvider extends AbstractNotification
|
||||||
{
|
{
|
||||||
use Queueable;
|
use Queueable;
|
||||||
|
|
||||||
@ -18,26 +17,18 @@ public function __construct(Server $server)
|
|||||||
$this->server = $server;
|
$this->server = $server;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function subject(): string
|
public function rawText(): string
|
||||||
{
|
{
|
||||||
return __('Failed to delete the server from the provider!');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function message(bool $mail = false): mixed
|
|
||||||
{
|
|
||||||
if ($mail) {
|
|
||||||
return $this->mail();
|
|
||||||
}
|
|
||||||
|
|
||||||
return __("We couldn't delete [:server] \nfrom :provider \nPlease check your provider and delete it manually", [
|
return __("We couldn't delete [:server] \nfrom :provider \nPlease check your provider and delete it manually", [
|
||||||
'server' => $this->server->name,
|
'server' => $this->server->name,
|
||||||
'provider' => $this->server->provider,
|
'provider' => $this->server->provider,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function mail(): MailMessage
|
public function toMail(object $notifiable): MailMessage
|
||||||
{
|
{
|
||||||
return (new MailMessage)
|
return (new MailMessage)
|
||||||
|
->subject(__('Failed to delete the server from the provider!'))
|
||||||
->line("We couldn't delete [".$this->server->name.'] from '.$this->server->provider)
|
->line("We couldn't delete [".$this->server->name.'] from '.$this->server->provider)
|
||||||
->line('Please check your provider and delete it manually');
|
->line('Please check your provider and delete it manually');
|
||||||
}
|
}
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Notifications;
|
|
||||||
|
|
||||||
use App\Contracts\Notification;
|
|
||||||
use App\Models\Ssl;
|
|
||||||
|
|
||||||
class SSLExpirationAlert implements Notification
|
|
||||||
{
|
|
||||||
protected Ssl $ssl;
|
|
||||||
|
|
||||||
public function __construct(Ssl $ssl)
|
|
||||||
{
|
|
||||||
$this->ssl = $ssl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function subject(): string
|
|
||||||
{
|
|
||||||
return __('SSL expiring soon!');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function message(bool $mail = false): string
|
|
||||||
{
|
|
||||||
return $this->ssl->site->domain."'s ".__('SSL is expiring on').' '.$this->ssl->expires_at->format('Y-m-d');
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,11 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Notifications;
|
namespace App\Notifications;
|
||||||
|
|
||||||
use App\Contracts\Notification;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
|
||||||
class ServerDisconnected implements Notification
|
class ServerDisconnected extends AbstractNotification
|
||||||
{
|
{
|
||||||
protected Server $server;
|
protected Server $server;
|
||||||
|
|
||||||
@ -15,25 +14,17 @@ public function __construct(Server $server)
|
|||||||
$this->server = $server;
|
$this->server = $server;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function subject(): string
|
public function rawText(): string
|
||||||
{
|
{
|
||||||
return __('Server disconnected!');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function message(bool $mail = false): mixed
|
|
||||||
{
|
|
||||||
if ($mail) {
|
|
||||||
return $this->mail();
|
|
||||||
}
|
|
||||||
|
|
||||||
return __("We've disconnected from your server [:server]", [
|
return __("We've disconnected from your server [:server]", [
|
||||||
'server' => $this->server->name,
|
'server' => $this->server->name,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function mail(): MailMessage
|
public function toMail(object $notifiable): MailMessage
|
||||||
{
|
{
|
||||||
return (new MailMessage)
|
return (new MailMessage)
|
||||||
|
->subject(__('Server disconnected!'))
|
||||||
->line("We've disconnected from your server [".$this->server->name.'].')
|
->line("We've disconnected from your server [".$this->server->name.'].')
|
||||||
->line('Please check your sever is online and make sure that has our public keys in it');
|
->line('Please check your sever is online and make sure that has our public keys in it');
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Notifications;
|
namespace App\Notifications;
|
||||||
|
|
||||||
use App\Contracts\Notification;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
|
||||||
class ServerInstallationFailed implements Notification
|
class ServerInstallationFailed extends AbstractNotification
|
||||||
{
|
{
|
||||||
protected Server $server;
|
protected Server $server;
|
||||||
|
|
||||||
@ -15,26 +14,18 @@ public function __construct(Server $server)
|
|||||||
$this->server = $server;
|
$this->server = $server;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function subject(): string
|
public function rawText(): string
|
||||||
{
|
{
|
||||||
return __('Server installation failed!');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function message(bool $mail = false): mixed
|
|
||||||
{
|
|
||||||
if ($mail) {
|
|
||||||
return $this->mail();
|
|
||||||
}
|
|
||||||
|
|
||||||
return __("Installation failed for server [:server] \nCheck your server's logs \n:logs", [
|
return __("Installation failed for server [:server] \nCheck your server's logs \n:logs", [
|
||||||
'server' => $this->server->name,
|
'server' => $this->server->name,
|
||||||
'logs' => url('/servers/'.$this->server->id.'/logs'),
|
'logs' => url('/servers/'.$this->server->id.'/logs'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function mail(): MailMessage
|
public function toMail(object $notifiable): MailMessage
|
||||||
{
|
{
|
||||||
return (new MailMessage)
|
return (new MailMessage)
|
||||||
|
->subject(__('Server installation failed!'))
|
||||||
->line('Your server ['.$this->server->name.'] installation has been failed.')
|
->line('Your server ['.$this->server->name.'] installation has been failed.')
|
||||||
->line('Check your server logs')
|
->line('Check your server logs')
|
||||||
->action('View Logs', url('/servers/'.$this->server->id.'/logs'));
|
->action('View Logs', url('/servers/'.$this->server->id.'/logs'));
|
||||||
|
@ -2,11 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Notifications;
|
namespace App\Notifications;
|
||||||
|
|
||||||
use App\Contracts\Notification;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
|
||||||
class ServerInstallationStarted implements Notification
|
class ServerInstallationStarted extends AbstractNotification
|
||||||
{
|
{
|
||||||
protected Server $server;
|
protected Server $server;
|
||||||
|
|
||||||
@ -15,26 +14,18 @@ public function __construct(Server $server)
|
|||||||
$this->server = $server;
|
$this->server = $server;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function subject(): string
|
public function rawText(): string
|
||||||
{
|
{
|
||||||
return __('Server installation started!');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function message(bool $mail = false): mixed
|
|
||||||
{
|
|
||||||
if ($mail) {
|
|
||||||
return $this->mail();
|
|
||||||
}
|
|
||||||
|
|
||||||
return __("Installation started for server [:server]\nThis may take several minutes depending on many things like your server's internet speed.\nAs soon as it finishes, We will notify you through this channel.\nYou can check the progress live on your dashboard.\n:progress", [
|
return __("Installation started for server [:server]\nThis may take several minutes depending on many things like your server's internet speed.\nAs soon as it finishes, We will notify you through this channel.\nYou can check the progress live on your dashboard.\n:progress", [
|
||||||
'server' => $this->server->name,
|
'server' => $this->server->name,
|
||||||
'progress' => url('/servers/'.$this->server->id),
|
'progress' => url('/servers/'.$this->server->id),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function mail(): MailMessage
|
public function toMail(object $notifiable): MailMessage
|
||||||
{
|
{
|
||||||
return (new MailMessage)
|
return (new MailMessage)
|
||||||
|
->subject(__('Server installation started!'))
|
||||||
->line('Your server\'s ['.$this->server->name.'] installation has been started.')
|
->line('Your server\'s ['.$this->server->name.'] installation has been started.')
|
||||||
->line("This may take several minutes depending on many things like your server's internet speed.")
|
->line("This may take several minutes depending on many things like your server's internet speed.")
|
||||||
->line('As soon as it finishes, We will notify you through this channel.')
|
->line('As soon as it finishes, We will notify you through this channel.')
|
||||||
|
@ -2,11 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Notifications;
|
namespace App\Notifications;
|
||||||
|
|
||||||
use App\Contracts\Notification;
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
|
||||||
class ServerInstallationSucceed implements Notification
|
class ServerInstallationSucceed extends AbstractNotification
|
||||||
{
|
{
|
||||||
protected Server $server;
|
protected Server $server;
|
||||||
|
|
||||||
@ -20,14 +19,10 @@ public function subject(): string
|
|||||||
return __('Server installation succeed!');
|
return __('Server installation succeed!');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function message(bool $mail = false): mixed
|
public function rawText(): string
|
||||||
{
|
{
|
||||||
$this->server->refresh();
|
$this->server->refresh();
|
||||||
|
|
||||||
if ($mail) {
|
|
||||||
return $this->mail();
|
|
||||||
}
|
|
||||||
|
|
||||||
return __("Installation succeed for server [:server] \nServer IP: :ip \nUser: :user\nPassword: :password\n:link", [
|
return __("Installation succeed for server [:server] \nServer IP: :ip \nUser: :user\nPassword: :password\n:link", [
|
||||||
'server' => $this->server->name,
|
'server' => $this->server->name,
|
||||||
'ip' => $this->server->ip,
|
'ip' => $this->server->ip,
|
||||||
@ -37,11 +32,12 @@ public function message(bool $mail = false): mixed
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function mail(): MailMessage
|
public function toMail(object $notifiable): MailMessage
|
||||||
{
|
{
|
||||||
$this->server->refresh();
|
$this->server->refresh();
|
||||||
|
|
||||||
return (new MailMessage)
|
return (new MailMessage)
|
||||||
|
->subject(__('Server installation succeed!'))
|
||||||
->line('Your server ['.$this->server->name.'] has been installed successfully.')
|
->line('Your server ['.$this->server->name.'] has been installed successfully.')
|
||||||
->line('Server IP: '.$this->server->ip)
|
->line('Server IP: '.$this->server->ip)
|
||||||
->line('User: '.$this->server->authentication['user'])
|
->line('User: '.$this->server->authentication['user'])
|
||||||
|
30
app/Notifications/SiteInstallationFailed.php
Normal file
30
app/Notifications/SiteInstallationFailed.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Notifications;
|
||||||
|
|
||||||
|
use App\Models\Site;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
|
||||||
|
class SiteInstallationFailed extends AbstractNotification
|
||||||
|
{
|
||||||
|
public function __construct(protected Site $site)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rawText(): string
|
||||||
|
{
|
||||||
|
return __("Installation failed for site [:site] \nCheck your server's logs \n:logs", [
|
||||||
|
'site' => $this->site->domain,
|
||||||
|
'logs' => url('/servers/'.$this->site->server_id.'/logs'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toMail(object $notifiable): MailMessage
|
||||||
|
{
|
||||||
|
return (new MailMessage)
|
||||||
|
->subject(__('Site installation failed!'))
|
||||||
|
->line('Your site\'s ['.$this->site->domain.'] installation has been failed.')
|
||||||
|
->line('Check your server logs')
|
||||||
|
->action('View Logs', url('/servers/'.$this->site->server_id.'/logs'));
|
||||||
|
}
|
||||||
|
}
|
29
app/Notifications/SiteInstallationSucceed.php
Normal file
29
app/Notifications/SiteInstallationSucceed.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Notifications;
|
||||||
|
|
||||||
|
use App\Models\Site;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
|
||||||
|
class SiteInstallationSucceed extends AbstractNotification
|
||||||
|
{
|
||||||
|
public function __construct(protected Site $site)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rawText(): string
|
||||||
|
{
|
||||||
|
return __('Installation succeed for site [:site]', [
|
||||||
|
'site' => $this->site->domain,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toMail(object $notifiable): MailMessage
|
||||||
|
{
|
||||||
|
return (new MailMessage)
|
||||||
|
->subject(__('Site installation succeed!'))
|
||||||
|
->line('Your site\'s ['.$this->site->domain.'] installation has been installed.')
|
||||||
|
->line('Check your site')
|
||||||
|
->action('View Site', url('/servers/'.$this->site->server_id.'/sites/'.$this->site->id));
|
||||||
|
}
|
||||||
|
}
|
@ -2,41 +2,26 @@
|
|||||||
|
|
||||||
namespace App\Notifications;
|
namespace App\Notifications;
|
||||||
|
|
||||||
use Illuminate\Bus\Queueable;
|
use App\Models\SourceControl;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
use Illuminate\Notifications\Notification;
|
|
||||||
|
|
||||||
class SourceControlDisconnected extends Notification implements ShouldQueue
|
class SourceControlDisconnected extends AbstractNotification
|
||||||
{
|
{
|
||||||
use Queueable;
|
public function __construct(protected SourceControl $sourceControl)
|
||||||
|
|
||||||
protected string $sourceControl;
|
|
||||||
|
|
||||||
public function __construct(string $sourceControl)
|
|
||||||
{
|
{
|
||||||
$this->sourceControl = $sourceControl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function via(): array
|
public function rawText(): string
|
||||||
{
|
{
|
||||||
return ['mail'];
|
return __('Source control [:sourceControl] has been disconnected from Vito', [
|
||||||
|
'sourceControl' => $this->sourceControl->profile,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toMail(): MailMessage
|
public function toMail(object $notifiable): MailMessage
|
||||||
{
|
{
|
||||||
return (new MailMessage)
|
return (new MailMessage)
|
||||||
->subject('Lost connection to your '.$this->sourceControl)
|
->subject(__('Source control disconnected!'))
|
||||||
->line("We've lost connection to your $this->sourceControl account.")
|
->line($this->rawText());
|
||||||
->line("We'll not able to do any deployments until you reconnect.")
|
|
||||||
->line("To reconnect your $this->sourceControl account please click on the bellow button.")
|
|
||||||
->action('Reconnect', url('/source-controls'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function toArray(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
//
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use App\Helpers\Notifier;
|
||||||
use App\Helpers\SSH;
|
use App\Helpers\SSH;
|
||||||
use App\Support\SocialiteProviders\DropboxProvider;
|
use App\Support\SocialiteProviders\DropboxProvider;
|
||||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||||
@ -29,6 +30,9 @@ public function boot(): void
|
|||||||
$this->app->bind('ssh', function () {
|
$this->app->bind('ssh', function () {
|
||||||
return new SSH;
|
return new SSH;
|
||||||
});
|
});
|
||||||
|
$this->app->bind('notifier', function () {
|
||||||
|
return new Notifier;
|
||||||
|
});
|
||||||
|
|
||||||
$this->extendSocialite();
|
$this->extendSocialite();
|
||||||
}
|
}
|
||||||
|
26
app/SSHCommands/Nginx/GetNginxVHostCommand.php
Executable file
26
app/SSHCommands/Nginx/GetNginxVHostCommand.php
Executable file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\SSHCommands\Nginx;
|
||||||
|
|
||||||
|
use App\SSHCommands\Command;
|
||||||
|
use Illuminate\Support\Facades\File;
|
||||||
|
|
||||||
|
class GetNginxVHostCommand extends Command
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected string $domain
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function file(): string
|
||||||
|
{
|
||||||
|
return File::get(resource_path('commands/webserver/nginx/get-vhost.sh'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function content(): string
|
||||||
|
{
|
||||||
|
return str($this->file())
|
||||||
|
->replace('__domain__', $this->domain)
|
||||||
|
->toString();
|
||||||
|
}
|
||||||
|
}
|
@ -4,10 +4,14 @@
|
|||||||
|
|
||||||
use App\Enums\OperatingSystem;
|
use App\Enums\OperatingSystem;
|
||||||
use App\Exceptions\CouldNotConnectToProvider;
|
use App\Exceptions\CouldNotConnectToProvider;
|
||||||
|
use App\Facades\Notifier;
|
||||||
|
use App\Notifications\FailedToDeleteServerFromProvider;
|
||||||
use Aws\Ec2\Ec2Client;
|
use Aws\Ec2\Ec2Client;
|
||||||
use Aws\EC2InstanceConnect\EC2InstanceConnectClient;
|
use Aws\EC2InstanceConnect\EC2InstanceConnectClient;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use Illuminate\Filesystem\FilesystemAdapter;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
class AWS extends AbstractProvider
|
class AWS extends AbstractProvider
|
||||||
{
|
{
|
||||||
@ -125,9 +129,8 @@ public function delete(): void
|
|||||||
$this->ec2Client->terminateInstances([
|
$this->ec2Client->terminateInstances([
|
||||||
'InstanceIds' => [$this->server->provider_data['instance_id']],
|
'InstanceIds' => [$this->server->provider_data['instance_id']],
|
||||||
]);
|
]);
|
||||||
} catch (Exception) {
|
} catch (Throwable) {
|
||||||
/** @todo notify */
|
Notifier::send($this->server, new FailedToDeleteServerFromProvider($this->server));
|
||||||
// $this->server->team->notify(new FailedToDeleteServerFromProvider($this->server));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,10 +167,12 @@ private function createKeyPair(): void
|
|||||||
$result = $this->ec2Client->createKeyPair([
|
$result = $this->ec2Client->createKeyPair([
|
||||||
'KeyName' => $keyName,
|
'KeyName' => $keyName,
|
||||||
]);
|
]);
|
||||||
Storage::disk(config('core.key_pairs_disk'))->put((string) $this->server->id, $result['KeyMaterial']);
|
/** @var FilesystemAdapter $storageDisk */
|
||||||
|
$storageDisk = Storage::disk(config('core.key_pairs_disk'));
|
||||||
|
$storageDisk->put((string) $this->server->id, $result['KeyMaterial']);
|
||||||
generate_public_key(
|
generate_public_key(
|
||||||
Storage::disk(config('core.key_pairs_disk'))->path((string) $this->server->id),
|
$storageDisk->path((string) $this->server->id),
|
||||||
Storage::disk(config('core.key_pairs_disk'))->path($this->server->id.'.pub'),
|
$storageDisk->path($this->server->id.'.pub'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,8 @@ public function __construct(?Server $server = null)
|
|||||||
|
|
||||||
protected function generateKeyPair(): void
|
protected function generateKeyPair(): void
|
||||||
{
|
{
|
||||||
generate_key_pair(Storage::disk(config('core.key_pairs_disk'))->path((string) $this->server->id));
|
/** @var \Illuminate\Filesystem\FilesystemAdapter $storageDisk */
|
||||||
|
$storageDisk = Storage::disk(config('core.key_pairs_disk'));
|
||||||
|
generate_key_pair($storageDisk->path((string) $this->server->id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,13 +59,15 @@ public function regions(): array
|
|||||||
|
|
||||||
public function create(): void
|
public function create(): void
|
||||||
{
|
{
|
||||||
|
/** @var \Illuminate\Filesystem\FilesystemAdapter $storageDisk */
|
||||||
|
$storageDisk = Storage::disk(config('core.key_pairs_disk'));
|
||||||
File::copy(
|
File::copy(
|
||||||
storage_path(config('core.ssh_private_key_name')),
|
storage_path(config('core.ssh_private_key_name')),
|
||||||
Storage::disk(config('core.key_pairs_disk'))->path($this->server->id)
|
$storageDisk->path($this->server->id)
|
||||||
);
|
);
|
||||||
File::copy(
|
File::copy(
|
||||||
storage_path(config('core.ssh_public_key_name')),
|
storage_path(config('core.ssh_public_key_name')),
|
||||||
Storage::disk(config('core.key_pairs_disk'))->path($this->server->id.'.pub')
|
$storageDisk->path($this->server->id.'.pub')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
use App\Exceptions\CouldNotConnectToProvider;
|
use App\Exceptions\CouldNotConnectToProvider;
|
||||||
use App\Exceptions\ServerProviderError;
|
use App\Exceptions\ServerProviderError;
|
||||||
|
use App\Facades\Notifier;
|
||||||
|
use App\Notifications\FailedToDeleteServerFromProvider;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
@ -148,10 +150,9 @@ public function delete(): void
|
|||||||
$delete = Http::withToken($this->server->serverProvider->credentials['token'])
|
$delete = Http::withToken($this->server->serverProvider->credentials['token'])
|
||||||
->delete($this->apiUrl.'/droplets/'.$this->server->provider_data['droplet_id']);
|
->delete($this->apiUrl.'/droplets/'.$this->server->provider_data['droplet_id']);
|
||||||
|
|
||||||
/** @todo notify */
|
if (! $delete->ok()) {
|
||||||
// if (! $delete->ok()) {
|
Notifier::send($this->server, new FailedToDeleteServerFromProvider($this->server));
|
||||||
// $this->server->team->notify(new FailedToDeleteServerFromProvider($this->server));
|
}
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
use App\Exceptions\CouldNotConnectToProvider;
|
use App\Exceptions\CouldNotConnectToProvider;
|
||||||
use App\Exceptions\ServerProviderError;
|
use App\Exceptions\ServerProviderError;
|
||||||
|
use App\Facades\Notifier;
|
||||||
|
use App\Notifications\FailedToDeleteServerFromProvider;
|
||||||
use Illuminate\Http\Client\Response;
|
use Illuminate\Http\Client\Response;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
@ -122,10 +124,9 @@ public function delete(): void
|
|||||||
$delete = Http::withToken($this->server->serverProvider->credentials['token'])
|
$delete = Http::withToken($this->server->serverProvider->credentials['token'])
|
||||||
->delete($this->apiUrl.'/servers/'.$this->server->provider_data['hetzner_id']);
|
->delete($this->apiUrl.'/servers/'.$this->server->provider_data['hetzner_id']);
|
||||||
|
|
||||||
/** @todo notify */
|
if (! $delete->ok()) {
|
||||||
// if (! $delete->ok()) {
|
Notifier::send($this->server, new FailedToDeleteServerFromProvider($this->server));
|
||||||
// $this->server->team->notify(new FailedToDeleteServerFromProvider($this->server));
|
}
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete key
|
// delete key
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
use App\Exceptions\CouldNotConnectToProvider;
|
use App\Exceptions\CouldNotConnectToProvider;
|
||||||
use App\Exceptions\ServerProviderError;
|
use App\Exceptions\ServerProviderError;
|
||||||
|
use App\Facades\Notifier;
|
||||||
|
use App\Notifications\FailedToDeleteServerFromProvider;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
@ -131,10 +133,9 @@ public function delete(): void
|
|||||||
$delete = Http::withToken($this->server->serverProvider->credentials['token'])
|
$delete = Http::withToken($this->server->serverProvider->credentials['token'])
|
||||||
->delete($this->apiUrl.'/linode/instances/'.$this->server->provider_data['linode_id']);
|
->delete($this->apiUrl.'/linode/instances/'.$this->server->provider_data['linode_id']);
|
||||||
|
|
||||||
/** @todo notify */
|
if (! $delete->ok()) {
|
||||||
// if (! $delete->ok()) {
|
Notifier::send($this->server, new FailedToDeleteServerFromProvider($this->server));
|
||||||
// $this->server->team->notify(new FailedToDeleteServerFromProvider($this->server));
|
}
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
use App\Exceptions\CouldNotConnectToProvider;
|
use App\Exceptions\CouldNotConnectToProvider;
|
||||||
use App\Exceptions\ServerProviderError;
|
use App\Exceptions\ServerProviderError;
|
||||||
|
use App\Facades\Notifier;
|
||||||
|
use App\Notifications\FailedToDeleteServerFromProvider;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
@ -85,7 +87,9 @@ public function regions(): array
|
|||||||
public function create(): void
|
public function create(): void
|
||||||
{
|
{
|
||||||
// generate key pair
|
// generate key pair
|
||||||
generate_key_pair(Storage::disk(config('core.key_pairs_disk'))->path((string) $this->server->id));
|
/** @var \Illuminate\Filesystem\FilesystemAdapter $storageDisk */
|
||||||
|
$storageDisk = Storage::disk(config('core.key_pairs_disk'));
|
||||||
|
generate_key_pair($storageDisk->path((string) $this->server->id));
|
||||||
|
|
||||||
$createSshKey = Http::withToken($this->server->serverProvider->credentials['token'])
|
$createSshKey = Http::withToken($this->server->serverProvider->credentials['token'])
|
||||||
->post($this->apiUrl.'/ssh-keys', [
|
->post($this->apiUrl.'/ssh-keys', [
|
||||||
@ -142,10 +146,9 @@ public function delete(): void
|
|||||||
$delete = Http::withToken($this->server->serverProvider->credentials['token'])
|
$delete = Http::withToken($this->server->serverProvider->credentials['token'])
|
||||||
->delete($this->apiUrl.'/instances/'.$this->server->provider_data['instance_id']);
|
->delete($this->apiUrl.'/instances/'.$this->server->provider_data['instance_id']);
|
||||||
|
|
||||||
/** @todo notify */
|
if (! $delete->ok()) {
|
||||||
// if (! $delete->ok()) {
|
Notifier::send($this->server, new FailedToDeleteServerFromProvider($this->server));
|
||||||
// $this->server->team->notify(new FailedToDeleteServerFromProvider($this->server));
|
}
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,12 @@
|
|||||||
namespace App\ServerTypes;
|
namespace App\ServerTypes;
|
||||||
|
|
||||||
use App\Events\Broadcast;
|
use App\Events\Broadcast;
|
||||||
|
use App\Facades\Notifier;
|
||||||
use App\Jobs\Installation\Initialize;
|
use App\Jobs\Installation\Initialize;
|
||||||
use App\Jobs\Installation\InstallRequirements;
|
use App\Jobs\Installation\InstallRequirements;
|
||||||
use App\Jobs\Installation\Upgrade;
|
use App\Jobs\Installation\Upgrade;
|
||||||
|
use App\Notifications\ServerInstallationFailed;
|
||||||
|
use App\Notifications\ServerInstallationSucceed;
|
||||||
use Illuminate\Support\Facades\Bus;
|
use Illuminate\Support\Facades\Bus;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
@ -66,7 +69,7 @@ public function install(): void
|
|||||||
'server' => $this->server,
|
'server' => $this->server,
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
/** @todo notify */
|
Notifier::send($this->server, new ServerInstallationSucceed($this->server));
|
||||||
};
|
};
|
||||||
|
|
||||||
Bus::chain($jobs)
|
Bus::chain($jobs)
|
||||||
@ -79,7 +82,7 @@ public function install(): void
|
|||||||
'server' => $this->server,
|
'server' => $this->server,
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
/** @todo notify */
|
Notifier::send($this->server, new ServerInstallationFailed($this->server));
|
||||||
Log::error('server-installation-error', [
|
Log::error('server-installation-error', [
|
||||||
'error' => (string) $e,
|
'error' => (string) $e,
|
||||||
]);
|
]);
|
||||||
|
@ -4,12 +4,15 @@
|
|||||||
|
|
||||||
use App\Enums\ServerStatus;
|
use App\Enums\ServerStatus;
|
||||||
use App\Events\Broadcast;
|
use App\Events\Broadcast;
|
||||||
|
use App\Facades\Notifier;
|
||||||
use App\Jobs\Installation\Initialize;
|
use App\Jobs\Installation\Initialize;
|
||||||
use App\Jobs\Installation\InstallCertbot;
|
use App\Jobs\Installation\InstallCertbot;
|
||||||
use App\Jobs\Installation\InstallComposer;
|
use App\Jobs\Installation\InstallComposer;
|
||||||
use App\Jobs\Installation\InstallNodejs;
|
use App\Jobs\Installation\InstallNodejs;
|
||||||
use App\Jobs\Installation\InstallRequirements;
|
use App\Jobs\Installation\InstallRequirements;
|
||||||
use App\Jobs\Installation\Upgrade;
|
use App\Jobs\Installation\Upgrade;
|
||||||
|
use App\Notifications\ServerInstallationFailed;
|
||||||
|
use App\Notifications\ServerInstallationSucceed;
|
||||||
use Illuminate\Support\Facades\Bus;
|
use Illuminate\Support\Facades\Bus;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
@ -88,7 +91,7 @@ public function install(): void
|
|||||||
'server' => $this->server,
|
'server' => $this->server,
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
/** @todo notify */
|
Notifier::send($this->server, new ServerInstallationSucceed($this->server));
|
||||||
};
|
};
|
||||||
|
|
||||||
Bus::chain($jobs)
|
Bus::chain($jobs)
|
||||||
@ -101,7 +104,7 @@ public function install(): void
|
|||||||
'server' => $this->server,
|
'server' => $this->server,
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
/** @todo notify */
|
Notifier::send($this->server, new ServerInstallationFailed($this->server));
|
||||||
Log::error('server-installation-error', [
|
Log::error('server-installation-error', [
|
||||||
'error' => (string) $e,
|
'error' => (string) $e,
|
||||||
]);
|
]);
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
use App\SSHCommands\Nginx\ChangeNginxPHPVersionCommand;
|
use App\SSHCommands\Nginx\ChangeNginxPHPVersionCommand;
|
||||||
use App\SSHCommands\Nginx\CreateNginxVHostCommand;
|
use App\SSHCommands\Nginx\CreateNginxVHostCommand;
|
||||||
use App\SSHCommands\Nginx\DeleteNginxSiteCommand;
|
use App\SSHCommands\Nginx\DeleteNginxSiteCommand;
|
||||||
|
use App\SSHCommands\Nginx\GetNginxVHostCommand;
|
||||||
use App\SSHCommands\Nginx\UpdateNginxRedirectsCommand;
|
use App\SSHCommands\Nginx\UpdateNginxRedirectsCommand;
|
||||||
use App\SSHCommands\Nginx\UpdateNginxVHostCommand;
|
use App\SSHCommands\Nginx\UpdateNginxVHostCommand;
|
||||||
use App\SSHCommands\SSL\CreateCustomSSLCommand;
|
use App\SSHCommands\SSL\CreateCustomSSLCommand;
|
||||||
@ -39,19 +40,30 @@ public function createVHost(Site $site): void
|
|||||||
/**
|
/**
|
||||||
* @throws Throwable
|
* @throws Throwable
|
||||||
*/
|
*/
|
||||||
public function updateVHost(Site $site, bool $noSSL = false): void
|
public function updateVHost(Site $site, bool $noSSL = false, ?string $vhost = null): void
|
||||||
{
|
{
|
||||||
$this->service->server->ssh()->exec(
|
$this->service->server->ssh()->exec(
|
||||||
new UpdateNginxVHostCommand(
|
new UpdateNginxVHostCommand(
|
||||||
$site->domain,
|
$site->domain,
|
||||||
$site->path,
|
$site->path,
|
||||||
$this->generateVhost($site, $noSSL)
|
$vhost ?? $this->generateVhost($site, $noSSL)
|
||||||
),
|
),
|
||||||
'update-vhost',
|
'update-vhost',
|
||||||
$site->id
|
$site->id
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getVHost(Site $site): string
|
||||||
|
{
|
||||||
|
return $this->service->server->ssh()->exec(
|
||||||
|
new GetNginxVHostCommand(
|
||||||
|
$site->domain
|
||||||
|
),
|
||||||
|
'get-vhost',
|
||||||
|
$site->id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Throwable
|
* @throws Throwable
|
||||||
*/
|
*/
|
||||||
|
@ -22,11 +22,6 @@ public function delete(): void
|
|||||||
dispatch(new DeleteSite($this->site))->onConnection('ssh');
|
dispatch(new DeleteSite($this->site))->onConnection('ssh');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function install(): void
|
|
||||||
{
|
|
||||||
// TODO: Implement install() method.
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function progress(int $percentage): Closure
|
protected function progress(int $percentage): Closure
|
||||||
{
|
{
|
||||||
return function () use ($percentage) {
|
return function () use ($percentage) {
|
||||||
|
67
app/SiteTypes/PHPBlank.php
Executable file
67
app/SiteTypes/PHPBlank.php
Executable file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\SiteTypes;
|
||||||
|
|
||||||
|
use App\Enums\SiteFeature;
|
||||||
|
use App\Jobs\Site\CreateVHost;
|
||||||
|
use Illuminate\Support\Facades\Bus;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class PHPBlank extends PHPSite
|
||||||
|
{
|
||||||
|
public function supportedFeatures(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
SiteFeature::DEPLOYMENT,
|
||||||
|
SiteFeature::ENV,
|
||||||
|
SiteFeature::SSL,
|
||||||
|
SiteFeature::QUEUES,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createValidationRules(array $input): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'php_version' => [
|
||||||
|
'required',
|
||||||
|
Rule::in($this->site->server->installedPHPVersions()),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createFields(array $input): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'web_directory' => $input['web_directory'] ?? '',
|
||||||
|
'php_version' => $input['php_version'] ?? '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function data(array $input): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function install(): void
|
||||||
|
{
|
||||||
|
$chain = [
|
||||||
|
new CreateVHost($this->site),
|
||||||
|
$this->progress(65),
|
||||||
|
function () {
|
||||||
|
$this->site->php()?->restart();
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
$chain[] = function () {
|
||||||
|
$this->site->installationFinished();
|
||||||
|
};
|
||||||
|
|
||||||
|
Bus::chain($chain)
|
||||||
|
->catch(function (Throwable $e) {
|
||||||
|
$this->site->installationFailed($e);
|
||||||
|
})
|
||||||
|
->onConnection('ssh-long')
|
||||||
|
->dispatch();
|
||||||
|
}
|
||||||
|
}
|
@ -55,6 +55,7 @@ public function createFields(array $input): array
|
|||||||
'source_control_id' => $input['source_control'] ?? '',
|
'source_control_id' => $input['source_control'] ?? '',
|
||||||
'repository' => $input['repository'] ?? '',
|
'repository' => $input['repository'] ?? '',
|
||||||
'branch' => $input['branch'] ?? '',
|
'branch' => $input['branch'] ?? '',
|
||||||
|
'php_version' => $input['php_version'] ?? '',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +63,6 @@ public function data(array $input): array
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'composer' => isset($input['composer']) && $input['composer'],
|
'composer' => isset($input['composer']) && $input['composer'],
|
||||||
'php_version' => $input['php_version'] ?? '',
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Contracts\Database\Query\Expression;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
function random_color(): string
|
function random_color(): string
|
||||||
@ -63,3 +64,10 @@ function date_with_timezone($date, $timezone): string
|
|||||||
|
|
||||||
return $dt->format('Y-m-d H:i:s');
|
return $dt->format('Y-m-d H:i:s');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cast_to_json(array $json): Illuminate\Database\Query\Expression|Expression
|
||||||
|
{
|
||||||
|
$json = addslashes(json_encode($json));
|
||||||
|
|
||||||
|
return DB::raw("CAST('{$json}' AS JSON)");
|
||||||
|
}
|
||||||
|
@ -6,18 +6,22 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.1",
|
"php": "^8.1",
|
||||||
|
"ext-ftp": "*",
|
||||||
"aws/aws-sdk-php": "^3.158",
|
"aws/aws-sdk-php": "^3.158",
|
||||||
"bensampo/laravel-enum": "^6.3",
|
"bensampo/laravel-enum": "^6.3",
|
||||||
|
"blade-ui-kit/blade-heroicons": "^2.2",
|
||||||
|
"davidhsianturi/blade-bootstrap-icons": "^1.4",
|
||||||
"guzzlehttp/guzzle": "^7.2",
|
"guzzlehttp/guzzle": "^7.2",
|
||||||
|
"khatabwedaa/blade-css-icons": "^1.3",
|
||||||
"laravel/fortify": "^1.17",
|
"laravel/fortify": "^1.17",
|
||||||
"laravel/framework": "^10.0",
|
"laravel/framework": "^10.0",
|
||||||
"laravel/sanctum": "^3.2",
|
"laravel/sanctum": "^3.2",
|
||||||
"laravel/socialite": "^5.2",
|
"laravel/socialite": "^5.2",
|
||||||
"laravel/tinker": "^2.8",
|
"laravel/tinker": "^2.8",
|
||||||
"livewire/livewire": "^2.12",
|
"livewire/livewire": "^2.12",
|
||||||
"phpseclib/phpseclib": "~3.0",
|
|
||||||
"opcodesio/log-viewer": "^2.5",
|
"opcodesio/log-viewer": "^2.5",
|
||||||
"ext-ftp": "*"
|
"owenvoke/blade-fontawesome": "^2.5",
|
||||||
|
"phpseclib/phpseclib": "~3.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"fakerphp/faker": "^1.9.1",
|
"fakerphp/faker": "^1.9.1",
|
||||||
|
475
composer.lock
generated
475
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "bffd27708153bfce7c9dc34b975700de",
|
"content-hash": "b3f98cafe7fcc5d3ce67ad09a8f66661",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "aws/aws-crt-php",
|
"name": "aws/aws-crt-php",
|
||||||
@ -300,6 +300,156 @@
|
|||||||
],
|
],
|
||||||
"time": "2023-11-15T15:39:24+00:00"
|
"time": "2023-11-15T15:39:24+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "blade-ui-kit/blade-heroicons",
|
||||||
|
"version": "2.2.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/blade-ui-kit/blade-heroicons.git",
|
||||||
|
"reference": "bcf4be8f6bbde0bb4c23f2e3fb189b88dec1580a"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/blade-ui-kit/blade-heroicons/zipball/bcf4be8f6bbde0bb4c23f2e3fb189b88dec1580a",
|
||||||
|
"reference": "bcf4be8f6bbde0bb4c23f2e3fb189b88dec1580a",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"blade-ui-kit/blade-icons": "^1.1",
|
||||||
|
"illuminate/support": "^9.0|^10.0",
|
||||||
|
"php": "^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"orchestra/testbench": "^7.0|^8.0",
|
||||||
|
"phpunit/phpunit": "^9.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"BladeUI\\Heroicons\\BladeHeroiconsServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"BladeUI\\Heroicons\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Dries Vints",
|
||||||
|
"homepage": "https://driesvints.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A package to easily make use of Heroicons in your Laravel Blade views.",
|
||||||
|
"homepage": "https://github.com/blade-ui-kit/blade-heroicons",
|
||||||
|
"keywords": [
|
||||||
|
"Heroicons",
|
||||||
|
"blade",
|
||||||
|
"laravel"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/blade-ui-kit/blade-heroicons/issues",
|
||||||
|
"source": "https://github.com/blade-ui-kit/blade-heroicons/tree/2.2.1"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/sponsors/driesvints",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://www.paypal.com/paypalme/driesvints",
|
||||||
|
"type": "paypal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2023-12-18T20:44:03+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "blade-ui-kit/blade-icons",
|
||||||
|
"version": "1.5.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/blade-ui-kit/blade-icons.git",
|
||||||
|
"reference": "b5e6603218e2347ac81cb780bc6f71c8c3b31f5b"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/blade-ui-kit/blade-icons/zipball/b5e6603218e2347ac81cb780bc6f71c8c3b31f5b",
|
||||||
|
"reference": "b5e6603218e2347ac81cb780bc6f71c8c3b31f5b",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"illuminate/contracts": "^8.0|^9.0|^10.0",
|
||||||
|
"illuminate/filesystem": "^8.0|^9.0|^10.0",
|
||||||
|
"illuminate/support": "^8.0|^9.0|^10.0",
|
||||||
|
"illuminate/view": "^8.0|^9.0|^10.0",
|
||||||
|
"php": "^7.4|^8.0",
|
||||||
|
"symfony/console": "^5.3|^6.0",
|
||||||
|
"symfony/finder": "^5.3|^6.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"mockery/mockery": "^1.3",
|
||||||
|
"orchestra/testbench": "^6.0|^7.0|^8.0",
|
||||||
|
"phpunit/phpunit": "^9.0"
|
||||||
|
},
|
||||||
|
"bin": [
|
||||||
|
"bin/blade-icons-generate"
|
||||||
|
],
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"BladeUI\\Icons\\BladeIconsServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"src/helpers.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"BladeUI\\Icons\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Dries Vints",
|
||||||
|
"homepage": "https://driesvints.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A package to easily make use of icons in your Laravel Blade views.",
|
||||||
|
"homepage": "https://github.com/blade-ui-kit/blade-icons",
|
||||||
|
"keywords": [
|
||||||
|
"blade",
|
||||||
|
"icons",
|
||||||
|
"laravel",
|
||||||
|
"svg"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/blade-ui-kit/blade-icons/issues",
|
||||||
|
"source": "https://github.com/blade-ui-kit/blade-icons"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/sponsors/driesvints",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://www.paypal.com/paypalme/driesvints",
|
||||||
|
"type": "paypal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2023-10-18T10:50:13+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "brick/math",
|
"name": "brick/math",
|
||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
@ -618,6 +768,67 @@
|
|||||||
},
|
},
|
||||||
"time": "2023-08-25T16:18:39+00:00"
|
"time": "2023-08-25T16:18:39+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "davidhsianturi/blade-bootstrap-icons",
|
||||||
|
"version": "v1.4.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/davidhsianturi/blade-bootstrap-icons.git",
|
||||||
|
"reference": "255040a0058683dd5a0fd36dfa0857a91a95137f"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/davidhsianturi/blade-bootstrap-icons/zipball/255040a0058683dd5a0fd36dfa0857a91a95137f",
|
||||||
|
"reference": "255040a0058683dd5a0fd36dfa0857a91a95137f",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"blade-ui-kit/blade-icons": "^1.0",
|
||||||
|
"illuminate/support": "^8.0|^9.0|^10.0",
|
||||||
|
"php": "^7.4|^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"orchestra/testbench": "^6.0|^7.0|^8.0",
|
||||||
|
"phpunit/phpunit": "^9.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"Davidhsianturi\\BladeBootstrapIcons\\BladeBootstrapIconsServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Davidhsianturi\\BladeBootstrapIcons\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "David H. Sianturi",
|
||||||
|
"email": "davidhsianturi@gmail.com",
|
||||||
|
"homepage": "https://davidhsianturi.com",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A package to easily make use of Bootstrap Icons in your Laravel Blade views.",
|
||||||
|
"homepage": "https://github.com/davidhsianturi/blade-bootstrap-icons",
|
||||||
|
"keywords": [
|
||||||
|
"Bootstrap Icons",
|
||||||
|
"blade",
|
||||||
|
"laravel"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/davidhsianturi/blade-bootstrap-icons/issues",
|
||||||
|
"source": "https://github.com/davidhsianturi/blade-bootstrap-icons/tree/v1.4.0"
|
||||||
|
},
|
||||||
|
"time": "2023-03-17T14:49:47+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "dflydev/dot-access-data",
|
"name": "dflydev/dot-access-data",
|
||||||
"version": "v3.0.2",
|
"version": "v3.0.2",
|
||||||
@ -1533,6 +1744,67 @@
|
|||||||
],
|
],
|
||||||
"time": "2023-12-03T19:50:20+00:00"
|
"time": "2023-12-03T19:50:20+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "khatabwedaa/blade-css-icons",
|
||||||
|
"version": "1.3.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/khatabwedaa/blade-css-icons.git",
|
||||||
|
"reference": "a022e9a0057d9ce4f99728647fb139808c6134d8"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/khatabwedaa/blade-css-icons/zipball/a022e9a0057d9ce4f99728647fb139808c6134d8",
|
||||||
|
"reference": "a022e9a0057d9ce4f99728647fb139808c6134d8",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"blade-ui-kit/blade-icons": "^1.1",
|
||||||
|
"illuminate/support": "^9.0|^10",
|
||||||
|
"php": "^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"orchestra/testbench": "^7.0|^8.0",
|
||||||
|
"phpunit/phpunit": "^9.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"Khatabwedaa\\BladeCssIcons\\BladeCssIconsServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Khatabwedaa\\BladeCssIcons\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Khatab Wedaa",
|
||||||
|
"email": "khatabwedaa@gmail.com",
|
||||||
|
"homepage": "https://twitter.com/khatabwedaa",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A package to easily make use of Css.gg in your Laravel Blade views.",
|
||||||
|
"homepage": "https://github.com/khatabwedaa/blade-css-icons",
|
||||||
|
"keywords": [
|
||||||
|
"Css.gg",
|
||||||
|
"blade",
|
||||||
|
"laravel"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/khatabwedaa/blade-css-icons/issues",
|
||||||
|
"source": "https://github.com/khatabwedaa/blade-css-icons/tree/1.3.0"
|
||||||
|
},
|
||||||
|
"time": "2023-02-04T14:04:11+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "laminas/laminas-code",
|
"name": "laminas/laminas-code",
|
||||||
"version": "4.13.0",
|
"version": "4.13.0",
|
||||||
@ -3382,6 +3654,68 @@
|
|||||||
],
|
],
|
||||||
"time": "2023-09-03T08:22:57+00:00"
|
"time": "2023-09-03T08:22:57+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "owenvoke/blade-fontawesome",
|
||||||
|
"version": "v2.5.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/owenvoke/blade-fontawesome.git",
|
||||||
|
"reference": "b3eac80b0f2f1b70083d4acea0da49350f88856e"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/owenvoke/blade-fontawesome/zipball/b3eac80b0f2f1b70083d4acea0da49350f88856e",
|
||||||
|
"reference": "b3eac80b0f2f1b70083d4acea0da49350f88856e",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"blade-ui-kit/blade-icons": "^1.5",
|
||||||
|
"illuminate/support": "^10.34",
|
||||||
|
"php": "^8.1",
|
||||||
|
"thecodingmachine/safe": "^2.5"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"laravel/pint": "^1.13",
|
||||||
|
"orchestra/testbench": "^8.12",
|
||||||
|
"pestphp/pest": "^2.26",
|
||||||
|
"phpstan/phpstan": "^1.10",
|
||||||
|
"symfony/var-dumper": "^6.3",
|
||||||
|
"thecodingmachine/phpstan-safe-rule": "^1.2"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"laravel": {
|
||||||
|
"providers": [
|
||||||
|
"OwenVoke\\BladeFontAwesome\\BladeFontAwesomeServiceProvider"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"OwenVoke\\BladeFontAwesome\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "A package to easily make use of Font Awesome in your Laravel Blade views",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/owenvoke/blade-fontawesome/issues",
|
||||||
|
"source": "https://github.com/owenvoke/blade-fontawesome/tree/v2.5.1"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://ecologi.com/owenvoke?gift-trees",
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/owenvoke",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2023-12-12T09:07:03+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "paragonie/constant_time_encoding",
|
"name": "paragonie/constant_time_encoding",
|
||||||
"version": "v2.6.3",
|
"version": "v2.6.3",
|
||||||
@ -6710,6 +7044,145 @@
|
|||||||
],
|
],
|
||||||
"time": "2023-12-28T19:16:56+00:00"
|
"time": "2023-12-28T19:16:56+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "thecodingmachine/safe",
|
||||||
|
"version": "v2.5.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/thecodingmachine/safe.git",
|
||||||
|
"reference": "3115ecd6b4391662b4931daac4eba6b07a2ac1f0"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/thecodingmachine/safe/zipball/3115ecd6b4391662b4931daac4eba6b07a2ac1f0",
|
||||||
|
"reference": "3115ecd6b4391662b4931daac4eba6b07a2ac1f0",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpstan/phpstan": "^1.5",
|
||||||
|
"phpunit/phpunit": "^9.5",
|
||||||
|
"squizlabs/php_codesniffer": "^3.2",
|
||||||
|
"thecodingmachine/phpstan-strict-rules": "^1.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "2.2.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"deprecated/apc.php",
|
||||||
|
"deprecated/array.php",
|
||||||
|
"deprecated/datetime.php",
|
||||||
|
"deprecated/libevent.php",
|
||||||
|
"deprecated/misc.php",
|
||||||
|
"deprecated/password.php",
|
||||||
|
"deprecated/mssql.php",
|
||||||
|
"deprecated/stats.php",
|
||||||
|
"deprecated/strings.php",
|
||||||
|
"lib/special_cases.php",
|
||||||
|
"deprecated/mysqli.php",
|
||||||
|
"generated/apache.php",
|
||||||
|
"generated/apcu.php",
|
||||||
|
"generated/array.php",
|
||||||
|
"generated/bzip2.php",
|
||||||
|
"generated/calendar.php",
|
||||||
|
"generated/classobj.php",
|
||||||
|
"generated/com.php",
|
||||||
|
"generated/cubrid.php",
|
||||||
|
"generated/curl.php",
|
||||||
|
"generated/datetime.php",
|
||||||
|
"generated/dir.php",
|
||||||
|
"generated/eio.php",
|
||||||
|
"generated/errorfunc.php",
|
||||||
|
"generated/exec.php",
|
||||||
|
"generated/fileinfo.php",
|
||||||
|
"generated/filesystem.php",
|
||||||
|
"generated/filter.php",
|
||||||
|
"generated/fpm.php",
|
||||||
|
"generated/ftp.php",
|
||||||
|
"generated/funchand.php",
|
||||||
|
"generated/gettext.php",
|
||||||
|
"generated/gmp.php",
|
||||||
|
"generated/gnupg.php",
|
||||||
|
"generated/hash.php",
|
||||||
|
"generated/ibase.php",
|
||||||
|
"generated/ibmDb2.php",
|
||||||
|
"generated/iconv.php",
|
||||||
|
"generated/image.php",
|
||||||
|
"generated/imap.php",
|
||||||
|
"generated/info.php",
|
||||||
|
"generated/inotify.php",
|
||||||
|
"generated/json.php",
|
||||||
|
"generated/ldap.php",
|
||||||
|
"generated/libxml.php",
|
||||||
|
"generated/lzf.php",
|
||||||
|
"generated/mailparse.php",
|
||||||
|
"generated/mbstring.php",
|
||||||
|
"generated/misc.php",
|
||||||
|
"generated/mysql.php",
|
||||||
|
"generated/network.php",
|
||||||
|
"generated/oci8.php",
|
||||||
|
"generated/opcache.php",
|
||||||
|
"generated/openssl.php",
|
||||||
|
"generated/outcontrol.php",
|
||||||
|
"generated/pcntl.php",
|
||||||
|
"generated/pcre.php",
|
||||||
|
"generated/pgsql.php",
|
||||||
|
"generated/posix.php",
|
||||||
|
"generated/ps.php",
|
||||||
|
"generated/pspell.php",
|
||||||
|
"generated/readline.php",
|
||||||
|
"generated/rpminfo.php",
|
||||||
|
"generated/rrd.php",
|
||||||
|
"generated/sem.php",
|
||||||
|
"generated/session.php",
|
||||||
|
"generated/shmop.php",
|
||||||
|
"generated/sockets.php",
|
||||||
|
"generated/sodium.php",
|
||||||
|
"generated/solr.php",
|
||||||
|
"generated/spl.php",
|
||||||
|
"generated/sqlsrv.php",
|
||||||
|
"generated/ssdeep.php",
|
||||||
|
"generated/ssh2.php",
|
||||||
|
"generated/stream.php",
|
||||||
|
"generated/strings.php",
|
||||||
|
"generated/swoole.php",
|
||||||
|
"generated/uodbc.php",
|
||||||
|
"generated/uopz.php",
|
||||||
|
"generated/url.php",
|
||||||
|
"generated/var.php",
|
||||||
|
"generated/xdiff.php",
|
||||||
|
"generated/xml.php",
|
||||||
|
"generated/xmlrpc.php",
|
||||||
|
"generated/yaml.php",
|
||||||
|
"generated/yaz.php",
|
||||||
|
"generated/zip.php",
|
||||||
|
"generated/zlib.php"
|
||||||
|
],
|
||||||
|
"classmap": [
|
||||||
|
"lib/DateTime.php",
|
||||||
|
"lib/DateTimeImmutable.php",
|
||||||
|
"lib/Exceptions/",
|
||||||
|
"deprecated/Exceptions/",
|
||||||
|
"generated/Exceptions/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "PHP core functions that throw exceptions instead of returning FALSE on error",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/thecodingmachine/safe/issues",
|
||||||
|
"source": "https://github.com/thecodingmachine/safe/tree/v2.5.0"
|
||||||
|
},
|
||||||
|
"time": "2023-04-05T11:54:14+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "tijsverkoyen/css-to-inline-styles",
|
"name": "tijsverkoyen/css-to-inline-styles",
|
||||||
"version": "v2.2.7",
|
"version": "v2.2.7",
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
use App\NotificationChannels\Discord;
|
use App\NotificationChannels\Discord;
|
||||||
use App\NotificationChannels\Email;
|
use App\NotificationChannels\Email;
|
||||||
use App\NotificationChannels\Slack;
|
use App\NotificationChannels\Slack;
|
||||||
|
use App\NotificationChannels\Telegram;
|
||||||
use App\ServerProviders\AWS;
|
use App\ServerProviders\AWS;
|
||||||
use App\ServerProviders\DigitalOcean;
|
use App\ServerProviders\DigitalOcean;
|
||||||
use App\ServerProviders\Hetzner;
|
use App\ServerProviders\Hetzner;
|
||||||
@ -24,6 +25,7 @@
|
|||||||
use App\ServiceHandlers\ProcessManager\Supervisor;
|
use App\ServiceHandlers\ProcessManager\Supervisor;
|
||||||
use App\ServiceHandlers\Webserver\Nginx;
|
use App\ServiceHandlers\Webserver\Nginx;
|
||||||
use App\SiteTypes\Laravel;
|
use App\SiteTypes\Laravel;
|
||||||
|
use App\SiteTypes\PHPBlank;
|
||||||
use App\SiteTypes\PHPSite;
|
use App\SiteTypes\PHPSite;
|
||||||
use App\SiteTypes\Wordpress;
|
use App\SiteTypes\Wordpress;
|
||||||
use App\SourceControlProviders\Bitbucket;
|
use App\SourceControlProviders\Bitbucket;
|
||||||
@ -39,8 +41,8 @@
|
|||||||
'ssh_user' => env('SSH_USER', 'vito'),
|
'ssh_user' => env('SSH_USER', 'vito'),
|
||||||
'ssh_public_key_name' => env('SSH_PUBLIC_KEY_NAME', 'ssh-public.key'),
|
'ssh_public_key_name' => env('SSH_PUBLIC_KEY_NAME', 'ssh-public.key'),
|
||||||
'ssh_private_key_name' => env('SSH_PRIVATE_KEY_NAME', 'ssh-private.pem'),
|
'ssh_private_key_name' => env('SSH_PRIVATE_KEY_NAME', 'ssh-private.pem'),
|
||||||
'logs_disk' => env('SERVER_LOGS_DISK', 'server-logs-local'),
|
'logs_disk' => env('SERVER_LOGS_DISK', 'server-logs-local'), // should to be FilesystemAdapter storage
|
||||||
'key_pairs_disk' => env('KEY_PAIRS_DISK', 'key-pairs-local'),
|
'key_pairs_disk' => env('KEY_PAIRS_DISK', 'key-pairs-local'), // should to be FilesystemAdapter storage
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* General
|
* General
|
||||||
@ -262,11 +264,13 @@
|
|||||||
*/
|
*/
|
||||||
'site_types' => [
|
'site_types' => [
|
||||||
\App\Enums\SiteType::PHP,
|
\App\Enums\SiteType::PHP,
|
||||||
|
\App\Enums\SiteType::PHP_BLANK,
|
||||||
\App\Enums\SiteType::LARAVEL,
|
\App\Enums\SiteType::LARAVEL,
|
||||||
\App\Enums\SiteType::WORDPRESS,
|
\App\Enums\SiteType::WORDPRESS,
|
||||||
],
|
],
|
||||||
'site_types_class' => [
|
'site_types_class' => [
|
||||||
\App\Enums\SiteType::PHP => PHPSite::class,
|
\App\Enums\SiteType::PHP => PHPSite::class,
|
||||||
|
\App\Enums\SiteType::PHP_BLANK => PHPBlank::class,
|
||||||
\App\Enums\SiteType::LARAVEL => Laravel::class,
|
\App\Enums\SiteType::LARAVEL => Laravel::class,
|
||||||
\App\Enums\SiteType::WORDPRESS => Wordpress::class,
|
\App\Enums\SiteType::WORDPRESS => Wordpress::class,
|
||||||
],
|
],
|
||||||
@ -343,11 +347,13 @@
|
|||||||
\App\Enums\NotificationChannel::SLACK,
|
\App\Enums\NotificationChannel::SLACK,
|
||||||
\App\Enums\NotificationChannel::DISCORD,
|
\App\Enums\NotificationChannel::DISCORD,
|
||||||
\App\Enums\NotificationChannel::EMAIL,
|
\App\Enums\NotificationChannel::EMAIL,
|
||||||
|
\App\Enums\NotificationChannel::TELEGRAM,
|
||||||
],
|
],
|
||||||
'notification_channels_providers_class' => [
|
'notification_channels_providers_class' => [
|
||||||
\App\Enums\NotificationChannel::SLACK => Slack::class,
|
\App\Enums\NotificationChannel::SLACK => Slack::class,
|
||||||
\App\Enums\NotificationChannel::DISCORD => Discord::class,
|
\App\Enums\NotificationChannel::DISCORD => Discord::class,
|
||||||
\App\Enums\NotificationChannel::EMAIL => Email::class,
|
\App\Enums\NotificationChannel::EMAIL => Email::class,
|
||||||
|
\App\Enums\NotificationChannel::TELEGRAM => Telegram::class,
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
'disks' => [
|
'disks' => [
|
||||||
|
|
||||||
|
// should be FilesystemAdapter
|
||||||
'local' => [
|
'local' => [
|
||||||
'driver' => 'local',
|
'driver' => 'local',
|
||||||
'root' => storage_path('app'),
|
'root' => storage_path('app'),
|
||||||
|
25
database/factories/ProjectFactory.php
Normal file
25
database/factories/ProjectFactory.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use App\Models\Project;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends Factory<Project>
|
||||||
|
*/
|
||||||
|
class ProjectFactory extends Factory
|
||||||
|
{
|
||||||
|
protected $model = Project::class;
|
||||||
|
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'user_id' => $this->faker->randomNumber(),
|
||||||
|
'name' => $this->faker->name(),
|
||||||
|
'created_at' => Carbon::now(),
|
||||||
|
'updated_at' => Carbon::now(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('projects', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->bigInteger('user_id');
|
||||||
|
$table->string('name');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('projects');
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('servers', function (Blueprint $table) {
|
||||||
|
$table->unsignedBigInteger('project_id')->nullable()->after('id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('servers', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('project_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
27
database/migrations/2024_01_01_235900_update_users_table.php
Normal file
27
database/migrations/2024_01_01_235900_update_users_table.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->unsignedBigInteger('current_project_id')->nullable()->after('timezone');
|
||||||
|
});
|
||||||
|
User::query()->each(function (User $user) {
|
||||||
|
$project = $user->createDefaultProject();
|
||||||
|
$user->servers()->update(['project_id' => $project->id]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('users', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('current_project_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -22,6 +22,7 @@ public function run(): void
|
|||||||
]);
|
]);
|
||||||
$server = Server::factory()->create([
|
$server = Server::factory()->create([
|
||||||
'user_id' => $user->id,
|
'user_id' => $user->id,
|
||||||
|
'project_id' => $user->currentProject->id,
|
||||||
]);
|
]);
|
||||||
$server->services()->create([
|
$server->services()->create([
|
||||||
'type' => 'database',
|
'type' => 'database',
|
||||||
|
@ -233,6 +233,11 @@ chmod +x /home/${V_USERNAME}/${V_DOMAIN}/update.sh
|
|||||||
# cleanup
|
# cleanup
|
||||||
chown -R ${V_USERNAME}:${V_USERNAME} /home/${V_USERNAME}
|
chown -R ${V_USERNAME}:${V_USERNAME} /home/${V_USERNAME}
|
||||||
|
|
||||||
|
# cache
|
||||||
|
php artisan config:cache
|
||||||
|
php artisan icons:cache
|
||||||
|
|
||||||
|
# print info
|
||||||
echo "🎉 Congratulations!"
|
echo "🎉 Congratulations!"
|
||||||
echo "✅ SSH User: ${V_USERNAME}"
|
echo "✅ SSH User: ${V_USERNAME}"
|
||||||
echo "✅ SSH Password: ${V_PASSWORD}"
|
echo "✅ SSH Password: ${V_PASSWORD}"
|
||||||
|
12
package-lock.json
generated
12
package-lock.json
generated
@ -919,9 +919,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/follow-redirects": {
|
"node_modules/follow-redirects": {
|
||||||
"version": "1.15.2",
|
"version": "1.15.5",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
|
||||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -2525,9 +2525,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"follow-redirects": {
|
"follow-redirects": {
|
||||||
"version": "1.15.2",
|
"version": "1.15.5",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
|
||||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"form-data": {
|
"form-data": {
|
||||||
|
File diff suppressed because one or more lines are too long
1
public/build/assets/app-ae945733.css
Normal file
1
public/build/assets/app-ae945733.css
Normal file
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"resources/css/app.css": {
|
"resources/css/app.css": {
|
||||||
"file": "assets/app-328222da.css",
|
"file": "assets/app-ae945733.css",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
"src": "resources/css/app.css"
|
"src": "resources/css/app.css"
|
||||||
},
|
},
|
||||||
|
37
public/static/images/php-blank.svg
Normal file
37
public/static/images/php-blank.svg
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg width="256px" height="135px" viewBox="0 0 256 135" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
||||||
|
<defs>
|
||||||
|
<radialGradient id="radialGradient-1" cx="0.8366" cy="-125.811" r="363.0565" gradientTransform="matrix(0.463 0 0 0.463 76.4644 81.9182)" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0" style="stop-color:#FFFFFF"/>
|
||||||
|
<stop offset="0.5" style="stop-color:#4C6B97"/>
|
||||||
|
<stop offset="1" style="stop-color:#231F20"/>
|
||||||
|
</radialGradient>
|
||||||
|
</defs>
|
||||||
|
<g>
|
||||||
|
<ellipse fill="url(#radialGradient-1)" cx="128" cy="67.3" rx="128" ry="67.3"/>
|
||||||
|
<ellipse fill="#6181B6" cx="128" cy="67.3" rx="123" ry="62.3"/>
|
||||||
|
<g>
|
||||||
|
<path fill="#FFFFFF" d="M152.9,87.5c0,0,6.1-31.4,6.1-31.4c1.4-7.1,0.2-12.4-3.4-15.7c-3.5-3.2-9.5-4.8-18.3-4.8h-10.6l3-15.6
|
||||||
|
c0.1-0.6,0-1.2-0.4-1.7c-0.4-0.5-0.9-0.7-1.5-0.7h-14.6c-1,0-1.8,0.7-2,1.6l-6.5,33.3c-0.6-3.8-2-7-4.4-9.6
|
||||||
|
c-4.3-4.9-11-7.4-20.1-7.4H52.1c-1,0-1.8,0.7-2,1.6L37,104.7c-0.1,0.6,0,1.2,0.4,1.7c0.4,0.5,0.9,0.7,1.5,0.7h14.7
|
||||||
|
c1,0,1.8-0.7,2-1.6l3.2-16.3h10.9c5.7,0,10.6-0.6,14.3-1.8c3.9-1.3,7.4-3.4,10.5-6.3c2.5-2.3,4.6-4.9,6.2-7.7l-2.6,13.5
|
||||||
|
c-0.1,0.6,0,1.2,0.4,1.7s0.9,0.7,1.5,0.7h14.6c1,0,1.8-0.7,2-1.6l7.2-37h10c4.3,0,5.5,0.8,5.9,1.2c0.3,0.3,0.9,1.5,0.2,5.2
|
||||||
|
l-5.8,29.9c-0.1,0.6,0,1.2,0.4,1.7c0.4,0.5,0.9,0.7,1.5,0.7H151C151.9,89.1,152.7,88.4,152.9,87.5z M85.3,61.5
|
||||||
|
c-0.9,4.7-2.6,8.1-5.1,10c-2.5,1.9-6.6,2.9-12,2.9h-6.5l4.7-24.2h8.4c6.2,0,8.7,1.3,9.7,2.4C85.8,54.2,86.1,57.3,85.3,61.5z"/>
|
||||||
|
<path fill="#FFFFFF" d="M215.3,42.9c-4.3-4.9-11-7.4-20.1-7.4h-28.3c-1,0-1.8,0.7-2,1.6l-13.1,67.5c-0.1,0.6,0,1.2,0.4,1.7
|
||||||
|
c0.4,0.5,0.9,0.7,1.5,0.7h14.7c1,0,1.8-0.7,2-1.6l3.2-16.3h10.9c5.7,0,10.6-0.6,14.3-1.8c3.9-1.3,7.4-3.4,10.5-6.3
|
||||||
|
c2.6-2.4,4.8-5.1,6.4-8c1.6-2.9,2.8-6.1,3.5-9.6C220.9,54.7,219.6,47.9,215.3,42.9z M200,61.5c-0.9,4.7-2.6,8.1-5.1,10
|
||||||
|
c-2.5,1.9-6.6,2.9-12,2.9h-6.5l4.7-24.2h8.4c6.2,0,8.7,1.3,9.7,2.4C200.6,54.2,200.9,57.3,200,61.5z"/>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path fill="#000004" d="M74.8,48.2c5.6,0,9.3,1,11.2,3.1c1.9,2.1,2.3,5.6,1.3,10.6c-1,5.2-3,9-5.9,11.2c-2.9,2.2-7.3,3.3-13.2,3.3
|
||||||
|
h-8.9l5.5-28.2H74.8z M39,105h14.7l3.5-17.9h12.6c5.6,0,10.1-0.6,13.7-1.8c3.6-1.2,6.8-3.1,9.8-5.9c2.5-2.3,4.5-4.8,6-7.5
|
||||||
|
c1.5-2.7,2.6-5.7,3.2-9c1.6-8,0.4-14.2-3.5-18.7c-3.9-4.5-10.1-6.7-18.6-6.7H52.1L39,105z"/>
|
||||||
|
<path fill="#000004" d="M113.3,19.6h14.6l-3.5,17.9h13c8.2,0,13.8,1.4,16.9,4.3c3.1,2.9,4,7.5,2.8,13.9L151,87.1h-14.8l5.8-29.9
|
||||||
|
c0.7-3.4,0.4-5.7-0.7-6.9c-1.1-1.2-3.6-1.9-7.3-1.9h-11.7l-7.5,38.7h-14.6L113.3,19.6z"/>
|
||||||
|
<path fill="#000004" d="M189.5,48.2c5.6,0,9.3,1,11.2,3.1c1.9,2.1,2.3,5.6,1.3,10.6c-1,5.2-3,9-5.9,11.2c-2.9,2.2-7.3,3.3-13.2,3.3
|
||||||
|
h-8.9l5.5-28.2H189.5z M153.7,105h14.7l3.5-17.9h12.6c5.6,0,10.1-0.6,13.7-1.8c3.6-1.2,6.8-3.1,9.8-5.9c2.5-2.3,4.5-4.8,6-7.5
|
||||||
|
c1.5-2.7,2.6-5.7,3.2-9c1.6-8,0.4-14.2-3.5-18.7c-3.9-4.5-10.1-6.7-18.6-6.7h-28.3L153.7,105z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
1
resources/commands/webserver/nginx/get-vhost.sh
Executable file
1
resources/commands/webserver/nginx/get-vhost.sh
Executable file
@ -0,0 +1 @@
|
|||||||
|
cat /etc/nginx/sites-available/__domain__
|
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
|||||||
<a {{ $attributes->merge(['class' => 'block w-full px-4 py-2 text-left text-sm leading-5 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-800 transition duration-150 ease-in-out flex items-center justify-start']) }}>{{ $slot }}</a>
|
<a {{ $attributes->merge(['class' => 'block w-full px-4 py-2 text-left text-sm leading-5 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-700 transition duration-150 ease-in-out flex items-center justify-start']) }}>{{ $slot }}</a>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
@props(['align' => 'right', 'width' => '48', 'contentClasses' => 'py-1 bg-white dark:bg-gray-700'])
|
@props(['align' => 'right', 'width' => '48', 'contentClasses' => 'py-1 bg-white dark:bg-gray-800'])
|
||||||
|
|
||||||
@php
|
@php
|
||||||
switch ($align) {
|
switch ($align) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
@props(['server'])
|
@props(['server'])
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
@ -32,116 +32,124 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="font-sans antialiased bg-gray-100 dark:bg-gray-900 dark:text-gray-300 min-h-screen min-w-max"
|
<body class="font-sans antialiased bg-gray-100 dark:bg-gray-900 dark:text-gray-300 min-h-screen min-w-max"
|
||||||
x-data="" x-cloak>
|
x-data="" x-cloak>
|
||||||
<div class="flex min-h-screen">
|
<div class="flex min-h-screen">
|
||||||
<div
|
<div
|
||||||
class="left-0 top-0 min-h-screen w-64 flex-none bg-gray-800 dark:bg-gray-800/50 p-3 dark:border-r-2 dark:border-gray-800">
|
class="left-0 top-0 min-h-screen w-64 flex-none bg-gray-800 dark:bg-gray-800/50 p-3 dark:border-r-2 dark:border-gray-800">
|
||||||
<div class="h-16 block">
|
<div class="h-16 block">
|
||||||
<div class="flex items-center justify-start text-3xl font-extrabold text-white">
|
<div class="flex items-center justify-start text-2xl font-extrabold text-white">
|
||||||
<x-application-logo class="w-10 h-10 rounded-md"/>
|
<x-application-logo class="w-7 h-7 rounded-md" />
|
||||||
<span class="ml-1">Deploy</span>
|
<span class="ml-1">Deploy</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-5 space-y-2">
|
<div class="mb-5">
|
||||||
@include('layouts.partials.server-select', ['server' => isset($server) ? $server : null])
|
<div class="uppercase text-gray-300 text-sm font-semibold">{{ __("Projects") }}</div>
|
||||||
|
<div class="mt-2">
|
||||||
|
@include('layouts.partials.project-select', ['project' => auth()->user()->currentProject])
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-5 uppercase text-gray-300 text-sm font-semibold">{{ __("Servers") }}</div>
|
||||||
|
<div class="mt-2">
|
||||||
|
@include('layouts.partials.server-select', ['server' => isset($server) ? $server : null])
|
||||||
|
</div>
|
||||||
|
|
||||||
@if (isset($server))
|
@if (isset($server))
|
||||||
<x-sidebar-link :href="route('servers.show', ['server' => $server])" :active="request()->routeIs('servers.show')">
|
<div class="mt-3 space-y-1">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
<x-sidebar-link :href="route('servers.show', ['server' => $server])" :active="request()->routeIs('servers.show')">
|
||||||
stroke="currentColor" class="w-6 h-6">
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||||
<path stroke-linecap="round" stroke-linejoin="round"
|
stroke="currentColor" class="w-6 h-6">
|
||||||
d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
|
|
||||||
</svg>
|
|
||||||
<span class="ml-2 text-gray-50">{{ __('Overview') }}</span>
|
|
||||||
</x-sidebar-link>
|
|
||||||
@if ($server->isReady())
|
|
||||||
@if ($server->webserver())
|
|
||||||
<x-sidebar-link :href="route('servers.sites', ['server' => $server])" :active="request()->routeIs('servers.sites') || request()->is('servers/*/sites/*')">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round"
|
|
||||||
d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418" />
|
|
||||||
</svg>
|
|
||||||
<span class="ml-2 text-gray-50">{{ __('Sites') }}</span>
|
|
||||||
</x-sidebar-link>
|
|
||||||
@endif
|
|
||||||
@if ($server->database())
|
|
||||||
<x-sidebar-link :href="route('servers.databases', ['server' => $server])" :active="request()->routeIs('servers.databases') ||
|
|
||||||
request()->routeIs('servers.databases.backups')">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round"
|
|
||||||
d="M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75m16.5 0c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125" />
|
|
||||||
</svg>
|
|
||||||
<span class="ml-2 text-gray-50">{{ __('Databases') }}</span>
|
|
||||||
</x-sidebar-link>
|
|
||||||
@endif
|
|
||||||
@if ($server->php())
|
|
||||||
<x-sidebar-link :href="route('servers.php', ['server' => $server])" :active="request()->routeIs('servers.php')">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round"
|
|
||||||
d="M17.25 6.75L22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3l-4.5 16.5" />
|
|
||||||
</svg>
|
|
||||||
<span class="ml-2 text-gray-50">{{ __('PHP') }}</span>
|
|
||||||
</x-sidebar-link>
|
|
||||||
@endif
|
|
||||||
@if ($server->firewall())
|
|
||||||
<x-sidebar-link :href="route('servers.firewall', ['server' => $server])" :active="request()->routeIs('servers.firewall')">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round"
|
|
||||||
d="M15.362 5.214A8.252 8.252 0 0112 21 8.25 8.25 0 016.038 7.048 8.287 8.287 0 009 9.6a8.983 8.983 0 013.361-6.867 8.21 8.21 0 003 2.48z" />
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round"
|
|
||||||
d="M12 18a3.75 3.75 0 00.495-7.467 5.99 5.99 0 00-1.925 3.546 5.974 5.974 0 01-2.133-1A3.75 3.75 0 0012 18z" />
|
|
||||||
</svg>
|
|
||||||
<span class="ml-2 text-gray-50">{{ __('Firewall') }}</span>
|
|
||||||
</x-sidebar-link>
|
|
||||||
@endif
|
|
||||||
<x-sidebar-link :href="route('servers.cronjobs', ['server' => $server])" :active="request()->routeIs('servers.cronjobs')">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round"
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" />
|
d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
|
||||||
</svg>
|
</svg>
|
||||||
<span class="ml-2 text-gray-50">{{ __('Cronjobs') }}</span>
|
<span class="ml-2 text-gray-50">{{ __('Overview') }}</span>
|
||||||
</x-sidebar-link>
|
</x-sidebar-link>
|
||||||
<x-sidebar-link :href="route('servers.ssh-keys', ['server' => $server])" :active="request()->routeIs('servers.ssh-keys')">
|
@if ($server->isReady())
|
||||||
|
@if ($server->webserver())
|
||||||
|
<x-sidebar-link :href="route('servers.sites', ['server' => $server])" :active="request()->routeIs('servers.sites') || request()->is('servers/*/sites/*')">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418" />
|
||||||
|
</svg>
|
||||||
|
<span class="ml-2 text-gray-50">{{ __('Sites') }}</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
@endif
|
||||||
|
@if ($server->database())
|
||||||
|
<x-sidebar-link :href="route('servers.databases', ['server' => $server])" :active="request()->routeIs('servers.databases') ||
|
||||||
|
request()->routeIs('servers.databases.backups')">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
d="M20.25 6.375c0 2.278-3.694 4.125-8.25 4.125S3.75 8.653 3.75 6.375m16.5 0c0-2.278-3.694-4.125-8.25-4.125S3.75 4.097 3.75 6.375m16.5 0v11.25c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125V6.375m16.5 0v3.75m-16.5-3.75v3.75m16.5 0v3.75C20.25 16.153 16.556 18 12 18s-8.25-1.847-8.25-4.125v-3.75m16.5 0c0 2.278-3.694 4.125-8.25 4.125s-8.25-1.847-8.25-4.125" />
|
||||||
|
</svg>
|
||||||
|
<span class="ml-2 text-gray-50">{{ __('Databases') }}</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
@endif
|
||||||
|
@if ($server->php())
|
||||||
|
<x-sidebar-link :href="route('servers.php', ['server' => $server])" :active="request()->routeIs('servers.php')">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
d="M17.25 6.75L22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3l-4.5 16.5" />
|
||||||
|
</svg>
|
||||||
|
<span class="ml-2 text-gray-50">{{ __('PHP') }}</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
@endif
|
||||||
|
@if ($server->firewall())
|
||||||
|
<x-sidebar-link :href="route('servers.firewall', ['server' => $server])" :active="request()->routeIs('servers.firewall')">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
d="M15.362 5.214A8.252 8.252 0 0112 21 8.25 8.25 0 016.038 7.048 8.287 8.287 0 009 9.6a8.983 8.983 0 013.361-6.867 8.21 8.21 0 003 2.48z" />
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
d="M12 18a3.75 3.75 0 00.495-7.467 5.99 5.99 0 00-1.925 3.546 5.974 5.974 0 01-2.133-1A3.75 3.75 0 0012 18z" />
|
||||||
|
</svg>
|
||||||
|
<span class="ml-2 text-gray-50">{{ __('Firewall') }}</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
@endif
|
||||||
|
<x-sidebar-link :href="route('servers.cronjobs', ['server' => $server])" :active="request()->routeIs('servers.cronjobs')">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
d="M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" /> </svg>
|
||||||
|
<span class="ml-2 text-gray-50">{{ __('Cronjobs') }}</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
<x-sidebar-link :href="route('servers.ssh-keys', ['server' => $server])" :active="request()->routeIs('servers.ssh-keys')">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
d="M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z" />
|
||||||
|
</svg>
|
||||||
|
<span class="ml-2 text-gray-50">{{ __('SSH Keys') }}</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
<x-sidebar-link :href="route('servers.services', ['server' => $server])" :active="request()->routeIs('servers.services')">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /> </svg>
|
||||||
|
<span class="ml-2 text-gray-50">{{ __('Services') }}</span>
|
||||||
|
</x-sidebar-link>
|
||||||
|
@endif
|
||||||
|
<x-sidebar-link :href="route('servers.settings', ['server' => $server])" :active="request()->routeIs('servers.settings')">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round"
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
d="M15.75 5.25a3 3 0 013 3m3 0a6 6 0 01-7.029 5.912c-.563-.097-1.159.026-1.563.43L10.5 17.25H8.25v2.25H6v2.25H2.25v-2.818c0-.597.237-1.17.659-1.591l6.499-6.499c.404-.404.527-1 .43-1.563A6 6 0 1121.75 8.25z" />
|
d="M11.42 15.17L17.25 21A2.652 2.652 0 0021 17.25l-5.877-5.877M11.42 15.17l2.496-3.03c.317-.384.74-.626 1.208-.766M11.42 15.17l-4.655 5.653a2.548 2.548 0 11-3.586-3.586l6.837-5.63m5.108-.233c.55-.164 1.163-.188 1.743-.14a4.5 4.5 0 004.486-6.336l-3.276 3.277a3.004 3.004 0 01-2.25-2.25l3.276-3.276a4.5 4.5 0 00-6.336 4.486c.091 1.076-.071 2.264-.904 2.95l-.102.085m-1.745 1.437L5.909 7.5H4.5L2.25 3.75l1.5-1.5L7.5 4.5v1.409l4.26 4.26m-1.745 1.437l1.745-1.437m6.615 8.206L15.75 15.75M4.867 19.125h.008v.008h-.008v-.008z" />
|
||||||
</svg>
|
</svg>
|
||||||
<span class="ml-2 text-gray-50">{{ __('SSH Keys') }}</span>
|
<span class="ml-2 text-gray-50">{{ __('Settings') }}</span>
|
||||||
</x-sidebar-link>
|
</x-sidebar-link>
|
||||||
<x-sidebar-link :href="route('servers.services', ['server' => $server])" :active="request()->routeIs('servers.services')">
|
<x-sidebar-link :href="route('servers.logs', ['server' => $server])" :active="request()->routeIs('servers.logs')">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round"
|
<path stroke-linecap="round" stroke-linejoin="round"
|
||||||
d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
|
d="M6.429 9.75L2.25 12l4.179 2.25m0-4.5l5.571 3 5.571-3m-11.142 0L2.25 7.5 12 2.25l9.75 5.25-4.179 2.25m0 0L21.75 12l-4.179 2.25m0 0l4.179 2.25L12 21.75 2.25 16.5l4.179-2.25m11.142 0l-5.571 3-5.571-3" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round"
|
|
||||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
||||||
</svg>
|
</svg>
|
||||||
<span class="ml-2 text-gray-50">{{ __('Services') }}</span>
|
<span class="ml-2 text-gray-50">{{ __('Logs') }}</span>
|
||||||
</x-sidebar-link>
|
</x-sidebar-link>
|
||||||
@endif
|
</div>
|
||||||
<x-sidebar-link :href="route('servers.settings', ['server' => $server])" :active="request()->routeIs('servers.settings')">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round"
|
|
||||||
d="M11.42 15.17L17.25 21A2.652 2.652 0 0021 17.25l-5.877-5.877M11.42 15.17l2.496-3.03c.317-.384.74-.626 1.208-.766M11.42 15.17l-4.655 5.653a2.548 2.548 0 11-3.586-3.586l6.837-5.63m5.108-.233c.55-.164 1.163-.188 1.743-.14a4.5 4.5 0 004.486-6.336l-3.276 3.277a3.004 3.004 0 01-2.25-2.25l3.276-3.276a4.5 4.5 0 00-6.336 4.486c.091 1.076-.071 2.264-.904 2.95l-.102.085m-1.745 1.437L5.909 7.5H4.5L2.25 3.75l1.5-1.5L7.5 4.5v1.409l4.26 4.26m-1.745 1.437l1.745-1.437m6.615 8.206L15.75 15.75M4.867 19.125h.008v.008h-.008v-.008z" />
|
|
||||||
</svg>
|
|
||||||
<span class="ml-2 text-gray-50">{{ __('Settings') }}</span>
|
|
||||||
</x-sidebar-link>
|
|
||||||
<x-sidebar-link :href="route('servers.logs', ['server' => $server])" :active="request()->routeIs('servers.logs')">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
|
||||||
stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round"
|
|
||||||
d="M6.429 9.75L2.25 12l4.179 2.25m0-4.5l5.571 3 5.571-3m-11.142 0L2.25 7.5 12 2.25l9.75 5.25-4.179 2.25m0 0L21.75 12l-4.179 2.25m0 0l4.179 2.25L12 21.75 2.25 16.5l4.179-2.25m11.142 0l-5.571 3-5.571-3" />
|
|
||||||
</svg>
|
|
||||||
<span class="ml-2 text-gray-50">{{ __('Logs') }}</span>
|
|
||||||
</x-sidebar-link>
|
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -154,48 +162,7 @@ class="min-h-screen w-64 flex-none border-r border-gray-200 bg-white dark:border
|
|||||||
@endif
|
@endif
|
||||||
|
|
||||||
<div class="flex min-h-screen flex-grow flex-col">
|
<div class="flex min-h-screen flex-grow flex-col">
|
||||||
<nav class="h-16 border-b border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900">
|
@include('layouts.navigation')
|
||||||
<!-- Primary Navigation Menu -->
|
|
||||||
<div class="mx-auto max-w-full px-4 sm:px-6 lg:px-8">
|
|
||||||
<div class="flex h-16 justify-end">
|
|
||||||
<div class="flex items-center justify-center">
|
|
||||||
{{-- Search --}}
|
|
||||||
</div>
|
|
||||||
@include('layouts.partials.color-scheme')
|
|
||||||
<div class="ml-6 flex items-center">
|
|
||||||
<div class="relative ml-5">
|
|
||||||
<x-dropdown align="right" width="48">
|
|
||||||
<x-slot name="trigger">
|
|
||||||
<div class="flex cursor-pointer items-center justify-between">
|
|
||||||
{{ auth()->user()->name }}
|
|
||||||
<svg class="ml-2 -mr-0.5 h-4 w-4" xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 20 20" fill="currentColor">
|
|
||||||
<path fill-rule="evenodd"
|
|
||||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
|
||||||
clip-rule="evenodd" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</x-slot>
|
|
||||||
|
|
||||||
<x-slot name="content">
|
|
||||||
<x-dropdown-link :href="route('profile')">Profile</x-dropdown-link>
|
|
||||||
<div class="border-t border-gray-100 dark:border-gray-700"></div>
|
|
||||||
|
|
||||||
<div class="border-t border-gray-100 dark:border-gray-700"></div>
|
|
||||||
<form method="POST" action="{{ route('logout') }}">
|
|
||||||
@csrf
|
|
||||||
<x-dropdown-link :href="route('logout')"
|
|
||||||
onclick="event.preventDefault(); this.closest('form').submit();">
|
|
||||||
{{ __('Log Out') }}
|
|
||||||
</x-dropdown-link>
|
|
||||||
</form>
|
|
||||||
</x-slot>
|
|
||||||
</x-dropdown>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<!-- Page Heading -->
|
<!-- Page Heading -->
|
||||||
@if (isset($header))
|
@if (isset($header))
|
||||||
@ -234,7 +201,7 @@ class="min-h-screen w-64 flex-none border-r border-gray-200 bg-white dark:border
|
|||||||
|
|
||||||
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
|
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
|
||||||
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia(
|
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia(
|
||||||
'(prefers-color-scheme: dark)').matches)) {
|
'(prefers-color-scheme: dark)').matches)) {
|
||||||
document.documentElement.classList.add('dark')
|
document.documentElement.classList.add('dark')
|
||||||
} else {
|
} else {
|
||||||
document.documentElement.classList.remove('dark')
|
document.documentElement.classList.remove('dark')
|
||||||
|
@ -20,11 +20,14 @@
|
|||||||
<div class="min-h-screen flex flex-col sm:justify-center items-center pt-6 sm:pt-0 bg-gray-100 dark:bg-gray-900">
|
<div class="min-h-screen flex flex-col sm:justify-center items-center pt-6 sm:pt-0 bg-gray-100 dark:bg-gray-900">
|
||||||
<div>
|
<div>
|
||||||
<a href="/">
|
<a href="/">
|
||||||
<x-application-logo class="w-20 h-20 fill-current text-gray-500 rounded-lg" />
|
<div class="flex items-center justify-start text-3xl font-extrabold">
|
||||||
|
<x-application-logo class="w-9 h-9 rounded-md" />
|
||||||
|
<span class="ml-1">Deploy</span>
|
||||||
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="w-full sm:max-w-md mt-6 px-6 py-4 bg-white dark:bg-gray-800 shadow-md overflow-hidden rounded-lg">
|
<div class="w-full sm:max-w-md mt-10 px-6 py-4 bg-white dark:bg-gray-800 shadow-md overflow-hidden rounded-lg">
|
||||||
{{ $slot }}
|
{{ $slot }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,61 +1,16 @@
|
|||||||
<nav x-data="{ open: false }" class="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
|
<nav x-data="{ open: false }" class="h-16 border-b border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900">
|
||||||
<!-- Primary Navigation Menu -->
|
<div class="mx-auto max-w-full px-4 sm:px-6 lg:px-8">
|
||||||
<div class="max-w-7xl mx-auto px-6">
|
<div class="flex items-center justify-between h-16">
|
||||||
<div class="flex justify-between h-16">
|
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<!-- Logo -->
|
|
||||||
<div class="shrink-0 flex items-center">
|
</div>
|
||||||
<x-application-logo class="block h-9 w-auto fill-current text-gray-800 dark:text-gray-200 rounded-lg" />
|
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="mr-3">
|
||||||
|
@include('layouts.partials.color-scheme')
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Navigation Links -->
|
<livewire:user-dropdown />
|
||||||
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
|
|
||||||
<x-nav-link :href="route('servers')" :active="request()->routeIs('servers') || request()->is('servers/*')">
|
|
||||||
{{ __('Servers') }}
|
|
||||||
</x-nav-link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Settings Dropdown -->
|
|
||||||
<livewire:user-dropdown />
|
|
||||||
|
|
||||||
<!-- Hamburger -->
|
|
||||||
<div class="-mr-2 flex items-center sm:hidden">
|
|
||||||
<button @click="open = ! open" class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 dark:text-gray-500 hover:text-gray-500 dark:hover:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-900 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-900 focus:text-gray-500 dark:focus:text-gray-400 transition duration-150 ease-in-out">
|
|
||||||
<svg class="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
|
|
||||||
<path :class="{'hidden': open, 'inline-flex': ! open }" class="inline-flex" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
|
||||||
<path :class="{'hidden': ! open, 'inline-flex': open }" class="hidden" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Responsive Navigation Menu -->
|
|
||||||
<div :class="{'block': open, 'hidden': ! open}" class="hidden sm:hidden">
|
|
||||||
<!-- Responsive Settings Options -->
|
|
||||||
<div class="pt-4 pb-1 border-t border-gray-200 dark:border-gray-600">
|
|
||||||
<div class="px-4">
|
|
||||||
<div class="font-medium text-base text-gray-800 dark:text-gray-200">{{ Auth::user()->name }}</div>
|
|
||||||
<div class="font-medium text-sm text-gray-500">{{ Auth::user()->email }}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-3 space-y-1">
|
|
||||||
<x-responsive-nav-link :href="route('profile')">
|
|
||||||
{{ __('Profile') }}
|
|
||||||
</x-responsive-nav-link>
|
|
||||||
|
|
||||||
<x-responsive-nav-link :href="route('profile')">
|
|
||||||
{{ __('Settings') }}
|
|
||||||
</x-responsive-nav-link>
|
|
||||||
|
|
||||||
<!-- Authentication -->
|
|
||||||
<form method="POST" action="{{ route('logout') }}">
|
|
||||||
@csrf
|
|
||||||
<x-responsive-nav-link :href="route('logout')" onclick="event.preventDefault(); this.closest('form').submit();">
|
|
||||||
{{ __('Log Out') }}
|
|
||||||
</x-responsive-nav-link>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,37 +1,46 @@
|
|||||||
<div class="flex items-center" x-data="{
|
<div class="flex items-center text-gray-600 dark:text-gray-300" x-data="{
|
||||||
isDarkMode: localStorage.theme,
|
theme: localStorage.theme,
|
||||||
toggleTheme() {
|
isDark() {
|
||||||
localStorage.theme = this.isDarkMode === 'dark' ? 'light' : 'dark';
|
if (this.theme === 'dark') {
|
||||||
|
return true
|
||||||
if (localStorage.theme === 'dark') {
|
}
|
||||||
|
console.log(window.matchMedia('(prefers-color-scheme: dark)'))
|
||||||
|
return this.theme === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
},
|
||||||
|
changeTheme(theme) {
|
||||||
|
this.theme = theme;
|
||||||
|
localStorage.theme = theme;
|
||||||
|
this.updateDocument();
|
||||||
|
},
|
||||||
|
updateDocument() {
|
||||||
|
if (this.isDark()) {
|
||||||
document.documentElement.classList.add('dark')
|
document.documentElement.classList.add('dark')
|
||||||
} else {
|
} else {
|
||||||
document.documentElement.classList.remove('dark')
|
document.documentElement.classList.remove('dark')
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isDarkMode = localStorage.theme
|
|
||||||
}
|
}
|
||||||
}" x-on:click="toggleTheme()">
|
}" x-init="updateDocument()">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="flex items-center justify-end">
|
<div class="flex items-center justify-end">
|
||||||
<button id="theme-toggle" type="button" class="text-sm p-2"
|
<x-dropdown>
|
||||||
:class="isDarkMode === 'dark' ? 'text-gray-300 border-gray-300' :
|
<x-slot name="trigger">
|
||||||
'text-gray-800 border-gray-800'">
|
<button type="button" class="flex items-center">
|
||||||
<svg x-show="isDarkMode !== 'dark'" id="theme-toggle-dark-icon" class="w-5 h-5"
|
<x-heroicon-o-moon x-show="isDark()" class="h-7 w-7" />
|
||||||
fill="currentColor" viewBox="0 0 20 20"
|
<x-heroicon-o-sun x-show="!isDark()" class="h-7 w-7" />
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
</button>
|
||||||
<path
|
</x-slot>
|
||||||
d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z">
|
<x-slot name="content">
|
||||||
</path>
|
<x-dropdown-link class="cursor-pointer" x-on:click="changeTheme('dark')">
|
||||||
</svg>
|
<x-heroicon-o-moon class="h-5 w-5 mr-2" x-bind:class="theme === 'dark' ? 'text-primary-600' : ''" /> {{ __("Dark") }}
|
||||||
<svg x-show="isDarkMode === 'dark'" id="theme-toggle-light-icon" class="w-5 h-5"
|
</x-dropdown-link>
|
||||||
fill="currentColor" viewBox="0 0 20 20"
|
<x-dropdown-link class="cursor-pointer" x-on:click="changeTheme('light')">
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
<x-heroicon-o-sun class="h-5 w-5 mr-2" x-bind:class="theme === 'light' ? 'text-primary-600' : ''" /> {{ __("Light") }}
|
||||||
<path
|
</x-dropdown-link>
|
||||||
d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
|
<x-dropdown-link class="cursor-pointer" x-on:click="changeTheme('system')">
|
||||||
fillRule="evenodd" clipRule="evenodd"></path>
|
<x-heroicon-o-computer-desktop class="h-5 w-5 mr-2" x-bind:class="theme === 'system' ? 'text-primary-600' : ''" /> {{ __("System") }}
|
||||||
</svg>
|
</x-dropdown-link>
|
||||||
</button>
|
</x-slot>
|
||||||
|
</x-dropdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
90
resources/views/layouts/partials/project-select.blade.php
Normal file
90
resources/views/layouts/partials/project-select.blade.php
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<div x-data="projectCombobox()">
|
||||||
|
<div class="relative">
|
||||||
|
<div @click="open = !open" class="z-0 w-full cursor-pointer px-4 py-3 pr-10 text-md leading-5 text-gray-100 focus:ring-1 focus:ring-gray-700 bg-gray-900 rounded-md h-10 flex items-center" x-text="selected.name ?? 'Select Project'"></div>
|
||||||
|
<button type="button" @click="open = !open" class="z-0 absolute inset-y-0 right-0 flex items-center pr-2">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" class="h-5 w-5 text-gray-400"><path fill-rule="evenodd" d="M10 3a.75.75 0 01.55.24l3.25 3.5a.75.75 0 11-1.1 1.02L10 4.852 7.3 7.76a.75.75 0 01-1.1-1.02l3.25-3.5A.75.75 0 0110 3zm-3.76 9.2a.75.75 0 011.06.04l2.7 2.908 2.7-2.908a.75.75 0 111.1 1.02l-3.25 3.5a.75.75 0 01-1.1 0l-3.25-3.5a.75.75 0 01.04-1.06z" clip-rule="evenodd"></path></svg>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
x-show="open"
|
||||||
|
@click.away="open = false"
|
||||||
|
class="z-10 absolute mt-1 w-full overflow-auto rounded-md pb-1 bg-white dark:bg-gray-700 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
|
||||||
|
<div class="p-2 relative">
|
||||||
|
<input x-model="query"
|
||||||
|
@input="filterProjectsAndOpen"
|
||||||
|
placeholder="Filter"
|
||||||
|
class="w-full py-2 pl-3 pr-10 text-sm leading-5 dark:text-gray-100 focus:ring-1 focus:ring-gray-400 dark:focus:ring-800 bg-gray-200 dark:bg-gray-900 rounded-md"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="relative max-h-[350px] overflow-y-auto">
|
||||||
|
<template x-for="(project, index) in filteredProjects" :key="index">
|
||||||
|
<div
|
||||||
|
@click="selectProject(project); open = false"
|
||||||
|
:class="project.id === selected.id ? 'cursor-default bg-primary-600 text-white' : 'cursor-pointer'"
|
||||||
|
class="relative select-none py-2 px-4 text-gray-700 dark:text-white hover:bg-primary-600 hover:text-white">
|
||||||
|
<span class="block truncate" x-text="project.name"></span>
|
||||||
|
<template x-if="project.id === selected.id">
|
||||||
|
<span class="absolute inset-y-0 right-0 flex items-center pr-3 text-white">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" class="h-5 w-5"><path fill-rule="evenodd" d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z" clip-rule="evenodd"></path></svg>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
x-show="filteredProjects.length === 0"
|
||||||
|
class="relative cursor-default select-none py-2 px-4 text-gray-700 dark:text-white block truncate">
|
||||||
|
No projects found!
|
||||||
|
</div>
|
||||||
|
<div class="py-1">
|
||||||
|
<hr class="border-gray-300 dark:border-gray-600">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a
|
||||||
|
href="{{ route('projects') }}"
|
||||||
|
class="relative select-none py-2 px-4 text-gray-700 dark:text-white hover:bg-primary-600 hover:text-white block cursor-pointer">
|
||||||
|
<span class="block truncate">Projects List</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a
|
||||||
|
href="{{ route('projects', ['create' => true]) }}"
|
||||||
|
class="relative select-none py-2 px-4 text-gray-700 dark:text-white hover:bg-primary-600 hover:text-white block cursor-pointer">
|
||||||
|
<span class="block truncate">Create a Project</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function projectCombobox() {
|
||||||
|
const projects = @json(auth()->user()->projects()->select('id', 'name')->get());
|
||||||
|
return {
|
||||||
|
open: false,
|
||||||
|
query: '',
|
||||||
|
projects: projects,
|
||||||
|
selected: @if(isset($project)) @json($project->only('id', 'name')) @else {} @endif,
|
||||||
|
filteredProjects: projects,
|
||||||
|
selectProject(project) {
|
||||||
|
if (this.selected.id !== project.id) {
|
||||||
|
this.selected = project;
|
||||||
|
window.location.href = '{{ url('/settings/projects/') }}/' + project.id
|
||||||
|
}
|
||||||
|
},
|
||||||
|
filterProjectsAndOpen() {
|
||||||
|
if (this.query === '') {
|
||||||
|
this.filteredProjects = this.projects;
|
||||||
|
this.open = false;
|
||||||
|
} else {
|
||||||
|
this.filteredProjects = this.projects.filter((project) =>
|
||||||
|
project.name
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\s+/g, '')
|
||||||
|
.includes(this.query.toLowerCase().replace(/\s+/g, ''))
|
||||||
|
);
|
||||||
|
this.open = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
</script>
|
@ -1,13 +1,13 @@
|
|||||||
<div x-data="serverCombobox()">
|
<div x-data="serverCombobox()">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<div @click="open = !open" class="w-full cursor-pointer px-4 py-3 pr-10 text-md leading-5 text-gray-100 focus:ring-1 focus:ring-gray-700 bg-gray-900 rounded-md h-10 flex items-center" x-text="selected.name ?? 'Select Server'"></div>
|
<div @click="open = !open" class="z-0 w-full cursor-pointer px-4 py-3 pr-10 text-md leading-5 text-gray-100 focus:ring-1 focus:ring-gray-700 bg-gray-900 rounded-md h-10 flex items-center" x-text="selected.name ?? 'Select Server'"></div>
|
||||||
<button type="button" @click="open = !open" class="absolute inset-y-0 right-0 flex items-center pr-2">
|
<button type="button" @click="open = !open" class="z-0 absolute inset-y-0 right-0 flex items-center pr-2">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" class="h-5 w-5 text-gray-400"><path fill-rule="evenodd" d="M10 3a.75.75 0 01.55.24l3.25 3.5a.75.75 0 11-1.1 1.02L10 4.852 7.3 7.76a.75.75 0 01-1.1-1.02l3.25-3.5A.75.75 0 0110 3zm-3.76 9.2a.75.75 0 011.06.04l2.7 2.908 2.7-2.908a.75.75 0 111.1 1.02l-3.25 3.5a.75.75 0 01-1.1 0l-3.25-3.5a.75.75 0 01.04-1.06z" clip-rule="evenodd"></path></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" class="h-5 w-5 text-gray-400"><path fill-rule="evenodd" d="M10 3a.75.75 0 01.55.24l3.25 3.5a.75.75 0 11-1.1 1.02L10 4.852 7.3 7.76a.75.75 0 01-1.1-1.02l3.25-3.5A.75.75 0 0110 3zm-3.76 9.2a.75.75 0 011.06.04l2.7 2.908 2.7-2.908a.75.75 0 111.1 1.02l-3.25 3.5a.75.75 0 01-1.1 0l-3.25-3.5a.75.75 0 01.04-1.06z" clip-rule="evenodd"></path></svg>
|
||||||
</button>
|
</button>
|
||||||
<div
|
<div
|
||||||
x-show="open"
|
x-show="open"
|
||||||
@click.away="open = false"
|
@click.away="open = false"
|
||||||
class="absolute mt-1 w-full overflow-auto rounded-md pb-1 bg-white dark:bg-gray-700 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
|
class="z-10 absolute mt-1 w-full overflow-auto rounded-md pb-1 bg-white dark:bg-gray-700 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
|
||||||
<div class="p-2 relative">
|
<div class="p-2 relative">
|
||||||
<input x-model="query"
|
<input x-model="query"
|
||||||
@input="filterServersAndOpen"
|
@input="filterServersAndOpen"
|
||||||
@ -58,7 +58,7 @@ class="relative select-none py-2 px-4 text-gray-700 dark:text-white hover:bg-pri
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
function serverCombobox() {
|
function serverCombobox() {
|
||||||
const servers = @json(\App\Models\Server::query()->select('id', 'name')->get());
|
const servers = @json(auth()->user()->currentProject->servers()->select('id', 'name')->get());
|
||||||
return {
|
return {
|
||||||
open: false,
|
open: false,
|
||||||
query: '',
|
query: '',
|
||||||
|
@ -16,6 +16,12 @@
|
|||||||
</svg>
|
</svg>
|
||||||
{{ __('Profile') }}
|
{{ __('Profile') }}
|
||||||
</x-secondary-sidebar-link>
|
</x-secondary-sidebar-link>
|
||||||
|
<x-secondary-sidebar-link :href="route('projects')" :active="request()->routeIs('projects')">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 mr-2">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="m7.875 14.25 1.214 1.942a2.25 2.25 0 0 0 1.908 1.058h2.006c.776 0 1.497-.4 1.908-1.058l1.214-1.942M2.41 9h4.636a2.25 2.25 0 0 1 1.872 1.002l.164.246a2.25 2.25 0 0 0 1.872 1.002h2.092a2.25 2.25 0 0 0 1.872-1.002l.164-.246A2.25 2.25 0 0 1 16.954 9h4.636M2.41 9a2.25 2.25 0 0 0-.16.832V12a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 12V9.832c0-.287-.055-.57-.16-.832M2.41 9a2.25 2.25 0 0 1 .382-.632l3.285-3.832a2.25 2.25 0 0 1 1.708-.786h8.43c.657 0 1.281.287 1.709.786l3.284 3.832c.163.19.291.404.382.632M4.5 20.25h15A2.25 2.25 0 0 0 21.75 18v-2.625c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125V18a2.25 2.25 0 0 0 2.25 2.25Z" />
|
||||||
|
</svg>
|
||||||
|
{{ __('Projects') }}
|
||||||
|
</x-secondary-sidebar-link>
|
||||||
<x-secondary-sidebar-link :href="route('server-providers')" :active="request()->routeIs('server-providers')">
|
<x-secondary-sidebar-link :href="route('server-providers')" :active="request()->routeIs('server-providers')">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 mr-2">
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 mr-2">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5.25 14.25h13.5m-13.5 0a3 3 0 01-3-3m3 3a3 3 0 100 6h13.5a3 3 0 100-6m-16.5-3a3 3 0 013-3h13.5a3 3 0 013 3m-19.5 0a4.5 4.5 0 01.9-2.7L5.737 5.1a3.375 3.375 0 012.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 01.9 2.7m0 0a3 3 0 01-3 3m0 3h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008zm-3 6h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008z" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M5.25 14.25h13.5m-13.5 0a3 3 0 01-3-3m3 3a3 3 0 100 6h13.5a3 3 0 100-6m-16.5-3a3 3 0 013-3h13.5a3 3 0 013 3m-19.5 0a4.5 4.5 0 01.9-2.7L5.737 5.1a3.375 3.375 0 012.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 01.9 2.7m0 0a3 3 0 01-3 3m0 3h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008zm-3 6h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008z" />
|
||||||
|
@ -1,36 +1 @@
|
|||||||
<div>
|
@include('livewire.application.php-app')
|
||||||
<x-card-header>
|
|
||||||
<x-slot name="title">{{ __("Application") }}</x-slot>
|
|
||||||
<x-slot name="description">{{ __("Here you can manage your application") }}</x-slot>
|
|
||||||
<x-slot name="aside">
|
|
||||||
<div class="flex items-center">
|
|
||||||
<div class="mr-2">
|
|
||||||
<livewire:application.deploy :site="$site" />
|
|
||||||
</div>
|
|
||||||
<div class="mr-2">
|
|
||||||
<livewire:application.auto-deployment :site="$site" />
|
|
||||||
</div>
|
|
||||||
<x-dropdown>
|
|
||||||
<x-slot name="trigger">
|
|
||||||
<x-secondary-button>
|
|
||||||
{{ __('Manage') }}
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 ml-1">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9" />
|
|
||||||
</svg>
|
|
||||||
</x-secondary-button>
|
|
||||||
</x-slot>
|
|
||||||
<x-slot name="content">
|
|
||||||
<x-dropdown-link class="cursor-pointer" x-on:click="$dispatch('open-modal', 'change-branch')">{{ __("Branch") }}</x-dropdown-link>
|
|
||||||
<x-dropdown-link class="cursor-pointer" x-on:click="$dispatch('open-modal', 'deployment-script')">{{ __("Deployment Script") }}</x-dropdown-link>
|
|
||||||
<x-dropdown-link class="cursor-pointer" x-on:click="$dispatch('open-modal', 'update-env')">{{ __(".env") }}</x-dropdown-link>
|
|
||||||
</x-slot>
|
|
||||||
</x-dropdown>
|
|
||||||
<livewire:application.change-branch :site="$site" />
|
|
||||||
<livewire:application.deployment-script :site="$site" />
|
|
||||||
<livewire:application.env :site="$site" />
|
|
||||||
</div>
|
|
||||||
</x-slot>
|
|
||||||
</x-card-header>
|
|
||||||
|
|
||||||
<livewire:application.deployments-list :site="$site" />
|
|
||||||
</div>
|
|
||||||
|
@ -1,3 +1,62 @@
|
|||||||
<div>
|
<div>
|
||||||
|
<x-card-header>
|
||||||
|
<x-slot name="title">{{ __('Application') }}</x-slot>
|
||||||
|
<x-slot name="description">{{ __('Here you can manage your application') }}</x-slot>
|
||||||
|
<x-slot name="aside">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="mr-2">
|
||||||
|
<livewire:application.deploy :site="$site" />
|
||||||
|
</div>
|
||||||
|
@if ($site->source_control_id)
|
||||||
|
<div class="mr-2">
|
||||||
|
<livewire:application.auto-deployment :site="$site" />
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
<x-dropdown>
|
||||||
|
<x-slot name="trigger">
|
||||||
|
<x-secondary-button>
|
||||||
|
{{ __('Manage') }}
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke="currentColor"
|
||||||
|
class="ml-1 h-5 w-5"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
d="M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</x-secondary-button>
|
||||||
|
</x-slot>
|
||||||
|
<x-slot name="content">
|
||||||
|
@if ($site->source_control_id)
|
||||||
|
<x-dropdown-link
|
||||||
|
class="cursor-pointer"
|
||||||
|
x-on:click="$dispatch('open-modal', 'change-branch')"
|
||||||
|
>{{ __('Branch') }}</x-dropdown-link>
|
||||||
|
@endif
|
||||||
|
<x-dropdown-link
|
||||||
|
class="cursor-pointer"
|
||||||
|
x-on:click="$dispatch('open-modal', 'deployment-script')"
|
||||||
|
>{{ __('Deployment Script') }}</x-dropdown-link>
|
||||||
|
<x-dropdown-link
|
||||||
|
class="cursor-pointer"
|
||||||
|
x-on:click="$dispatch('open-modal', 'update-env')"
|
||||||
|
>{{ __('.env') }}</x-dropdown-link>
|
||||||
|
</x-slot>
|
||||||
|
</x-dropdown>
|
||||||
|
@if ($site->source_control_id)
|
||||||
|
<livewire:application.change-branch :site="$site" />
|
||||||
|
@endif
|
||||||
|
<livewire:application.deployment-script :site="$site" />
|
||||||
|
<livewire:application.env :site="$site" />
|
||||||
|
</div>
|
||||||
|
</x-slot>
|
||||||
|
</x-card-header>
|
||||||
|
|
||||||
|
<livewire:application.deployments-list :site="$site" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
@include('livewire.application.php-app')
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user