mirror of
https://github.com/vitodeploy/vito.git
synced 2025-07-01 14:06:15 +00:00
Compare commits
39 Commits
Author | SHA1 | Date | |
---|---|---|---|
9db310a06b | |||
f70963d6bb | |||
b75df8e1c5 | |||
a22e9cb946 | |||
b2b9bea0b1 | |||
3f4a2bce3a | |||
8bffefabef | |||
3da1f4fe4c | |||
2214a76e09 | |||
55bf8b8ecf | |||
0420babdef | |||
1c3d78a5ed | |||
8665435bc4 | |||
0ec6a9dea2 | |||
bdfda05398 | |||
919cdc6892 | |||
902548e463 | |||
2462b31f3b | |||
e997d0deea | |||
f06b8f7d20 | |||
f120a570e8 | |||
2d7f225ff2 | |||
31bd146239 | |||
10a6bb57a8 | |||
fd2244d382 | |||
551f1ce40e | |||
1ce92d9361 | |||
ec6e55e30c | |||
4cda14f4b8 | |||
5e6d338bdc | |||
7312e3f515 | |||
b771db882b | |||
94977797cc | |||
c45872df55 | |||
16fae5334c | |||
7b8deddeca | |||
1bf3c94358 | |||
700cc5f44c | |||
9d13cc0756 |
@ -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/
|
23
CONTRIBUTING.md
Normal file
23
CONTRIBUTING.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Contributing
|
||||||
|
Thank you for your interest in contributing! There are a couple of contribution guidelines that make it easier to apply the incoming suggestions.
|
||||||
|
|
||||||
|
If you want to contribute please start with the issues. Issues labeled with "Bug" are the higher priorities.
|
||||||
|
|
||||||
|
## Issues
|
||||||
|
1. Issues are the best place to propose a new feature.
|
||||||
|
2. If you are adding a feature that there is no issue for yet, please first open an issue and label it as "feature" and lets discuss it before you implement it.
|
||||||
|
3. Search the issues before proposing a feature to see if it is already under discussion. Referencing existing issues is a good way to increase the priority of your own.
|
||||||
|
4. We don't have an issue template yet, but the more detailed your explanation, the more quickly we'll be able to evaluate it.
|
||||||
|
5. Search for the issue that you also have. Give it a reaction (and comment, if you have something to add). We note that!
|
||||||
|
|
||||||
|
## Pull Requests
|
||||||
|
1. Open PRs represent issues that we're actively thinking about merging (at a pace we can manage). If we think a proposal needs more discussion, or that the existing code would require a lot of back-and-forth to merge, we might close it and suggest you make an issue.
|
||||||
|
2. All PRs should be made against the `main` branch. This can be changed in the future.
|
||||||
|
3. If you are making changes to the front-end layer, Please build the assets via `npm run build` and push it with the other changes.
|
||||||
|
4. Write tests for your code. Tests can be Unit or Feature.
|
||||||
|
5. Code refactors will be closed. For the architectural refactors open an issue first.
|
||||||
|
6. Use `./vendor/bin/pint` to style your code before opening a PR otherwise the actions will fail.
|
||||||
|
7. Typo fixes in documentation are welcome, but if it's at all debatable we might just close it.
|
||||||
|
|
||||||
|
## Misc
|
||||||
|
1. If you think we closed something incorrectly, feel free to (politely) tell us why! We're human and make mistakes.
|
16
README.md
16
README.md
@ -4,15 +4,29 @@ # Vito
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
[[Discord](https://discord.gg/dcUWA5DV)]
|
||||||
|
|
||||||
Better Readme will come soon... :)
|
Better Readme will come soon... :)
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
https://vitodeploy.com
|
https://vitodeploy.com
|
||||||
|
|
||||||
|
## Feedbacks
|
||||||
|
|
||||||
|
https://vitodeploy.featurebase.app
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
https://vitodeploy.featurebase.app/roadmap
|
||||||
|
|
||||||
## Contribution
|
## Contribution
|
||||||
|
|
||||||
Feel free to open a PR
|
Please read the contribution guide [Here](/CONTRIBUTING.md)
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
Please read the security policy [Here](/SECURITY.md)
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
|
11
SECURITY.md
Normal file
11
SECURITY.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
| Version | Supported |
|
||||||
|
| ------- | ------------------ |
|
||||||
|
| 0.x | :white_check_mark: |
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
If you see a vulnerability, please open an issue or report it directly to me (sa.vaziry@gmail.com)
|
@ -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'];
|
||||||
}
|
}
|
||||||
@ -118,7 +118,6 @@ private function validateInputs(array $input): void
|
|||||||
if ($input['provider'] == 'custom') {
|
if ($input['provider'] == 'custom') {
|
||||||
$rules['ip'] = [
|
$rules['ip'] = [
|
||||||
'required',
|
'required',
|
||||||
'ip',
|
|
||||||
new RestrictedIPAddressesRule(),
|
new RestrictedIPAddressesRule(),
|
||||||
];
|
];
|
||||||
$rules['port'] = [
|
$rules['port'] = [
|
||||||
|
@ -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'],
|
||||||
@ -49,11 +48,6 @@ public function create(Server $server, array $input): Site
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// detect php version
|
|
||||||
if ($site->type()->language() === 'php') {
|
|
||||||
$site->php_version = $input['php_version'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate type
|
// validate type
|
||||||
$this->validateType($site, $input);
|
$this->validateType($site, $input);
|
||||||
|
|
||||||
|
35
app/Actions/Site/UpdateSourceControl.php
Executable file
35
app/Actions/Site/UpdateSourceControl.php
Executable file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Site;
|
||||||
|
|
||||||
|
use App\Models\Site;
|
||||||
|
use Illuminate\Support\Facades\Validator;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
use Illuminate\Validation\ValidationException;
|
||||||
|
|
||||||
|
class UpdateSourceControl
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @throws ValidationException
|
||||||
|
*/
|
||||||
|
public function update(Site $site, array $input): void
|
||||||
|
{
|
||||||
|
$this->validate($input);
|
||||||
|
|
||||||
|
$site->source_control_id = $input['source_control'];
|
||||||
|
$site->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ValidationException
|
||||||
|
*/
|
||||||
|
protected function validate(array $input): void
|
||||||
|
{
|
||||||
|
Validator::make($input, [
|
||||||
|
'source_control' => [
|
||||||
|
'required',
|
||||||
|
Rule::exists('source_controls', 'id'),
|
||||||
|
],
|
||||||
|
])->validate();
|
||||||
|
}
|
||||||
|
}
|
@ -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 toEmail(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;
|
||||||
}
|
}
|
||||||
|
@ -15,13 +15,13 @@ public function create(
|
|||||||
?int $siteId = null
|
?int $siteId = null
|
||||||
): void;
|
): void;
|
||||||
|
|
||||||
public function delete(int $id, int $siteId = null): void;
|
public function delete(int $id, ?int $siteId = null): void;
|
||||||
|
|
||||||
public function restart(int $id, int $siteId = null): void;
|
public function restart(int $id, ?int $siteId = null): void;
|
||||||
|
|
||||||
public function stop(int $id, int $siteId = null): void;
|
public function stop(int $id, ?int $siteId = null): void;
|
||||||
|
|
||||||
public function start(int $id, int $siteId = null): void;
|
public function start(int $id, ?int $siteId = null): void;
|
||||||
|
|
||||||
public function getLogs(string $logPath): string;
|
public function getLogs(string $logPath): string;
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ public function credentialData(array $input): array;
|
|||||||
|
|
||||||
public function data(array $input): array;
|
public function data(array $input): array;
|
||||||
|
|
||||||
public function connect(array $credentials = null): bool;
|
public function connect(?array $credentials = null): bool;
|
||||||
|
|
||||||
public function plans(): array;
|
public function plans(): array;
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ interface SiteType
|
|||||||
{
|
{
|
||||||
public function language(): string;
|
public function language(): string;
|
||||||
|
|
||||||
|
public function supportedFeatures(): array;
|
||||||
|
|
||||||
public function createValidationRules(array $input): array;
|
public function createValidationRules(array $input): array;
|
||||||
|
|
||||||
public function createFields(array $input): array;
|
public function createFields(array $input): array;
|
||||||
|
@ -6,7 +6,7 @@ interface SourceControlProvider
|
|||||||
{
|
{
|
||||||
public function connect(): bool;
|
public function connect(): bool;
|
||||||
|
|
||||||
public function getRepo(string $repo = null): mixed;
|
public function getRepo(?string $repo = null): mixed;
|
||||||
|
|
||||||
public function fullRepoUrl(string $repo, string $key): string;
|
public function fullRepoUrl(string $repo, string $key): string;
|
||||||
|
|
||||||
|
@ -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';
|
||||||
}
|
}
|
||||||
|
16
app/Enums/SiteFeature.php
Normal file
16
app/Enums/SiteFeature.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
use BenSampo\Enum\Enum;
|
||||||
|
|
||||||
|
final class SiteFeature extends Enum
|
||||||
|
{
|
||||||
|
const DEPLOYMENT = 'deployment';
|
||||||
|
|
||||||
|
const ENV = 'env';
|
||||||
|
|
||||||
|
const SSL = 'ssl';
|
||||||
|
|
||||||
|
const QUEUES = 'queues';
|
||||||
|
}
|
@ -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';
|
||||||
|
@ -11,6 +11,4 @@ final class SourceControl extends Enum
|
|||||||
const GITLAB = 'gitlab';
|
const GITLAB = 'gitlab';
|
||||||
|
|
||||||
const BITBUCKET = 'bitbucket';
|
const BITBUCKET = 'bitbucket';
|
||||||
|
|
||||||
const CUSTOM = 'custom';
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
class SourceControlIsNotConnected extends Exception
|
class SourceControlIsNotConnected extends Exception
|
||||||
{
|
{
|
||||||
public function __construct(protected SourceControl|string $sourceControl, string $message = null)
|
public function __construct(protected SourceControl|string|null $sourceControl, ?string $message = null)
|
||||||
{
|
{
|
||||||
parent::__construct($message ?? 'Source control is not connected');
|
parent::__construct($message ?? 'Source control is not connected');
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -31,7 +31,7 @@ class SSH
|
|||||||
|
|
||||||
protected PrivateKey $privateKey;
|
protected PrivateKey $privateKey;
|
||||||
|
|
||||||
public function init(Server $server, string $asUser = null): self
|
public function init(Server $server, ?string $asUser = null): self
|
||||||
{
|
{
|
||||||
$this->connection = null;
|
$this->connection = null;
|
||||||
$this->log = null;
|
$this->log = null;
|
||||||
@ -64,11 +64,15 @@ public function setLog(string $logType, $siteId = null): void
|
|||||||
*/
|
*/
|
||||||
public function connect(bool $sftp = false): void
|
public function connect(bool $sftp = false): void
|
||||||
{
|
{
|
||||||
|
$ip = $this->server->ip;
|
||||||
|
if (str($ip)->contains(':')) {
|
||||||
|
$ip = '['.$ip.']';
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if ($sftp) {
|
if ($sftp) {
|
||||||
$this->connection = new SFTP($this->server->ip, $this->server->port);
|
$this->connection = new SFTP($ip, $this->server->port);
|
||||||
} else {
|
} else {
|
||||||
$this->connection = new SSH2($this->server->ip, $this->server->port);
|
$this->connection = new SSH2($ip, $this->server->port);
|
||||||
}
|
}
|
||||||
|
|
||||||
$login = $this->connection->login($this->user, $this->privateKey);
|
$login = $this->connection->login($this->user, $this->privateKey);
|
||||||
@ -87,7 +91,7 @@ public function connect(bool $sftp = false): void
|
|||||||
/**
|
/**
|
||||||
* @throws Throwable
|
* @throws Throwable
|
||||||
*/
|
*/
|
||||||
public function exec(string|array|SSHCommand $commands, string $log = '', int $siteId = null): string
|
public function exec(string|array|SSHCommand $commands, string $log = '', ?int $siteId = null): string
|
||||||
{
|
{
|
||||||
if ($log) {
|
if ($log) {
|
||||||
$this->setLog($log, $siteId);
|
$this->setLog($log, $siteId);
|
||||||
|
@ -32,9 +32,6 @@ public function info(string $message): void
|
|||||||
|
|
||||||
private function toast(string $type, string $message): void
|
private function toast(string $type, string $message): void
|
||||||
{
|
{
|
||||||
$this->component->dispatchBrowserEvent('toast', [
|
$this->component->dispatch('toast', type: $type, message: $message);
|
||||||
'type' => $type,
|
|
||||||
'message' => $message,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
42
app/Http/Controllers/GitHookController.php
Normal file
42
app/Http/Controllers/GitHookController.php
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Exceptions\SourceControlIsNotConnected;
|
||||||
|
use App\Facades\Notifier;
|
||||||
|
use App\Models\GitHook;
|
||||||
|
use App\Notifications\SourceControlDisconnected;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class GitHookController extends Controller
|
||||||
|
{
|
||||||
|
public function __invoke(Request $request)
|
||||||
|
{
|
||||||
|
if (! $request->input('secret')) {
|
||||||
|
abort(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var GitHook $gitHook */
|
||||||
|
$gitHook = GitHook::query()
|
||||||
|
->where('secret', $request->input('secret'))
|
||||||
|
->firstOrFail();
|
||||||
|
|
||||||
|
foreach ($gitHook->actions as $action) {
|
||||||
|
if ($action == 'deploy') {
|
||||||
|
try {
|
||||||
|
$gitHook->site->deploy();
|
||||||
|
} catch (SourceControlIsNotConnected) {
|
||||||
|
Notifier::send($gitHook->sourceControl, new SourceControlDisconnected($gitHook->sourceControl));
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
Log::error('git-hook-exception', (array) $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
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');
|
||||||
|
}
|
||||||
|
}
|
60
app/Http/Livewire/Application/AutoDeployment.php
Normal file
60
app/Http/Livewire/Application/AutoDeployment.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Application;
|
||||||
|
|
||||||
|
use App\Exceptions\SourceControlIsNotConnected;
|
||||||
|
use App\Models\Site;
|
||||||
|
use App\Traits\HasToast;
|
||||||
|
use App\Traits\RefreshComponentOnBroadcast;
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Livewire\Component;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class AutoDeployment extends Component
|
||||||
|
{
|
||||||
|
use HasToast;
|
||||||
|
use RefreshComponentOnBroadcast;
|
||||||
|
|
||||||
|
public Site $site;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Throwable
|
||||||
|
*/
|
||||||
|
public function enable(): void
|
||||||
|
{
|
||||||
|
if (! $this->site->auto_deployment) {
|
||||||
|
try {
|
||||||
|
$this->site->enableAutoDeployment();
|
||||||
|
|
||||||
|
$this->site->refresh();
|
||||||
|
|
||||||
|
$this->toast()->success(__('Auto deployment has been enabled.'));
|
||||||
|
} catch (SourceControlIsNotConnected) {
|
||||||
|
$this->toast()->error(__('Source control is not connected. Check site\'s settings.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Throwable
|
||||||
|
*/
|
||||||
|
public function disable(): void
|
||||||
|
{
|
||||||
|
if ($this->site->auto_deployment) {
|
||||||
|
try {
|
||||||
|
$this->site->disableAutoDeployment();
|
||||||
|
|
||||||
|
$this->site->refresh();
|
||||||
|
|
||||||
|
$this->toast()->success(__('Auto deployment has been disabled.'));
|
||||||
|
} catch (SourceControlIsNotConnected) {
|
||||||
|
$this->toast()->error(__('Source control is not connected. Check site\'s settings.'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render(): View
|
||||||
|
{
|
||||||
|
return view('livewire.application.auto-deployment');
|
||||||
|
}
|
||||||
|
}
|
@ -11,8 +11,8 @@
|
|||||||
|
|
||||||
class Deploy extends Component
|
class Deploy extends Component
|
||||||
{
|
{
|
||||||
use RefreshComponentOnBroadcast;
|
|
||||||
use HasToast;
|
use HasToast;
|
||||||
|
use RefreshComponentOnBroadcast;
|
||||||
|
|
||||||
public Site $site;
|
public Site $site;
|
||||||
|
|
||||||
@ -23,9 +23,9 @@ public function deploy(): void
|
|||||||
|
|
||||||
$this->toast()->success(__('Deployment started!'));
|
$this->toast()->success(__('Deployment started!'));
|
||||||
|
|
||||||
$this->emitTo(DeploymentsList::class, '$refresh');
|
$this->dispatch('$refresh')->to(DeploymentsList::class);
|
||||||
|
|
||||||
$this->emitTo(DeploymentScript::class, '$refresh');
|
$this->dispatch('$refresh')->to(DeploymentScript::class);
|
||||||
} catch (SourceControlIsNotConnected $e) {
|
} catch (SourceControlIsNotConnected $e) {
|
||||||
session()->flash('toast.type', 'error');
|
session()->flash('toast.type', 'error');
|
||||||
session()->flash('toast.message', $e->getMessage());
|
session()->flash('toast.message', $e->getMessage());
|
||||||
|
@ -27,7 +27,8 @@ public function save(): void
|
|||||||
|
|
||||||
session()->flash('status', 'script-updated');
|
session()->flash('status', 'script-updated');
|
||||||
|
|
||||||
$this->emitTo(Deploy::class, '$refresh');
|
$this->dispatch('$refresh')->to(Deploy::class);
|
||||||
|
$this->dispatch('$refresh')->to(AutoDeployment::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -10,8 +10,8 @@
|
|||||||
|
|
||||||
class DeploymentsList extends Component
|
class DeploymentsList extends Component
|
||||||
{
|
{
|
||||||
use RefreshComponentOnBroadcast;
|
|
||||||
use HasCustomPaginationView;
|
use HasCustomPaginationView;
|
||||||
|
use RefreshComponentOnBroadcast;
|
||||||
|
|
||||||
public Site $site;
|
public Site $site;
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ public function showLog(int $id): void
|
|||||||
$deployment = $this->site->deployments()->findOrFail($id);
|
$deployment = $this->site->deployments()->findOrFail($id);
|
||||||
$this->logContent = $deployment->log->content;
|
$this->logContent = $deployment->log->content;
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('open-modal', 'show-log');
|
$this->dispatch('open-modal', 'show-log');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -27,7 +27,7 @@ public function save(): void
|
|||||||
|
|
||||||
session()->flash('status', 'updating-env');
|
session()->flash('status', 'updating-env');
|
||||||
|
|
||||||
$this->emit(Deploy::class, '$refresh');
|
$this->dispatch('$refresh')->to(Deploy::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,7 @@ public function render(): View
|
|||||||
$event = Cache::get('broadcast');
|
$event = Cache::get('broadcast');
|
||||||
if ($event) {
|
if ($event) {
|
||||||
Cache::forget('broadcast');
|
Cache::forget('broadcast');
|
||||||
$this->emit('broadcast', $event);
|
$this->dispatch('broadcast', $event);
|
||||||
}
|
}
|
||||||
|
|
||||||
return view('livewire.broadcast');
|
return view('livewire.broadcast');
|
||||||
|
@ -22,9 +22,9 @@ public function create(): void
|
|||||||
{
|
{
|
||||||
app(\App\Actions\CronJob\CreateCronJob::class)->create($this->server, $this->all());
|
app(\App\Actions\CronJob\CreateCronJob::class)->create($this->server, $this->all());
|
||||||
|
|
||||||
$this->emitTo(CronjobsList::class, '$refresh');
|
$this->dispatch('$refresh')->to(CronjobsList::class);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('created', true);
|
$this->dispatch('created');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -23,7 +23,7 @@ public function delete(): void
|
|||||||
|
|
||||||
$this->refreshComponent([]);
|
$this->refreshComponent([]);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('confirmed', true);
|
$this->dispatch('confirmed');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -45,7 +45,7 @@ public function restore(): void
|
|||||||
|
|
||||||
$this->refreshComponent([]);
|
$this->refreshComponent([]);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('restored', true);
|
$this->dispatch('restored');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete(): void
|
public function delete(): void
|
||||||
@ -55,7 +55,7 @@ public function delete(): void
|
|||||||
|
|
||||||
$file->delete();
|
$file->delete();
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('confirmed', true);
|
$this->dispatch('confirmed');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -40,7 +40,7 @@ public function create(): void
|
|||||||
|
|
||||||
$this->refreshComponent([]);
|
$this->refreshComponent([]);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('backup-created', true);
|
$this->dispatch('backup-created');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function files(int $id): void
|
public function files(int $id): void
|
||||||
@ -48,7 +48,7 @@ public function files(int $id): void
|
|||||||
$backup = Backup::query()->findOrFail($id);
|
$backup = Backup::query()->findOrFail($id);
|
||||||
$this->backup = $backup;
|
$this->backup = $backup;
|
||||||
$this->files = $backup->files()->orderByDesc('id')->simplePaginate(1);
|
$this->files = $backup->files()->orderByDesc('id')->simplePaginate(1);
|
||||||
$this->dispatchBrowserEvent('show-files', true);
|
$this->dispatch('show-files');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function backup(): void
|
public function backup(): void
|
||||||
@ -57,7 +57,7 @@ public function backup(): void
|
|||||||
|
|
||||||
$this->files = $this->backup?->files()->orderByDesc('id')->simplePaginate();
|
$this->files = $this->backup?->files()->orderByDesc('id')->simplePaginate();
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('backup-running', true);
|
$this->dispatch('backup-running');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete(): void
|
public function delete(): void
|
||||||
@ -67,7 +67,7 @@ public function delete(): void
|
|||||||
|
|
||||||
$backup->delete();
|
$backup->delete();
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('confirmed', true);
|
$this->dispatch('confirmed');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -40,7 +40,7 @@ public function create(): void
|
|||||||
|
|
||||||
$this->refreshComponent([]);
|
$this->refreshComponent([]);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('database-created', true);
|
$this->dispatch('database-created');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete(): void
|
public function delete(): void
|
||||||
@ -52,9 +52,9 @@ public function delete(): void
|
|||||||
|
|
||||||
$this->refreshComponent([]);
|
$this->refreshComponent([]);
|
||||||
|
|
||||||
$this->emitTo(DatabaseUserList::class, '$refresh');
|
$this->dispatch('$refresh')->to(DatabaseUserList::class);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('confirmed', true);
|
$this->dispatch('confirmed');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -38,7 +38,7 @@ public function create(): void
|
|||||||
|
|
||||||
$this->refreshComponent([]);
|
$this->refreshComponent([]);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('database-user-created', true);
|
$this->dispatch('database-user-created');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete(): void
|
public function delete(): void
|
||||||
@ -50,9 +50,9 @@ public function delete(): void
|
|||||||
|
|
||||||
$this->refreshComponent([]);
|
$this->refreshComponent([]);
|
||||||
|
|
||||||
$this->emitTo(DatabaseList::class, '$refresh');
|
$this->dispatch('$refresh')->to(DatabaseList::class);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('confirmed', true);
|
$this->dispatch('confirmed');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function viewPassword(int $id): void
|
public function viewPassword(int $id): void
|
||||||
@ -62,7 +62,7 @@ public function viewPassword(int $id): void
|
|||||||
|
|
||||||
$this->viewPassword = $databaseUser->password;
|
$this->viewPassword = $databaseUser->password;
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('open-modal', 'database-user-password');
|
$this->dispatch('open-modal', 'database-user-password');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function showLink(int $id): void
|
public function showLink(int $id): void
|
||||||
@ -73,7 +73,7 @@ public function showLink(int $id): void
|
|||||||
$this->linkId = $id;
|
$this->linkId = $id;
|
||||||
$this->link = $databaseUser->databases ?? [];
|
$this->link = $databaseUser->databases ?? [];
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('open-modal', 'link-database-user');
|
$this->dispatch('open-modal', 'link-database-user');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function link(): void
|
public function link(): void
|
||||||
@ -85,7 +85,7 @@ public function link(): void
|
|||||||
|
|
||||||
$this->refreshComponent([]);
|
$this->refreshComponent([]);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('linked', true);
|
$this->dispatch('linked');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -28,9 +28,9 @@ public function create(): void
|
|||||||
{
|
{
|
||||||
app(CreateRule::class)->create($this->server, $this->all());
|
app(CreateRule::class)->create($this->server, $this->all());
|
||||||
|
|
||||||
$this->emitTo(FirewallRulesList::class, '$refresh');
|
$this->dispatch('$refresh')->to(FirewallRulesList::class);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('created', true);
|
$this->dispatch('created');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -25,7 +25,7 @@ public function delete(): void
|
|||||||
|
|
||||||
$this->refreshComponent([]);
|
$this->refreshComponent([]);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('confirmed', true);
|
$this->dispatch('confirmed');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -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(
|
||||||
@ -22,9 +26,9 @@ public function add(): void
|
|||||||
$this->all()
|
$this->all()
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->emitTo(ChannelsList::class, '$refresh');
|
$this->dispatch('$refresh')->to(ChannelsList::class);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('added', true);
|
$this->dispatch('added');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -25,7 +25,7 @@ public function delete(): void
|
|||||||
|
|
||||||
$this->refreshComponent([]);
|
$this->refreshComponent([]);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('confirmed', true);
|
$this->dispatch('confirmed');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -3,11 +3,13 @@
|
|||||||
namespace App\Http\Livewire\Php;
|
namespace App\Http\Livewire\Php;
|
||||||
|
|
||||||
use App\Actions\PHP\InstallNewPHP;
|
use App\Actions\PHP\InstallNewPHP;
|
||||||
|
use App\Actions\PHP\InstallPHPExtension;
|
||||||
use App\Actions\PHP\UpdatePHPIni;
|
use App\Actions\PHP\UpdatePHPIni;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use App\SSHCommands\PHP\GetPHPIniCommand;
|
use App\SSHCommands\PHP\GetPHPIniCommand;
|
||||||
use App\Traits\RefreshComponentOnBroadcast;
|
use App\Traits\RefreshComponentOnBroadcast;
|
||||||
|
use Exception;
|
||||||
use Illuminate\Contracts\View\View;
|
use Illuminate\Contracts\View\View;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
@ -24,6 +26,10 @@ class InstalledVersions extends Component
|
|||||||
|
|
||||||
public string $ini = 'Loading php.ini';
|
public string $ini = 'Loading php.ini';
|
||||||
|
|
||||||
|
public ?int $extensionId = null;
|
||||||
|
|
||||||
|
public string $extension = '';
|
||||||
|
|
||||||
public function install(string $version): void
|
public function install(string $version): void
|
||||||
{
|
{
|
||||||
app(InstallNewPHP::class)->install($this->server, [
|
app(InstallNewPHP::class)->install($this->server, [
|
||||||
@ -35,6 +41,7 @@ public function install(string $version): void
|
|||||||
|
|
||||||
public function restart(int $id): void
|
public function restart(int $id): void
|
||||||
{
|
{
|
||||||
|
/** @var Service $service */
|
||||||
$service = Service::query()->findOrFail($id);
|
$service = Service::query()->findOrFail($id);
|
||||||
$service->restart();
|
$service->restart();
|
||||||
|
|
||||||
@ -43,12 +50,13 @@ public function restart(int $id): void
|
|||||||
|
|
||||||
public function uninstall(): void
|
public function uninstall(): void
|
||||||
{
|
{
|
||||||
|
/** @var Service $service */
|
||||||
$service = Service::query()->findOrFail($this->uninstallId);
|
$service = Service::query()->findOrFail($this->uninstallId);
|
||||||
$service->uninstall();
|
$service->uninstall();
|
||||||
|
|
||||||
$this->refreshComponent([]);
|
$this->refreshComponent([]);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('confirmed', true);
|
$this->dispatch('confirmed');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loadIni(int $id): void
|
public function loadIni(int $id): void
|
||||||
@ -56,6 +64,7 @@ public function loadIni(int $id): void
|
|||||||
$this->iniId = $id;
|
$this->iniId = $id;
|
||||||
$this->ini = 'Loading php.ini';
|
$this->ini = 'Loading php.ini';
|
||||||
|
|
||||||
|
/** @var Service $service */
|
||||||
$service = Service::query()->findOrFail($this->iniId);
|
$service = Service::query()->findOrFail($this->iniId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -67,6 +76,7 @@ public function loadIni(int $id): void
|
|||||||
|
|
||||||
public function saveIni(): void
|
public function saveIni(): void
|
||||||
{
|
{
|
||||||
|
/** @var Service $service */
|
||||||
$service = Service::query()->findOrFail($this->iniId);
|
$service = Service::query()->findOrFail($this->iniId);
|
||||||
|
|
||||||
app(UpdatePHPIni::class)->update($service, $this->all()['ini']);
|
app(UpdatePHPIni::class)->update($service, $this->all()['ini']);
|
||||||
@ -76,10 +86,32 @@ public function saveIni(): void
|
|||||||
session()->flash('status', 'ini-updated');
|
session()->flash('status', 'ini-updated');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function installExtension(): void
|
||||||
|
{
|
||||||
|
/** @var Service $service */
|
||||||
|
$service = Service::query()->findOrFail($this->extensionId);
|
||||||
|
|
||||||
|
app(InstallPHPExtension::class)->handle($service, [
|
||||||
|
'name' => $this->extension,
|
||||||
|
]);
|
||||||
|
|
||||||
|
session()->flash('status', 'started-installation');
|
||||||
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
{
|
{
|
||||||
|
if ($this->extensionId) {
|
||||||
|
/** @var Service $php */
|
||||||
|
$php = Service::query()->findOrFail($this->extensionId);
|
||||||
|
$installedExtensions = $php->type_data['extensions'] ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
return view('livewire.php.installed-versions', [
|
return view('livewire.php.installed-versions', [
|
||||||
'phps' => $this->server->services()->where('type', 'php')->get(),
|
'phps' => $this->server->services()->where('type', 'php')->get(),
|
||||||
|
'installedExtensions' => $installedExtensions ?? [],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ public function submit(): void
|
|||||||
|
|
||||||
session()->flash('status', 'profile-updated');
|
session()->flash('status', 'profile-updated');
|
||||||
|
|
||||||
$this->emitTo(UserDropdown::class, '$refresh');
|
$this->dispatch('$refresh')->to(UserDropdown::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendVerificationEmail(): void
|
public function sendVerificationEmail(): void
|
||||||
|
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->dispatch('$refresh')->to(ProjectsList::class);
|
||||||
|
|
||||||
|
$this->dispatch('created');
|
||||||
|
}
|
||||||
|
|
||||||
|
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(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -24,9 +24,9 @@ public function create(): void
|
|||||||
{
|
{
|
||||||
app(\App\Actions\Queue\CreateQueue::class)->create($this->site, $this->all());
|
app(\App\Actions\Queue\CreateQueue::class)->create($this->site, $this->all());
|
||||||
|
|
||||||
$this->emitTo(QueuesList::class, '$refresh');
|
$this->dispatch('$refresh')->to(QueuesList::class);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('created', true);
|
$this->dispatch('created');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -24,7 +24,7 @@ public function delete(): void
|
|||||||
|
|
||||||
$this->refreshComponent([]);
|
$this->refreshComponent([]);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('confirmed', true);
|
$this->dispatch('confirmed');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function start(Queue $queue): void
|
public function start(Queue $queue): void
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
|
|
||||||
class LogsList extends Component
|
class LogsList extends Component
|
||||||
{
|
{
|
||||||
use RefreshComponentOnBroadcast;
|
|
||||||
use HasCustomPaginationView;
|
use HasCustomPaginationView;
|
||||||
|
use RefreshComponentOnBroadcast;
|
||||||
|
|
||||||
public ?int $count = null;
|
public ?int $count = null;
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ public function showLog(int $id): void
|
|||||||
$log = $this->server->logs()->findOrFail($id);
|
$log = $this->server->logs()->findOrFail($id);
|
||||||
$this->logContent = $log->content;
|
$this->logContent = $log->content;
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('open-modal', 'show-log');
|
$this->dispatch('open-modal', 'show-log');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -22,9 +22,9 @@ public function connect(): void
|
|||||||
{
|
{
|
||||||
app(CreateServerProvider::class)->create(auth()->user(), $this->all());
|
app(CreateServerProvider::class)->create(auth()->user(), $this->all());
|
||||||
|
|
||||||
$this->emitTo(ProvidersList::class, '$refresh');
|
$this->dispatch('$refresh')->to(ProvidersList::class);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('connected', true);
|
$this->dispatch('connected');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -25,7 +25,7 @@ public function delete(): void
|
|||||||
|
|
||||||
$this->refreshComponent([]);
|
$this->refreshComponent([]);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('confirmed', true);
|
$this->dispatch('confirmed');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -19,9 +19,9 @@ public function add(): void
|
|||||||
|
|
||||||
$key->deployTo($this->server);
|
$key->deployTo($this->server);
|
||||||
|
|
||||||
$this->emitTo(ServerKeysList::class, '$refresh');
|
$this->dispatch('$refresh')->to(ServerKeysList::class);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('added', true);
|
$this->dispatch('added');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -24,9 +24,9 @@ public function add(): void
|
|||||||
|
|
||||||
$key->deployTo($this->server);
|
$key->deployTo($this->server);
|
||||||
|
|
||||||
$this->emitTo(ServerKeysList::class, '$refresh');
|
$this->dispatch('$refresh')->to(ServerKeysList::class);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('added', true);
|
$this->dispatch('added');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -28,7 +28,7 @@ public function delete(): void
|
|||||||
|
|
||||||
$this->refreshComponent([]);
|
$this->refreshComponent([]);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('confirmed', true);
|
$this->dispatch('confirmed');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -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,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ public function refreshComponent(array $data): void
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->emit('refreshComponent');
|
$this->dispatch('refreshComponent');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -19,9 +19,9 @@ public function install(): void
|
|||||||
{
|
{
|
||||||
app(InstallPHPMyAdminAction::class)->install($this->server, $this->all());
|
app(InstallPHPMyAdminAction::class)->install($this->server, $this->all());
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('started', true);
|
$this->dispatch('started');
|
||||||
|
|
||||||
$this->emitTo(ServicesList::class, '$refresh');
|
$this->dispatch('$refresh')->to(ServicesList::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Http\Livewire\Sites;
|
namespace App\Http\Livewire\Sites;
|
||||||
|
|
||||||
use App\Enums\SiteType;
|
|
||||||
use App\Exceptions\SourceControlIsNotConnected;
|
use App\Exceptions\SourceControlIsNotConnected;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\SourceControl;
|
use App\Models\SourceControl;
|
||||||
@ -16,23 +15,12 @@ class CreateSite extends Component
|
|||||||
|
|
||||||
public Server $server;
|
public Server $server;
|
||||||
|
|
||||||
public string $type = SiteType::LARAVEL;
|
public array $inputs = [
|
||||||
|
'type' => '',
|
||||||
public string $domain;
|
'web_directory' => 'public',
|
||||||
|
'source_control' => '',
|
||||||
public string $alias;
|
'php_version' => '',
|
||||||
|
];
|
||||||
public string $php_version = '';
|
|
||||||
|
|
||||||
public string $web_directory = 'public';
|
|
||||||
|
|
||||||
public string $source_control = '';
|
|
||||||
|
|
||||||
public string $repository;
|
|
||||||
|
|
||||||
public string $branch;
|
|
||||||
|
|
||||||
public bool $composer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws SourceControlIsNotConnected
|
* @throws SourceControlIsNotConnected
|
||||||
@ -41,7 +29,7 @@ public function create(): void
|
|||||||
{
|
{
|
||||||
$site = app(\App\Actions\Site\CreateSite::class)->create(
|
$site = app(\App\Actions\Site\CreateSite::class)->create(
|
||||||
$this->server,
|
$this->server,
|
||||||
$this->all()
|
$this->inputs
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->redirect(route('servers.sites.show', [
|
$this->redirect(route('servers.sites.show', [
|
||||||
|
@ -26,7 +26,7 @@ public function refreshComponent(array $data): void
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->emit('refreshComponent');
|
$this->dispatch('refreshComponent');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
33
app/Http/Livewire/Sites/UpdateSourceControlProvider.php
Normal file
33
app/Http/Livewire/Sites/UpdateSourceControlProvider.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Sites;
|
||||||
|
|
||||||
|
use App\Actions\Site\UpdateSourceControl;
|
||||||
|
use App\Models\Site;
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class UpdateSourceControlProvider extends Component
|
||||||
|
{
|
||||||
|
public Site $site;
|
||||||
|
|
||||||
|
public $source_control = null;
|
||||||
|
|
||||||
|
public function update(): void
|
||||||
|
{
|
||||||
|
app(UpdateSourceControl::class)->update($this->site, $this->all());
|
||||||
|
|
||||||
|
$this->resetErrorBag();
|
||||||
|
|
||||||
|
session()->flash('status', 'source-control-updated');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function render(): View
|
||||||
|
{
|
||||||
|
if (! $this->source_control) {
|
||||||
|
$this->source_control = $this->site->source_control_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('livewire.sites.update-source-control-provider');
|
||||||
|
}
|
||||||
|
}
|
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');
|
||||||
|
}
|
||||||
|
}
|
@ -20,9 +20,9 @@ public function connect(): void
|
|||||||
{
|
{
|
||||||
app(ConnectSourceControl::class)->connect($this->all());
|
app(ConnectSourceControl::class)->connect($this->all());
|
||||||
|
|
||||||
$this->emitTo(SourceControlsList::class, '$refresh');
|
$this->dispatch('$refresh')->to(SourceControlsList::class);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('connected', true);
|
$this->dispatch('connected');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -25,7 +25,7 @@ public function delete(): void
|
|||||||
|
|
||||||
$this->refreshComponent([]);
|
$this->refreshComponent([]);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('confirmed', true);
|
$this->dispatch('confirmed');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -19,9 +19,9 @@ public function add(): void
|
|||||||
$this->all()
|
$this->all()
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->emitTo(KeysList::class, '$refresh');
|
$this->dispatch('$refresh')->to(KeysList::class);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('added', true);
|
$this->dispatch('added');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -25,7 +25,7 @@ public function delete(): void
|
|||||||
|
|
||||||
$this->refreshComponent([]);
|
$this->refreshComponent([]);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('confirmed', true);
|
$this->dispatch('confirmed');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -23,9 +23,9 @@ public function create(): void
|
|||||||
{
|
{
|
||||||
app(\App\Actions\SSL\CreateSSL::class)->create($this->site, $this->all());
|
app(\App\Actions\SSL\CreateSSL::class)->create($this->site, $this->all());
|
||||||
|
|
||||||
$this->emitTo(SslsList::class, '$refresh');
|
$this->dispatch('$refresh')->to(SslsList::class);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('created', true);
|
$this->dispatch('created');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -10,8 +10,8 @@
|
|||||||
|
|
||||||
class SslsList extends Component
|
class SslsList extends Component
|
||||||
{
|
{
|
||||||
use RefreshComponentOnBroadcast;
|
|
||||||
use HasToast;
|
use HasToast;
|
||||||
|
use RefreshComponentOnBroadcast;
|
||||||
|
|
||||||
public Site $site;
|
public Site $site;
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ public function delete(): void
|
|||||||
|
|
||||||
$this->refreshComponent([]);
|
$this->refreshComponent([]);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('confirmed', true);
|
$this->dispatch('confirmed');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function refreshComponent(array $data): void
|
public function refreshComponent(array $data): void
|
||||||
@ -34,7 +34,7 @@ public function refreshComponent(array $data): void
|
|||||||
$this->toast()->error(__('SSL creation failed!'));
|
$this->toast()->error(__('SSL creation failed!'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->emit('refreshComponent');
|
$this->dispatch('refreshComponent');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -32,9 +32,9 @@ public function connect(): void
|
|||||||
{
|
{
|
||||||
app(CreateStorageProvider::class)->create(auth()->user(), $this->all());
|
app(CreateStorageProvider::class)->create(auth()->user(), $this->all());
|
||||||
|
|
||||||
$this->emitTo(ProvidersList::class, '$refresh');
|
$this->dispatch('$refresh')->to(ProvidersList::class);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('connected', true);
|
$this->dispatch('connected');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -25,7 +25,7 @@ public function delete(): void
|
|||||||
|
|
||||||
$this->refreshComponent([]);
|
$this->refreshComponent([]);
|
||||||
|
|
||||||
$this->dispatchBrowserEvent('confirmed', true);
|
$this->dispatch('confirmed');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
public function render(): View
|
||||||
|
@ -13,7 +13,7 @@ class Initialize extends InstallationJob
|
|||||||
|
|
||||||
protected ?string $asUser;
|
protected ?string $asUser;
|
||||||
|
|
||||||
public function __construct(Server $server, string $asUser = null)
|
public function __construct(Server $server, ?string $asUser = null)
|
||||||
{
|
{
|
||||||
$this->server = $server->refresh();
|
$this->server = $server->refresh();
|
||||||
$this->asUser = $asUser;
|
$this->asUser = $asUser;
|
||||||
|
@ -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,
|
||||||
|
@ -52,7 +52,7 @@ public function handle(): void
|
|||||||
$this->site->id
|
$this->site->id
|
||||||
);
|
);
|
||||||
|
|
||||||
if (! Str::contains($result, 'Wordpress installed!')) {
|
if (! Str::contains($result, 'Success')) {
|
||||||
throw new FailedToInstallWordpress($result);
|
throw new FailedToInstallWordpress($result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -57,19 +57,19 @@ public function server(): BelongsTo
|
|||||||
/**
|
/**
|
||||||
* create database on server
|
* create database on server
|
||||||
*/
|
*/
|
||||||
public function createOnServer(): void
|
public function createOnServer(string $queue = 'ssh'): void
|
||||||
{
|
{
|
||||||
dispatch(new CreateOnServer($this))->onConnection('ssh');
|
dispatch(new CreateOnServer($this))->onConnection($queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* delete database from server
|
* delete database from server
|
||||||
*/
|
*/
|
||||||
public function deleteFromServer(): void
|
public function deleteFromServer(string $queue = 'ssh'): void
|
||||||
{
|
{
|
||||||
$this->status = DatabaseStatus::DELETING;
|
$this->status = DatabaseStatus::DELETING;
|
||||||
$this->save();
|
$this->save();
|
||||||
dispatch(new DeleteFromServer($this))->onConnection('ssh');
|
dispatch(new DeleteFromServer($this))->onConnection($queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function backups(): HasMany
|
public function backups(): HasMany
|
||||||
|
@ -54,17 +54,17 @@ public function scopeHasDatabase(Builder $query, string $databaseName): Builder
|
|||||||
return $query->where('databases', 'like', "%\"$databaseName\"%");
|
return $query->where('databases', 'like', "%\"$databaseName\"%");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createOnServer(): void
|
public function createOnServer(string $queue = 'ssh'): void
|
||||||
{
|
{
|
||||||
dispatch(new CreateOnServer($this))->onConnection('ssh');
|
dispatch(new CreateOnServer($this))->onConnection($queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deleteFromServer(): void
|
public function deleteFromServer(string $queue = 'ssh'): void
|
||||||
{
|
{
|
||||||
$this->status = DatabaseStatus::DELETING;
|
$this->status = DatabaseStatus::DELETING;
|
||||||
$this->save();
|
$this->save();
|
||||||
|
|
||||||
dispatch(new DeleteFromServer($this))->onConnection('ssh');
|
dispatch(new DeleteFromServer($this))->onConnection($queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function linkNewDatabase(string $name): void
|
public function linkNewDatabase(string $name): void
|
||||||
@ -79,14 +79,14 @@ public function linkNewDatabase(string $name): void
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function linkUser(): void
|
public function linkUser(string $queue = 'ssh'): void
|
||||||
{
|
{
|
||||||
dispatch(new LinkUser($this))->onConnection('ssh');
|
dispatch(new LinkUser($this))->onConnection($queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function unlinkUser(): void
|
public function unlinkUser(string $queue = 'ssh'): void
|
||||||
{
|
{
|
||||||
dispatch(new UnlinkUser($this))->onConnection('ssh');
|
dispatch(new UnlinkUser($this))->onConnection($queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFullUserAttribute(): string
|
public function getFullUserAttribute(): string
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use App\Exceptions\FailedToDeployGitHook;
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
@ -22,6 +22,8 @@
|
|||||||
*/
|
*/
|
||||||
class GitHook extends AbstractModel
|
class GitHook extends AbstractModel
|
||||||
{
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'site_id',
|
'site_id',
|
||||||
'source_control_id',
|
'source_control_id',
|
||||||
@ -55,9 +57,6 @@ public function scopeHasEvent(Builder $query, string $event): Builder
|
|||||||
return $query->where('events', 'like', "%\"{$event}\"%");
|
return $query->where('events', 'like', "%\"{$event}\"%");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws FailedToDeployGitHook
|
|
||||||
*/
|
|
||||||
public function deployHook(): void
|
public function deployHook(): void
|
||||||
{
|
{
|
||||||
$this->update(
|
$this->update(
|
||||||
@ -70,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,25 @@
|
|||||||
|
|
||||||
use App\Contracts\ServerType;
|
use App\Contracts\ServerType;
|
||||||
use App\Enums\ServerStatus;
|
use App\Enums\ServerStatus;
|
||||||
|
use App\Enums\ServiceStatus;
|
||||||
|
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 +43,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 +65,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 +89,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 +114,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 +135,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');
|
||||||
@ -213,6 +228,18 @@ public function defaultService($type): ?Service
|
|||||||
->where('is_default', 1)
|
->where('is_default', 1)
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
|
// If no default service found, get the first service with status ready or stopped
|
||||||
|
if (! $service) {
|
||||||
|
$service = $this->services()
|
||||||
|
->where('type', $type)
|
||||||
|
->whereIn('status', [ServiceStatus::READY, ServiceStatus::STOPPED])
|
||||||
|
->first();
|
||||||
|
if ($service) {
|
||||||
|
$service->is_default = 1;
|
||||||
|
$service->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $service;
|
return $service;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,10 +257,10 @@ 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
|
||||||
{
|
{
|
||||||
return SSH::init($this, $user);
|
return SSH::init($this, $user);
|
||||||
}
|
}
|
||||||
@ -263,7 +290,7 @@ public function provider(): \App\Contracts\ServerProvider
|
|||||||
return new $providerClass($this);
|
return new $providerClass($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function webserver(string $version = null): ?Service
|
public function webserver(?string $version = null): ?Service
|
||||||
{
|
{
|
||||||
if (! $version) {
|
if (! $version) {
|
||||||
return $this->defaultService('webserver');
|
return $this->defaultService('webserver');
|
||||||
@ -272,7 +299,7 @@ public function webserver(string $version = null): ?Service
|
|||||||
return $this->service('webserver', $version);
|
return $this->service('webserver', $version);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function database(string $version = null): ?Service
|
public function database(?string $version = null): ?Service
|
||||||
{
|
{
|
||||||
if (! $version) {
|
if (! $version) {
|
||||||
return $this->defaultService('database');
|
return $this->defaultService('database');
|
||||||
@ -281,7 +308,7 @@ public function database(string $version = null): ?Service
|
|||||||
return $this->service('database', $version);
|
return $this->service('database', $version);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function firewall(string $version = null): ?Service
|
public function firewall(?string $version = null): ?Service
|
||||||
{
|
{
|
||||||
if (! $version) {
|
if (! $version) {
|
||||||
return $this->defaultService('firewall');
|
return $this->defaultService('firewall');
|
||||||
@ -290,7 +317,7 @@ public function firewall(string $version = null): ?Service
|
|||||||
return $this->service('firewall', $version);
|
return $this->service('firewall', $version);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function processManager(string $version = null): ?Service
|
public function processManager(?string $version = null): ?Service
|
||||||
{
|
{
|
||||||
if (! $version) {
|
if (! $version) {
|
||||||
return $this->defaultService('process_manager');
|
return $this->defaultService('process_manager');
|
||||||
@ -299,7 +326,7 @@ public function processManager(string $version = null): ?Service
|
|||||||
return $this->service('process_manager', $version);
|
return $this->service('process_manager', $version);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function php(string $version = null): ?Service
|
public function php(?string $version = null): ?Service
|
||||||
{
|
{
|
||||||
if (! $version) {
|
if (! $version) {
|
||||||
return $this->defaultService('php');
|
return $this->defaultService('php');
|
||||||
@ -334,10 +361,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';
|
||||||
|
@ -6,12 +6,15 @@
|
|||||||
use App\Enums\DeploymentStatus;
|
use App\Enums\DeploymentStatus;
|
||||||
use App\Enums\SiteStatus;
|
use App\Enums\SiteStatus;
|
||||||
use App\Enums\SslStatus;
|
use App\Enums\SslStatus;
|
||||||
use App\Exceptions\FailedToDeployGitHook;
|
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;
|
||||||
@ -19,7 +22,8 @@
|
|||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -341,30 +345,24 @@ public function getWebDirectoryPathAttribute(): string
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws SourceControlIsNotConnected
|
* @throws SourceControlIsNotConnected
|
||||||
* @throws ValidationException
|
|
||||||
* @throws FailedToDeployGitHook
|
|
||||||
* @throws Throwable
|
* @throws Throwable
|
||||||
*/
|
*/
|
||||||
public function enableAutoDeployment(): void
|
public function enableAutoDeployment(): void
|
||||||
{
|
{
|
||||||
if ($this->gitHook) {
|
if ($this->gitHook) {
|
||||||
throw ValidationException::withMessages([
|
return;
|
||||||
'auto_deployment' => __('Auto deployment already enabled'),
|
|
||||||
])->errorBag('auto_deployment');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! $this->sourceControl()) {
|
if (! $this->sourceControl()) {
|
||||||
throw ValidationException::withMessages([
|
throw new SourceControlIsNotConnected($this->source_control);
|
||||||
'auto_deployment' => __('Your application does not use any source controls'),
|
|
||||||
])->errorBag('auto_deployment');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
'secret' => generate_uid(),
|
'secret' => Str::uuid()->toString(),
|
||||||
'actions' => ['deploy'],
|
'actions' => ['deploy'],
|
||||||
'events' => ['push'],
|
'events' => ['push'],
|
||||||
]);
|
]);
|
||||||
@ -399,4 +397,49 @@ public function getSshKeyNameAttribute(): string
|
|||||||
{
|
{
|
||||||
return str('site_'.$this->id)->toString();
|
return str('site_'.$this->id)->toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function installationFinished(): void
|
||||||
|
{
|
||||||
|
$this->update([
|
||||||
|
'status' => SiteStatus::READY,
|
||||||
|
'progress' => 100,
|
||||||
|
]);
|
||||||
|
event(
|
||||||
|
new Broadcast('install-site-finished', [
|
||||||
|
'site' => $this,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
Notifier::send($this, new SiteInstallationSucceed($this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Throwable
|
||||||
|
*/
|
||||||
|
public function installationFailed(Throwable $e): void
|
||||||
|
{
|
||||||
|
$this->update([
|
||||||
|
'status' => SiteStatus::INSTALLATION_FAILED,
|
||||||
|
]);
|
||||||
|
event(
|
||||||
|
new Broadcast('install-site-failed', [
|
||||||
|
'site' => $this,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
Notifier::send($this, new SiteInstallationFailed($this));
|
||||||
|
Log::error('install-site-error', [
|
||||||
|
'error' => (string) $e,
|
||||||
|
]);
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasFeature(string $feature): bool
|
||||||
|
{
|
||||||
|
return in_array($feature, $this->type()->supportedFeatures());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isReady(): bool
|
||||||
|
{
|
||||||
|
return $this->status === SiteStatus::READY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ public function provider(): SourceControlProvider
|
|||||||
return new $providerClass($this);
|
return new $providerClass($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRepo(string $repo = null): ?array
|
public function getRepo(?string $repo = null): ?array
|
||||||
{
|
{
|
||||||
return $this->provider()->getRepo($repo);
|
return $this->provider()->getRepo($repo);
|
||||||
}
|
}
|
||||||
|
@ -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,30 @@
|
|||||||
|
|
||||||
namespace App\NotificationChannels;
|
namespace App\NotificationChannels;
|
||||||
|
|
||||||
|
use App\Contracts\Notification;
|
||||||
|
use App\Models\NotificationChannel;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
class Discord extends AbstractProvider
|
class Discord extends AbstractNotificationChannel
|
||||||
{
|
{
|
||||||
public function validationRules(): array
|
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 +33,39 @@ 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
|
||||||
|
{
|
||||||
|
/** @var NotificationChannel $notifiable */
|
||||||
|
$this->notificationChannel = $notifiable;
|
||||||
|
$data = $this->notificationChannel->data;
|
||||||
|
Http::post($data['webhook_url'], [
|
||||||
|
'content' => $notification->toSlack($notifiable),
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,36 +2,58 @@
|
|||||||
|
|
||||||
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(
|
||||||
|
'Connected to VitoDeploy',
|
||||||
|
'This email confirms that you have connected your email to VitoDeploy.'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} 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->toEmail($notifiable);
|
||||||
|
Mail::to($this->data()['email'])->send(
|
||||||
|
new NotificationMail($message->subject, $message->render())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,21 +2,30 @@
|
|||||||
|
|
||||||
namespace App\NotificationChannels;
|
namespace App\NotificationChannels;
|
||||||
|
|
||||||
|
use App\Contracts\Notification;
|
||||||
|
use App\Models\NotificationChannel;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
class Slack extends AbstractProvider
|
class Slack extends AbstractNotificationChannel
|
||||||
{
|
{
|
||||||
public function validationRules(): array
|
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 +33,39 @@ 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
|
||||||
|
{
|
||||||
|
/** @var NotificationChannel $notifiable */
|
||||||
|
$this->notificationChannel = $notifiable;
|
||||||
|
$data = $this->notificationChannel->data;
|
||||||
|
Http::post($data['webhook_url'], [
|
||||||
|
'text' => $notification->toSlack($notifiable),
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
65
app/NotificationChannels/Telegram.php
Normal file
65
app/NotificationChannels/Telegram.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\NotificationChannels;
|
||||||
|
|
||||||
|
use App\Contracts\Notification;
|
||||||
|
use App\Models\NotificationChannel;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class Telegram extends AbstractNotificationChannel
|
||||||
|
{
|
||||||
|
protected string $apiUrl = 'https://api.telegram.org/bot';
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
/** @var NotificationChannel $notifiable */
|
||||||
|
$this->notificationChannel = $notifiable;
|
||||||
|
$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,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user