Compare commits

...

12 Commits

Author SHA1 Message Date
debcd002f1 Update README.md (#110) 2024-02-23 16:48:32 +01:00
59e8c82e5c Making the whole app into one docker image for easier usage (#104) 2024-02-23 15:28:41 +01:00
682da0d6d5 load the actual env from the server 2024-02-22 20:30:21 +01:00
9db310a06b fix notifications (#109) 2024-02-16 21:54:51 +01:00
f70963d6bb fix notification chanels and more tests (#108)
* fix notification chanels and more tests

* fix code style
2024-02-16 21:10:17 +01:00
b75df8e1c5 temp - disable notifier 2024-02-05 00:33:06 +01:00
a22e9cb946 temp - disable notifications on installation 2024-02-05 00:23:44 +01:00
b2b9bea0b1 temp - disable ufw validation 2024-02-05 00:12:55 +01:00
3f4a2bce3a fix (#106) 2024-02-05 00:07:44 +01:00
8bffefabef Upgrade to Livewire 3 (#103)
* upgrade to livewire 3

* fix updater

* fix modal events

* fix modal events
2024-02-04 18:11:22 +01:00
3da1f4fe4c fix server not having default service (#100) 2024-01-30 20:45:17 +01:00
2214a76e09 Update README.md (#97) 2024-01-29 14:56:28 +01:00
224 changed files with 1454 additions and 503 deletions

38
.env.docker Executable file
View File

@ -0,0 +1,38 @@
APP_NAME=Vito
APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=http://localhost
LOG_CHANNEL=stack
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=vito
DB_USERNAME=root
DB_PASSWORD=password
BROADCAST_DRIVER=null
CACHE_DRIVER=file
FILESYSTEM_DRIVER=local
QUEUE_CONNECTION=default
SESSION_DRIVER=database
SESSION_LIFETIME=120
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=
MAIL_PORT=
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
SSH_PUBLIC_KEY_NAME=ssh-public.key
SSH_PRIVATE_KEY_NAME=ssh-private.pem

65
Dockerfile Normal file
View File

@ -0,0 +1,65 @@
FROM ubuntu:22.04
WORKDIR /var/www/html
ENV DEBIAN_FRONTEND noninteractive
RUN echo "mysql-server mysql-server/root_password password password" | debconf-set-selections
RUN echo "mysql-server mysql-server/root_password_again password password" | debconf-set-selections
# upgrade
RUN apt clean && apt update && apt update && apt upgrade -y && apt autoremove -y
# requirements
RUN apt install -y software-properties-common curl zip unzip git gcc
# nginx
RUN apt install -y nginx
# php
RUN apt update \
&& apt install -y gnupg gosu curl ca-certificates zip unzip git supervisor libcap2-bin libpng-dev \
python2 dnsutils librsvg2-bin fswatch wget \
&& add-apt-repository ppa:ondrej/php -y \
&& apt update \
&& apt install -y php8.1 php8.1-fpm php8.1-mbstring php8.1-mysql php8.1-mcrypt php8.1-gd php8.1-xml \
php8.1-curl php8.1-gettext php8.1-zip php8.1-bcmath php8.1-soap php8.1-redis
COPY docker/standalone/php.ini /etc/php/8.1/cli/conf.d/99-vito.ini
# composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# mysql
RUN wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.22-1_all.deb \
&& mkdir -p /etc/apt/keyrings \
&& apt clean \
&& apt update \
&& dpkg -i mysql-apt-config_0.8.22-1_all.deb \
&& apt install mysql-server -y
RUN service mysql stop
# app
COPY . /var/www/html
RUN rm -rf /var/www/html/vendor
RUN rm -rf /var/www/html/.env
RUN composer install --no-dev --prefer-dist
RUN chown -R www-data:www-data /var/www/html \
&& chmod -R 755 /var/www/html/storage /var/www/html/bootstrap/cache
# webserver
RUN rm /etc/nginx/sites-available/default
RUN rm /etc/nginx/sites-enabled/default
COPY docker/standalone/nginx.conf /etc/nginx/sites-available/default
RUN ln -s /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default
# supervisord
COPY docker/standalone/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
# start
COPY docker/standalone/start.sh /start.sh
RUN chmod +x /start.sh
EXPOSE 80 3306
CMD ["/start.sh"]

View File

@ -1,36 +1,49 @@
# Vito
![](https://github.com/vitodeploy/vito/workflows/tests/badge.svg)
<p align="center">
<img alt="srcshot 2024-02-23 at 16 26 21@2x" src="https://github.com/vitodeploy/vito/assets/61919774/c22842ff-b458-443c-90b7-a98148b0d49e" alt="VitoDeploy>
<p align="center">
<a href="https://github.com/vitodeploy/vito/actions"><img alt="GitHub Workflow Status" src="https://github.com/vitodeploy/vito/workflows/tests/badge.svg"></a>
</p>
</p>
![image](https://github.com/vitodeploy/vito/assets/61919774/687d50e5-8a61-41b5-b708-752567e30aed)
------
## About Vito
Better Readme will come soon... :)
Vito is a self-hosted web application that helps you manage your servers and deploy your PHP applications into production servers without a hassle.
## Documentation
## Features
https://vitodeploy.com
- Provisions and Manages the server
- Easy database management, Supports Mysql and MariaDB
- Deploy your PHP applications such as Laravel
- Manage your server's firewall
- Supports Custom and Letsencrypt SSL
- Uses supervisor to handle queues
- Manages server's services
- Deploy your SSH Keys to the server
- Create and Manage cron jobs on the server
## Feedbacks
## Useful Links
https://vitodeploy.featurebase.app
## Roadmap
https://vitodeploy.featurebase.app/roadmap
## Contribution
Please read the contribution guide [Here](/CONTRIBUTING.md)
## Security
Please read the security policy [Here](/SECURITY.md)
- [Documentation](https://vitodeploy.com)
- [Feedbacks](https://vitodeploy.featurebase.app)
- [Roadmap](https://vitodeploy.featurebase.app/roadmap)
- [Discord](https://discord.gg/dcUWA5DV)
- [Contribution](/CONTRIBUTING.md)
- [Security](/SECURITY.md)
## Credits
- Laravel
- Tailwind
- Tailwindcss
- Livewire
- Alpinejs
- Vite
- Laravel Enum by BenSampo
- Log Viewer by Arunas Skirius
- PHPSecLib
- Laravel Blade Icons
- Guzzlehttp
- Owenvoke for `owenvoke/blade-fontawesome`
- Axios
- Toastr by CodeSeven

0
app/Actions/Site/UpdateEnv.php Executable file → Normal file
View File

View File

@ -8,7 +8,7 @@ interface Notification
{
public function rawText(): string;
public function toMail(object $notifiable): MailMessage;
public function toEmail(object $notifiable): MailMessage;
public function toSlack(object $notifiable): string;

View File

@ -8,7 +8,7 @@
class Notifier
{
/**
* In the future we can send notifications based on the notifiable instance
* 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.
*/

View File

@ -32,9 +32,6 @@ public function info(string $message): void
private function toast(string $type, string $message): void
{
$this->component->dispatchBrowserEvent('toast', [
'type' => $type,
'message' => $message,
]);
$this->component->dispatch('toast', type: $type, message: $message);
}
}

View File

@ -23,9 +23,9 @@ public function deploy(): void
$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) {
session()->flash('toast.type', 'error');
session()->flash('toast.message', $e->getMessage());

View File

@ -27,8 +27,8 @@ public function save(): void
session()->flash('status', 'script-updated');
$this->emitTo(Deploy::class, '$refresh');
$this->emitTo(AutoDeployment::class, '$refresh');
$this->dispatch('$refresh')->to(Deploy::class);
$this->dispatch('$refresh')->to(AutoDeployment::class);
}
public function render(): View

View File

@ -22,7 +22,7 @@ public function showLog(int $id): void
$deployment = $this->site->deployments()->findOrFail($id);
$this->logContent = $deployment->log->content;
$this->dispatchBrowserEvent('open-modal', 'show-log');
$this->dispatch('open-modal', 'show-log');
}
public function render(): View

View File

@ -14,11 +14,11 @@ class Env extends Component
public Site $site;
public string $env;
public string $env = 'Loading...';
public function mount(): void
public function loadEnv(): void
{
$this->env = $this->site->env;
$this->env = $this->site->getEnv();
}
public function save(): void
@ -27,7 +27,7 @@ public function save(): void
session()->flash('status', 'updating-env');
$this->emit(Deploy::class, '$refresh');
$this->dispatch('$refresh')->to(Deploy::class);
}
public function render(): View

View File

@ -13,7 +13,7 @@ public function render(): View
$event = Cache::get('broadcast');
if ($event) {
Cache::forget('broadcast');
$this->emit('broadcast', $event);
$this->dispatch('broadcast', $event);
}
return view('livewire.broadcast');

View File

@ -22,9 +22,9 @@ public function create(): void
{
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

View File

@ -23,7 +23,7 @@ public function delete(): void
$this->refreshComponent([]);
$this->dispatchBrowserEvent('confirmed', true);
$this->dispatch('confirmed');
}
public function render(): View

View File

@ -45,7 +45,7 @@ public function restore(): void
$this->refreshComponent([]);
$this->dispatchBrowserEvent('restored', true);
$this->dispatch('restored');
}
public function delete(): void
@ -55,7 +55,7 @@ public function delete(): void
$file->delete();
$this->dispatchBrowserEvent('confirmed', true);
$this->dispatch('confirmed');
}
public function render(): View

View File

@ -40,7 +40,7 @@ public function create(): void
$this->refreshComponent([]);
$this->dispatchBrowserEvent('backup-created', true);
$this->dispatch('backup-created');
}
public function files(int $id): void
@ -48,7 +48,7 @@ public function files(int $id): void
$backup = Backup::query()->findOrFail($id);
$this->backup = $backup;
$this->files = $backup->files()->orderByDesc('id')->simplePaginate(1);
$this->dispatchBrowserEvent('show-files', true);
$this->dispatch('show-files');
}
public function backup(): void
@ -57,7 +57,7 @@ public function backup(): void
$this->files = $this->backup?->files()->orderByDesc('id')->simplePaginate();
$this->dispatchBrowserEvent('backup-running', true);
$this->dispatch('backup-running');
}
public function delete(): void
@ -67,7 +67,7 @@ public function delete(): void
$backup->delete();
$this->dispatchBrowserEvent('confirmed', true);
$this->dispatch('confirmed');
}
public function render(): View

View File

@ -40,7 +40,7 @@ public function create(): void
$this->refreshComponent([]);
$this->dispatchBrowserEvent('database-created', true);
$this->dispatch('database-created');
}
public function delete(): void
@ -52,9 +52,9 @@ public function delete(): void
$this->refreshComponent([]);
$this->emitTo(DatabaseUserList::class, '$refresh');
$this->dispatch('$refresh')->to(DatabaseUserList::class);
$this->dispatchBrowserEvent('confirmed', true);
$this->dispatch('confirmed');
}
public function render(): View

View File

@ -38,7 +38,7 @@ public function create(): void
$this->refreshComponent([]);
$this->dispatchBrowserEvent('database-user-created', true);
$this->dispatch('database-user-created');
}
public function delete(): void
@ -50,9 +50,9 @@ public function delete(): void
$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
@ -62,7 +62,7 @@ public function viewPassword(int $id): void
$this->viewPassword = $databaseUser->password;
$this->dispatchBrowserEvent('open-modal', 'database-user-password');
$this->dispatch('open-modal', 'database-user-password');
}
public function showLink(int $id): void
@ -73,7 +73,7 @@ public function showLink(int $id): void
$this->linkId = $id;
$this->link = $databaseUser->databases ?? [];
$this->dispatchBrowserEvent('open-modal', 'link-database-user');
$this->dispatch('open-modal', 'link-database-user');
}
public function link(): void
@ -85,7 +85,7 @@ public function link(): void
$this->refreshComponent([]);
$this->dispatchBrowserEvent('linked', true);
$this->dispatch('linked');
}
public function render(): View

View File

@ -28,9 +28,9 @@ public function create(): void
{
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

View File

@ -25,7 +25,7 @@ public function delete(): void
$this->refreshComponent([]);
$this->dispatchBrowserEvent('confirmed', true);
$this->dispatch('confirmed');
}
public function render(): View

View File

@ -26,9 +26,9 @@ public function add(): void
$this->all()
);
$this->emitTo(ChannelsList::class, '$refresh');
$this->dispatch('$refresh')->to(ChannelsList::class);
$this->dispatchBrowserEvent('added', true);
$this->dispatch('added');
}
public function render(): View

View File

@ -25,7 +25,7 @@ public function delete(): void
$this->refreshComponent([]);
$this->dispatchBrowserEvent('confirmed', true);
$this->dispatch('confirmed');
}
public function render(): View

View File

@ -56,7 +56,7 @@ public function uninstall(): void
$this->refreshComponent([]);
$this->dispatchBrowserEvent('confirmed', true);
$this->dispatch('confirmed');
}
public function loadIni(int $id): void

View File

@ -33,7 +33,7 @@ public function submit(): void
session()->flash('status', 'profile-updated');
$this->emitTo(UserDropdown::class, '$refresh');
$this->dispatch('$refresh')->to(UserDropdown::class);
}
public function sendVerificationEmail(): void

View File

@ -21,9 +21,9 @@ public function create(): void
app(\App\Actions\Projects\CreateProject::class)
->create(auth()->user(), $this->inputs);
$this->emitTo(ProjectsList::class, '$refresh');
$this->dispatch('$refresh')->to(ProjectsList::class);
$this->dispatchBrowserEvent('created', true);
$this->dispatch('created');
}
public function render(): View

View File

@ -24,9 +24,9 @@ public function create(): void
{
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

View File

@ -24,7 +24,7 @@ public function delete(): void
$this->refreshComponent([]);
$this->dispatchBrowserEvent('confirmed', true);
$this->dispatch('confirmed');
}
public function start(Queue $queue): void

View File

@ -27,7 +27,7 @@ public function showLog(int $id): void
$log = $this->server->logs()->findOrFail($id);
$this->logContent = $log->content;
$this->dispatchBrowserEvent('open-modal', 'show-log');
$this->dispatch('open-modal', 'show-log');
}
public function render(): View

View File

@ -22,9 +22,9 @@ public function connect(): void
{
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

View File

@ -25,7 +25,7 @@ public function delete(): void
$this->refreshComponent([]);
$this->dispatchBrowserEvent('confirmed', true);
$this->dispatch('confirmed');
}
public function render(): View

View File

@ -19,9 +19,9 @@ public function add(): void
$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

View File

@ -24,9 +24,9 @@ public function add(): void
$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

View File

@ -28,7 +28,7 @@ public function delete(): void
$this->refreshComponent([]);
$this->dispatchBrowserEvent('confirmed', true);
$this->dispatch('confirmed');
}
public function render(): View

View File

@ -21,7 +21,7 @@ public function refreshComponent(array $data): void
return;
}
$this->emit('refreshComponent');
$this->dispatch('refreshComponent');
}
public function render(): View

View File

@ -19,9 +19,9 @@ public function install(): void
{
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

View File

@ -26,7 +26,7 @@ public function refreshComponent(array $data): void
return;
}
$this->emit('refreshComponent');
$this->dispatch('refreshComponent');
}
public function render(): View

View File

@ -20,9 +20,9 @@ public function connect(): void
{
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

View File

@ -25,7 +25,7 @@ public function delete(): void
$this->refreshComponent([]);
$this->dispatchBrowserEvent('confirmed', true);
$this->dispatch('confirmed');
}
public function render(): View

View File

@ -19,9 +19,9 @@ public function add(): void
$this->all()
);
$this->emitTo(KeysList::class, '$refresh');
$this->dispatch('$refresh')->to(KeysList::class);
$this->dispatchBrowserEvent('added', true);
$this->dispatch('added');
}
public function render(): View

View File

@ -25,7 +25,7 @@ public function delete(): void
$this->refreshComponent([]);
$this->dispatchBrowserEvent('confirmed', true);
$this->dispatch('confirmed');
}
public function render(): View

View File

@ -23,9 +23,9 @@ public function create(): void
{
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

View File

@ -25,7 +25,7 @@ public function delete(): void
$this->refreshComponent([]);
$this->dispatchBrowserEvent('confirmed', true);
$this->dispatch('confirmed');
}
public function refreshComponent(array $data): void
@ -34,7 +34,7 @@ public function refreshComponent(array $data): void
$this->toast()->error(__('SSL creation failed!'));
}
$this->emit('refreshComponent');
$this->dispatch('refreshComponent');
}
public function render(): View

View File

@ -32,9 +32,9 @@ public function connect(): void
{
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

View File

@ -25,7 +25,7 @@ public function delete(): void
$this->refreshComponent([]);
$this->dispatchBrowserEvent('confirmed', true);
$this->dispatch('confirmed');
}
public function render(): View

View File

@ -26,9 +26,7 @@ public function handle(): void
new EditFileCommand(
$this->site->path.'/.env',
$this->site->env
),
'update-env',
$this->site->id
)
);
event(
new Broadcast('deploy-site-env-finished', [

View File

@ -4,6 +4,7 @@
use App\Contracts\ServerType;
use App\Enums\ServerStatus;
use App\Enums\ServiceStatus;
use App\Facades\Notifier;
use App\Facades\SSH;
use App\Jobs\Installation\Upgrade;
@ -227,6 +228,18 @@ public function defaultService($type): ?Service
->where('is_default', 1)
->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;
}

View File

@ -15,6 +15,7 @@
use App\Jobs\Site\UpdateBranch;
use App\Notifications\SiteInstallationFailed;
use App\Notifications\SiteInstallationSucceed;
use App\SSHCommands\Website\GetEnvCommand;
use Exception;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@ -442,4 +443,13 @@ public function isReady(): bool
{
return $this->status === SiteStatus::READY;
}
public function getEnv(): string
{
return $this->server->ssh()->exec(
new GetEnvCommand(
$this->domain
)
);
}
}

View File

@ -3,15 +3,11 @@
namespace App\NotificationChannels;
use App\Contracts\Notification;
use App\Models\NotificationChannel;
use Illuminate\Support\Facades\Http;
class Discord extends AbstractNotificationChannel
{
public function channel(): string
{
return 'discord';
}
public function createRules(array $input): array
{
return [
@ -65,6 +61,8 @@ private function checkConnection(string $subject, string $text): bool
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),

View File

@ -35,7 +35,10 @@ public function connect(): bool
{
try {
Mail::to($this->data()['email'])->send(
new NotificationMail('Test VitoDeploy', 'This is a test email!')
new NotificationMail(
'Connected to VitoDeploy',
'This email confirms that you have connected your email to VitoDeploy.'
)
);
} catch (Throwable) {
return false;
@ -48,8 +51,7 @@ public function send(object $notifiable, Notification $notification): void
{
/** @var NotificationChannel $notifiable */
$this->notificationChannel = $notifiable;
$message = $notification->toMail($notifiable);
$message = $notification->toEmail($notifiable);
Mail::to($this->data()['email'])->send(
new NotificationMail($message->subject, $message->render())
);

View File

@ -3,15 +3,11 @@
namespace App\NotificationChannels;
use App\Contracts\Notification;
use App\Models\NotificationChannel;
use Illuminate\Support\Facades\Http;
class Slack extends AbstractNotificationChannel
{
public function channel(): string
{
return 'slack';
}
public function createRules(array $input): array
{
return [
@ -65,6 +61,8 @@ private function checkConnection(string $subject, string $text): bool
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),

View File

@ -3,6 +3,7 @@
namespace App\NotificationChannels;
use App\Contracts\Notification;
use App\Models\NotificationChannel;
use Illuminate\Support\Facades\Http;
use Throwable;
@ -10,11 +11,6 @@ class Telegram extends AbstractNotificationChannel
{
protected string $apiUrl = 'https://api.telegram.org/bot';
public function channel(): string
{
return 'telegram';
}
public function createRules(array $input): array
{
return [
@ -52,6 +48,8 @@ public function connect(): bool
public function send(object $notifiable, Notification $notification): void
{
/** @var NotificationChannel $notifiable */
$this->notificationChannel = $notifiable;
$this->sendToTelegram($notification->toTelegram($notifiable));
}

View File

@ -3,6 +3,7 @@
namespace App\Notifications;
use App\Contracts\Notification as NotificationInterface;
use App\Models\NotificationChannel;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
@ -13,9 +14,16 @@ abstract class AbstractNotification extends Notification implements Notification
{
use Queueable, SerializesModels;
public function toMail(object $notifiable): MailMessage
public function via(object $notifiable): string
{
/** @var NotificationChannel $notifiable */
return get_class($notifiable->provider());
}
public function toEmail(object $notifiable): MailMessage
{
return (new MailMessage())
->subject('Notification')
->line($this->rawText());
}

View File

@ -25,7 +25,7 @@ public function rawText(): string
]);
}
public function toMail(object $notifiable): MailMessage
public function toEmail(object $notifiable): MailMessage
{
return (new MailMessage)
->subject(__('Failed to delete the server from the provider!'))

View File

@ -21,7 +21,7 @@ public function rawText(): string
]);
}
public function toMail(object $notifiable): MailMessage
public function toEmail(object $notifiable): MailMessage
{
return (new MailMessage)
->subject(__('Server disconnected!'))

View File

@ -22,7 +22,7 @@ public function rawText(): string
]);
}
public function toMail(object $notifiable): MailMessage
public function toEmail(object $notifiable): MailMessage
{
return (new MailMessage)
->subject(__('Server installation failed!'))

View File

@ -22,7 +22,7 @@ public function rawText(): string
]);
}
public function toMail(object $notifiable): MailMessage
public function toEmail(object $notifiable): MailMessage
{
return (new MailMessage)
->subject(__('Server installation started!'))

View File

@ -32,7 +32,7 @@ public function rawText(): string
]);
}
public function toMail(object $notifiable): MailMessage
public function toEmail(object $notifiable): MailMessage
{
$this->server->refresh();

View File

@ -19,7 +19,7 @@ public function rawText(): string
]);
}
public function toMail(object $notifiable): MailMessage
public function toEmail(object $notifiable): MailMessage
{
return (new MailMessage)
->subject(__('Site installation failed!'))

View File

@ -18,7 +18,7 @@ public function rawText(): string
]);
}
public function toMail(object $notifiable): MailMessage
public function toEmail(object $notifiable): MailMessage
{
return (new MailMessage)
->subject(__('Site installation succeed!'))

View File

@ -18,7 +18,7 @@ public function rawText(): string
]);
}
public function toMail(object $notifiable): MailMessage
public function toEmail(object $notifiable): MailMessage
{
return (new MailMessage)
->subject(__('Source control disconnected!'))

View File

@ -0,0 +1,26 @@
<?php
namespace App\SSHCommands\Website;
use App\SSHCommands\Command;
use Illuminate\Support\Facades\File;
class GetEnvCommand extends Command
{
public function __construct(
protected string $domain
) {
}
public function file(): string
{
return File::get(resource_path('commands/website/get-env.sh'));
}
public function content(): string
{
return str($this->file())
->replace('__domain__', $this->domain)
->toString();
}
}

View File

@ -163,7 +163,7 @@ protected function addSupervisor(): void
}
/**
* add supervisor
* add redis
*/
protected function addRedis(): void
{
@ -175,7 +175,7 @@ protected function addRedis(): void
}
/**
* add supervisor
* add ufw
*/
protected function addUfw(): void
{

View File

@ -15,6 +15,6 @@ public function getListeners(): array
public function refreshComponent(array $data): void
{
$this->emit('refreshComponent');
$this->dispatch('refreshComponent');
}
}

View File

@ -18,8 +18,8 @@
"laravel/sanctum": "^3.2",
"laravel/socialite": "^5.2",
"laravel/tinker": "^2.8",
"livewire/livewire": "^2.12",
"opcodesio/log-viewer": "^2.5",
"livewire/livewire": "^3.0",
"opcodesio/log-viewer": "^3.0",
"owenvoke/blade-fontawesome": "^2.5",
"phpseclib/phpseclib": "~3.0"
},

115
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "b3f98cafe7fcc5d3ce67ad09a8f66661",
"content-hash": "f2e6a21fc0abada9bc40b4e80df42b26",
"packages": [
{
"name": "aws/aws-crt-php",
@ -2931,34 +2931,36 @@
},
{
"name": "livewire/livewire",
"version": "v2.12.6",
"version": "v3.4.4",
"source": {
"type": "git",
"url": "https://github.com/livewire/livewire.git",
"reference": "7d3a57b3193299cf1a0639a3935c696f4da2cf92"
"reference": "c0489d4a76382f6dcf6e2702112f86aa089d0c8d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/livewire/livewire/zipball/7d3a57b3193299cf1a0639a3935c696f4da2cf92",
"reference": "7d3a57b3193299cf1a0639a3935c696f4da2cf92",
"url": "https://api.github.com/repos/livewire/livewire/zipball/c0489d4a76382f6dcf6e2702112f86aa089d0c8d",
"reference": "c0489d4a76382f6dcf6e2702112f86aa089d0c8d",
"shasum": ""
},
"require": {
"illuminate/database": "^7.0|^8.0|^9.0|^10.0",
"illuminate/support": "^7.0|^8.0|^9.0|^10.0",
"illuminate/validation": "^7.0|^8.0|^9.0|^10.0",
"illuminate/database": "^10.0|^11.0",
"illuminate/routing": "^10.0|^11.0",
"illuminate/support": "^10.0|^11.0",
"illuminate/validation": "^10.0|^11.0",
"league/mime-type-detection": "^1.9",
"php": "^7.2.5|^8.0",
"symfony/http-kernel": "^5.0|^6.0"
"php": "^8.1",
"symfony/http-kernel": "^6.2|^7.0"
},
"require-dev": {
"calebporzio/sushi": "^2.1",
"laravel/framework": "^7.0|^8.0|^9.0|^10.0",
"laravel/framework": "^10.0|^11.0",
"laravel/prompts": "^0.1.6",
"mockery/mockery": "^1.3.1",
"orchestra/testbench": "^5.0|^6.0|^7.0|^8.0",
"orchestra/testbench-dusk": "^5.2|^6.0|^7.0|^8.0",
"phpunit/phpunit": "^8.4|^9.0",
"psy/psysh": "@stable"
"orchestra/testbench": "8.20.0|^9.0",
"orchestra/testbench-dusk": "8.20.0|^9.0",
"phpunit/phpunit": "^10.4",
"psy/psysh": "^0.11.22|^0.12"
},
"type": "library",
"extra": {
@ -2992,7 +2994,7 @@
"description": "A front-end framework for Laravel.",
"support": {
"issues": "https://github.com/livewire/livewire/issues",
"source": "https://github.com/livewire/livewire/tree/v2.12.6"
"source": "https://github.com/livewire/livewire/tree/v3.4.4"
},
"funding": [
{
@ -3000,7 +3002,7 @@
"type": "github"
}
],
"time": "2023-08-11T04:02:34+00:00"
"time": "2024-01-28T19:07:11+00:00"
},
{
"name": "monolog/monolog",
@ -3568,20 +3570,21 @@
},
{
"name": "opcodesio/log-viewer",
"version": "v2.5.6",
"version": "v3.4.0",
"source": {
"type": "git",
"url": "https://github.com/opcodesio/log-viewer.git",
"reference": "34619b89ec0501222a661863e80dc2c92618d8f3"
"reference": "f1d89dc2e54e186f6852533a165fc49a6a83fff8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opcodesio/log-viewer/zipball/34619b89ec0501222a661863e80dc2c92618d8f3",
"reference": "34619b89ec0501222a661863e80dc2c92618d8f3",
"url": "https://api.github.com/repos/opcodesio/log-viewer/zipball/f1d89dc2e54e186f6852533a165fc49a6a83fff8",
"reference": "f1d89dc2e54e186f6852533a165fc49a6a83fff8",
"shasum": ""
},
"require": {
"illuminate/contracts": "^8.0|^9.0|^10.0",
"illuminate/contracts": "^8.0|^9.0|^10.0|^11.0",
"opcodesio/mail-parser": "^0.1.6",
"php": "^8.0"
},
"conflict": {
@ -3591,11 +3594,10 @@
"guzzlehttp/guzzle": "^7.2",
"itsgoingd/clockwork": "^5.1",
"laravel/pint": "^1.0",
"nunomaduro/collision": "^6.0",
"orchestra/testbench": "^7.6|^8.0",
"pestphp/pest": "^1.21",
"pestphp/pest-plugin-laravel": "^1.1",
"phpunit/phpunit": "^9.5",
"nunomaduro/collision": "^7.0|^8.0",
"orchestra/testbench": "^7.6|^8.0|^9.0",
"pestphp/pest": "^2.0",
"pestphp/pest-plugin-laravel": "^2.0",
"spatie/test-time": "^1.3"
},
"suggest": {
@ -3640,7 +3642,7 @@
],
"support": {
"issues": "https://github.com/opcodesio/log-viewer/issues",
"source": "https://github.com/opcodesio/log-viewer/tree/v2.5.6"
"source": "https://github.com/opcodesio/log-viewer/tree/v3.4.0"
},
"funding": [
{
@ -3652,7 +3654,60 @@
"type": "github"
}
],
"time": "2023-09-03T08:22:57+00:00"
"time": "2024-02-14T15:14:59+00:00"
},
{
"name": "opcodesio/mail-parser",
"version": "v0.1.6",
"source": {
"type": "git",
"url": "https://github.com/opcodesio/mail-parser.git",
"reference": "639ef31cbd146a63416283e75afce152e13233ea"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opcodesio/mail-parser/zipball/639ef31cbd146a63416283e75afce152e13233ea",
"reference": "639ef31cbd146a63416283e75afce152e13233ea",
"shasum": ""
},
"require": {
"php": "^8.0"
},
"require-dev": {
"pestphp/pest": "^2.16",
"symfony/var-dumper": "^6.3"
},
"type": "library",
"autoload": {
"psr-4": {
"Opcodes\\MailParser\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Arunas Skirius",
"email": "arukomp@gmail.com",
"role": "Developer"
}
],
"description": "Parse emails without the mailparse extension",
"keywords": [
"arukompas",
"email",
"email parser",
"mail",
"opcodesio",
"php"
],
"support": {
"issues": "https://github.com/opcodesio/mail-parser/issues",
"source": "https://github.com/opcodesio/mail-parser/tree/v0.1.6"
},
"time": "2023-11-19T08:47:43+00:00"
},
{
"name": "owenvoke/blade-fontawesome",
@ -9901,5 +9956,5 @@
"ext-ftp": "*"
},
"platform-dev": [],
"plugin-api-version": "2.3.0"
"plugin-api-version": "2.6.0"
}

159
config/livewire.php Normal file
View File

@ -0,0 +1,159 @@
<?php
return [
/*
|---------------------------------------------------------------------------
| Class Namespace
|---------------------------------------------------------------------------
|
| This value sets the root class namespace for Livewire component classes in
| your application. This value will change where component auto-discovery
| finds components. It's also referenced by the file creation commands.
|
*/
'class_namespace' => 'App\\Http\\Livewire',
/*
|---------------------------------------------------------------------------
| View Path
|---------------------------------------------------------------------------
|
| This value is used to specify where Livewire component Blade templates are
| stored when running file creation commands like `artisan make:livewire`.
| It is also used if you choose to omit a component's render() method.
|
*/
'view_path' => resource_path('views/livewire'),
/*
|---------------------------------------------------------------------------
| Layout
|---------------------------------------------------------------------------
| The view that will be used as the layout when rendering a single component
| as an entire page via `Route::get('/post/create', CreatePost::class);`.
| In this case, the view returned by CreatePost will render into $slot.
|
*/
'layout' => 'layouts.app',
/*
|---------------------------------------------------------------------------
| Lazy Loading Placeholder
|---------------------------------------------------------------------------
| Livewire allows you to lazy load components that would otherwise slow down
| the initial page load. Every component can have a custom placeholder or
| you can define the default placeholder view for all components below.
|
*/
'lazy_placeholder' => null,
/*
|---------------------------------------------------------------------------
| Temporary File Uploads
|---------------------------------------------------------------------------
|
| Livewire handles file uploads by storing uploads in a temporary directory
| before the file is stored permanently. All file uploads are directed to
| a global endpoint for temporary storage. You may configure this below:
|
*/
'temporary_file_upload' => [
'disk' => null, // Example: 'local', 's3' | Default: 'default'
'rules' => null, // Example: ['file', 'mimes:png,jpg'] | Default: ['required', 'file', 'max:12288'] (12MB)
'directory' => null, // Example: 'tmp' | Default: 'livewire-tmp'
'middleware' => null, // Example: 'throttle:5,1' | Default: 'throttle:60,1'
'preview_mimes' => [ // Supported file types for temporary pre-signed file URLs...
'png', 'gif', 'bmp', 'svg', 'wav', 'mp4',
'mov', 'avi', 'wmv', 'mp3', 'm4a',
'jpg', 'jpeg', 'mpga', 'webp', 'wma',
],
'max_upload_time' => 5, // Max duration (in minutes) before an upload is invalidated...
],
/*
|---------------------------------------------------------------------------
| Render On Redirect
|---------------------------------------------------------------------------
|
| This value determines if Livewire will run a component's `render()` method
| after a redirect has been triggered using something like `redirect(...)`
| Setting this to true will render the view once more before redirecting
|
*/
'render_on_redirect' => false,
/*
|---------------------------------------------------------------------------
| Eloquent Model Binding
|---------------------------------------------------------------------------
|
| Previous versions of Livewire supported binding directly to eloquent model
| properties using wire:model by default. However, this behavior has been
| deemed too "magical" and has therefore been put under a feature flag.
|
*/
'legacy_model_binding' => false,
/*
|---------------------------------------------------------------------------
| Auto-inject Frontend Assets
|---------------------------------------------------------------------------
|
| By default, Livewire automatically injects its JavaScript and CSS into the
| <head> and <body> of pages containing Livewire components. By disabling
| this behavior, you need to use @livewireStyles and @livewireScripts.
|
*/
'inject_assets' => true,
/*
|---------------------------------------------------------------------------
| Navigate (SPA mode)
|---------------------------------------------------------------------------
|
| By adding `wire:navigate` to links in your Livewire application, Livewire
| will prevent the default link handling and instead request those pages
| via AJAX, creating an SPA-like effect. Configure this behavior here.
|
*/
'navigate' => [
'show_progress_bar' => true,
'progress_bar_color' => '#2299dd',
],
/*
|---------------------------------------------------------------------------
| HTML Morph Markers
|---------------------------------------------------------------------------
|
| Livewire intelligently "morphs" existing HTML into the newly rendered HTML
| after each update. To make this process more reliable, Livewire injects
| "markers" into the rendered Blade surrounding @if, @class & @foreach.
|
*/
'inject_morph_markers' => true,
/*
|---------------------------------------------------------------------------
| Pagination Theme
|---------------------------------------------------------------------------
|
| When enabling Livewire's pagination feature by using the `WithPagination`
| trait, Livewire will use Tailwind templates to render pagination views
| on the page. If you want Bootstrap CSS, you can specify: "bootstrap"
|
*/
'pagination_theme' => 'tailwind',
];

View File

@ -1,7 +1,5 @@
<?php
use Opcodes\LogViewer\Level;
return [
/*
@ -14,6 +12,8 @@
'enabled' => env('LOG_VIEWER_ENABLED', true),
'require_auth_in_production' => true,
/*
|--------------------------------------------------------------------------
| Log Viewer Domain
@ -135,6 +135,19 @@
'include_files' => [
'*.log',
'**/*.log',
// You can include paths to other log types as well, such as apache, nginx, and more.
'/var/log/httpd/*',
'/var/log/nginx/*',
// MacOS Apple Silicon logs
'/opt/homebrew/var/log/nginx/*',
'/opt/homebrew/var/log/httpd/*',
'/opt/homebrew/var/log/php-fpm.log',
'/opt/homebrew/var/log/postgres*log',
'/opt/homebrew/var/log/redis*log',
'/opt/homebrew/var/log/supervisor*log',
// '/absolute/paths/supported',
],
@ -150,6 +163,18 @@
// 'my_secret.log'
],
/*
|--------------------------------------------------------------------------
| Hide unknown files.
|--------------------------------------------------------------------------
| The include/exclude options above might catch files which are not
| logs supported by Log Viewer. In that case, you can hide them
| from the UI and API calls by setting this to true.
|
*/
'hide_unknown_files' => true,
/*
|--------------------------------------------------------------------------
| Shorter stack trace filters.
@ -165,35 +190,6 @@
'/vendor/barryvdh/laravel-debugbar/',
],
/*
|--------------------------------------------------------------------------
| Log matching patterns
|--------------------------------------------------------------------------
| Regexes for matching log files
|
*/
'patterns' => [
'laravel' => [
'log_matching_regex' => '/^\[(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}\.?(\d{6}([\+-]\d\d:\d\d)?)?)\].*/',
/**
* This pattern, used for processing Laravel logs, returns these results:
* $matches[0] - the full log line being tested.
* $matches[1] - full timestamp between the square brackets (includes microseconds and timezone offset)
* $matches[2] - timestamp microseconds, if available
* $matches[3] - timestamp timezone offset, if available
* $matches[4] - contents between timestamp and the severity level
* $matches[5] - environment (local, production, etc)
* $matches[6] - log severity (info, debug, error, etc)
* $matches[7] - the log text, the rest of the text.
*/
'log_parsing_regex' => '/^\[(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}\.?(\d{6}([\+-]\d\d:\d\d)?)?)\](.*?(\w+)\.|.*?)('
.implode('|', array_filter(Level::caseValues()))
.')?: (.*?)( in [\/].*?:[0-9]+)?$/is',
],
],
/*
|--------------------------------------------------------------------------
| Cache driver
@ -214,4 +210,6 @@
*/
'lazy_scan_chunk_size_in_mb' => 50,
'strip_extracted_context' => true,
];

View File

@ -0,0 +1,28 @@
server {
listen 80;
listen [::]:80;
server_name _;
root /var/www/html/public;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
index index.php;
charset utf-8;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
}

View File

@ -0,0 +1,3 @@
[PHP]
post_max_size = 100M
upload_max_filesize = 100M

View File

@ -0,0 +1,61 @@
#!/bin/bash
INIT_FLAG="/var/www/html/storage/.INIT_ENV"
MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-"password"}
NAME=${NAME:-"vito"}
EMAIL=${EMAIL:-"vito@example.com"}
PASSWORD=${PASSWORD:-"password"}
# Check if the flag file does not exist, indicating a first run
if [ ! -f "$INIT_FLAG" ]; then
echo "First run of the container. Initializing MySQL..."
# Start MySQL temporarily
service mysql start
# Wait for MySQL to start up completely (may need to adjust the sleep duration)
sleep 3
# Create Database
mysql -u root -p`echo password` -e "CREATE DATABASE IF NOT EXISTS vito CHARACTER SET utf8 COLLATE utf8_general_ci;"
# Change Password
mysql -u root -p`echo password` -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '$MYSQL_ROOT_PASSWORD'; FLUSH PRIVILEGES;"
cp /var/www/html/.env.docker /var/www/html/.env
# Modify /var/www/html/.env and set DB_PASSWORD=password to DB_PASSWORD=MYSQL_ROOT_PASSWORD
sed -i "s/DB_PASSWORD=password/DB_PASSWORD=$MYSQL_ROOT_PASSWORD/g" /var/www/html/.env
php /var/www/html/artisan key:generate
php /var/www/html/artisan migrate --force
php /var/www/html/artisan user:create "$NAME" "$EMAIL" "$PASSWORD"
openssl genpkey -algorithm RSA -out /var/www/html/storage/ssh-private.pem
chmod 600 /var/www/html/storage/ssh-private.pem
ssh-keygen -y -f /var/www/html/storage/ssh-private.pem > /var/www/html/storage/ssh-public.key
# Create the flag file to indicate completion of initialization tasks
touch "$INIT_FLAG"
fi
service mysql start
service php8.1-fpm start
service nginx start
php /var/www/html/artisan migrate --force
php /var/www/html/artisan config:clear
php /var/www/html/artisan config:cache
php /var/www/html/artisan route:clear
php /var/www/html/artisan route:cache
php /var/www/html/artisan view:clear
php /var/www/html/artisan view:cache
php /var/www/html/artisan icons:cache
echo "Vito is running! 🚀"
/usr/bin/supervisord

View File

@ -0,0 +1,16 @@
[supervisord]
nodaemon=true
user=root
logfile=/var/log/supervisor/supervisord.log
pidfile=/var/run/supervisord.pid
redirect_stderr=true
[program:worker]
user=root
autostart=1
autorestart=1
numprocs=1
command=/usr/bin/php /var/www/html/artisan queue:work --sleep=3 --backoff=0 --queue=default,ssh,ssh-long --timeout=3600 --tries=1
redirect_stderr=true
stdout_logfile=/var/www/html/storage/logs/worker.log
stopwaitsecs=3600

View File

@ -2,9 +2,7 @@
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"queue:listen": "php artisan queue:listen --timeout=600 --queue=default,ssh,ssh-long",
"scheduler:run": "php artisan schedule:run"
"build": "vite build"
},
"devDependencies": {
"@ryangjchandler/alpine-clipboard": "^2.2.0",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,11 +1,11 @@
{
"resources/css/app.css": {
"file": "assets/app-ae945733.css",
"file": "assets/app-887de6f7.css",
"isEntry": true,
"src": "resources/css/app.css"
},
"resources/js/app.js": {
"file": "assets/app-9aa488bb.js",
"file": "assets/app-e6b0cd9c.js",
"isEntry": true,
"src": "resources/js/app.js"
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,8 @@
* @license MIT
*/
/*! #__NO_SIDE_EFFECTS__ */
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */

View File

@ -1,6 +1,6 @@
{
"/app.js": "/app.js?id=5f574f36f456b103dffcfa21d5612785",
"/app.css": "/app.css?id=b701a4344131bb2c00e9f0b1ef1ab3c1",
"/app.js": "/app.js?id=fc139aeb14d759b4a2906fc46d6c8150",
"/app.css": "/app.css?id=12c5e78a91987c11d8a0cc04436541e9",
"/img/log-viewer-128.png": "/img/log-viewer-128.png?id=d576c6d2e16074d3f064e60fe4f35166",
"/img/log-viewer-32.png": "/img/log-viewer-32.png?id=f8ec67d10f996aa8baf00df3b61eea6d",
"/img/log-viewer-64.png": "/img/log-viewer-64.png?id=8902d596fc883ca9eb8105bb683568c6"

View File

@ -0,0 +1 @@
[ -f /home/vito/__domain__/.env ] && cat /home/vito/__domain__/.env

View File

@ -1,24 +1,11 @@
import './bootstrap';
// import Echo from "laravel-echo"
// import Pusher from "pusher-js"
import Alpine from 'alpinejs';
import Clipboard from "@ryangjchandler/alpine-clipboard";
import { Livewire, Alpine } from '../../vendor/livewire/livewire/dist/livewire.esm';
Alpine.plugin(Clipboard)
Alpine.directive('clipboard', (el) => {
let text = el.textContent
// window.Echo = new Echo({
// broadcaster: 'pusher',
// key: 'app-key',
// wsHost: 'localhost',
// wsPort: 6001,
// cluster: '',
// forceTLS: false,
// disableStats: true,
// });
//
// window.Pusher = Pusher;
window.Alpine = Alpine;
Alpine.start();
el.addEventListener('click', () => {
navigator.clipboard.writeText(text)
})
})
Livewire.start()

View File

@ -1,7 +1,7 @@
@props(['name', 'input', 'title', 'description', 'method'])
<x-modal :name="$name">
<form wire:submit.prevent="{{ $method }}" class="p-6">
<form wire:submit="{{ $method }}" class="p-6">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('Confirm') }}
</h2>

View File

@ -189,15 +189,15 @@ class="min-h-screen w-64 flex-none border-r border-gray-200 bg-white dark:border
</div>
<x-toast />
<livewire:broadcast />
@livewireScripts
@livewireScriptConfig
<script>
document.addEventListener('livewire:load', () => {
Livewire.onPageExpired((response, message) => {
({
href: window.location.href
} = window.location);
})
})
// document.addEventListener('livewire:init', () => {
// Livewire.onPageExpired((response, message) => {
// ({
// href: window.location.href
// } = window.location);
// })
// })
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia(

View File

@ -4,7 +4,6 @@
if (this.theme === 'dark') {
return true
}
console.log(window.matchMedia('(prefers-color-scheme: dark)'))
return this.theme === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches;
},
changeTheme(theme) {

View File

@ -1,13 +1,13 @@
<div x-data="">
<x-modal name="change-branch">
<form wire:submit.prevent="change" class="p-6">
<form wire:submit="change" class="p-6">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('Change Branch') }}
</h2>
<div class="mt-6">
<x-input-label for="branch" :value="__('Branch')" />
<x-text-input wire:model.defer="branch" id="branch" name="branch" type="text" class="mt-1 w-full" />
<x-text-input wire:model="branch" id="branch" name="branch" type="text" class="mt-1 w-full" />
@error('branch')
<x-input-error class="mt-2" :messages="$message" />
@enderror

View File

@ -1,13 +1,13 @@
<div x-data="">
<x-modal name="deployment-script">
<form wire:submit.prevent="save" class="p-6">
<form wire:submit="save" class="p-6">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('Deployment Script') }}
</h2>
<div class="mt-6">
<x-input-label for="script" :value="__('Script')" />
<x-textarea wire:model.defer="script" rows="10" id="script" name="script" class="mt-1 w-full" />
<x-textarea wire:model="script" rows="10" id="script" name="script" class="mt-1 w-full" />
@error('script')
<x-input-error class="mt-2" :messages="$message" />
@enderror

View File

@ -1,13 +1,13 @@
<div x-data="">
<x-modal name="update-env">
<form wire:submit.prevent="save" class="p-6">
<form wire:submit="save" class="p-6">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('Update .env File') }}
</h2>
<div class="mt-6">
<x-input-label for="env" :value="__('.env')" />
<x-textarea wire:model.defer="env" rows="10" id="env" name="env" class="mt-1 w-full" />
<x-textarea id="env" wire:init="loadEnv" wire:model="env" rows="10" class="mt-1 block w-full"></x-textarea>
@error('env')
<x-input-error class="mt-2" :messages="$message" />
@enderror

View File

@ -4,14 +4,14 @@
</x-primary-button>
<x-modal name="create-cronjob">
<form wire:submit.prevent="create" class="p-6">
<form wire:submit="create" class="p-6">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('Create Cronjob') }}
</h2>
<div class="mt-6">
<x-input-label for="command" :value="__('Command')" />
<x-text-input wire:model.defer="command" id="command" name="command" type="text" class="mt-1 w-full" />
<x-text-input wire:model="command" id="command" name="command" type="text" class="mt-1 w-full" />
@error('command')
<x-input-error class="mt-2" :messages="$message" />
@enderror
@ -19,7 +19,7 @@
<div class="mt-6">
<x-input-label for="user" :value="__('User')" />
<x-select-input wire:model.defer="user" id="user" name="user" class="mt-1 w-full">
<x-select-input wire:model="user" id="user" name="user" class="mt-1 w-full">
<option value="" selected disabled>{{ __("Select") }}</option>
<option value="root" @if($user === 'root') selected @endif>root</option>
<option value="{{ $server->ssh_user }}" @if($user === $server->ssh_user) selected @endif>{{ $server->ssh_user }}</option>
@ -31,7 +31,7 @@
<div class="mt-6">
<x-input-label for="frequency" :value="__('Frequency')" />
<x-select-input wire:model="frequency" id="frequency" name="frequency" class="mt-1 w-full">
<x-select-input wire:model.live="frequency" id="frequency" name="frequency" class="mt-1 w-full">
<option value="" selected disabled>{{ __("Select") }}</option>
<option value="* * * * *" @if($frequency === '* * * * *') selected @endif>{{ __("Every minute") }}</option>
<option value="0 * * * *" @if($frequency === '0 * * * *') selected @endif>{{ __("Hourly") }}</option>
@ -48,7 +48,7 @@
@if($frequency === 'custom')
<div class="mt-6">
<x-input-label for="custom" :value="__('Custom Frequency')" />
<x-text-input wire:model.defer="custom" id="custom" name="custom" type="text" class="mt-1 w-full" placeholder="* * * * *" />
<x-text-input wire:model="custom" id="custom" name="custom" type="text" class="mt-1 w-full" placeholder="* * * * *" />
@error('custom')
<x-input-error class="mt-2" :messages="$message" />
@enderror

View File

@ -1,12 +1,12 @@
<x-modal name="create-backup">
<form wire:submit.prevent="create" class="p-6" x-data="{user: false, remote: false}">
<form wire:submit="create" class="p-6" x-data="{user: false, remote: false}">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('Create Backup') }}
</h2>
<div class="mt-6">
<x-input-label for="database" :value="__('Database')" />
<x-select-input wire:model="database" id="database" name="database" class="mt-1 w-full">
<x-select-input wire:model.live="database" id="database" name="database" class="mt-1 w-full">
<option value="" selected disabled>{{ __("Select") }}</option>
@foreach($databases as $db)
<option value="{{ $db->id }}" @if($database == $db->id) selected @endif>{{ $db->name }}</option>
@ -19,7 +19,7 @@
<div class="mt-6">
<x-input-label for="storage" :value="__('Storage')" />
<x-select-input wire:model="storage" id="storage" name="storage" class="mt-1 w-full">
<x-select-input wire:model.live="storage" id="storage" name="storage" class="mt-1 w-full">
<option value="" selected disabled>{{ __("Select") }}</option>
@foreach(auth()->user()->storageProviders as $st)
<option value="{{ $st->id }}" @if($storage == $st->id) selected @endif>{{ $st->profile }} - {{ $st->provider }}</option>
@ -33,7 +33,7 @@
<div class="mt-6">
<x-input-label for="interval" :value="__('Interval')" />
<x-select-input wire:model="interval" id="interval" name="interval" class="mt-1 w-full">
<x-select-input wire:model.live="interval" id="interval" name="interval" class="mt-1 w-full">
<option value="" selected disabled>{{ __("Select") }}</option>
<option value="0 * * * *" @if($interval === '0 * * * *') selected @endif>{{ __("Hourly") }}</option>
<option value="0 0 * * *" @if($interval === '0 0 * * *') selected @endif>{{ __("Daily") }}</option>
@ -50,7 +50,7 @@
@if($interval === 'custom')
<div class="mt-6">
<x-input-label for="custom" :value="__('Custom interval (Cron)')" />
<x-text-input wire:model.defer="custom" id="custom" name="custom" type="text" class="mt-1 w-full" placeholder="* * * * *" />
<x-text-input wire:model="custom" id="custom" name="custom" type="text" class="mt-1 w-full" placeholder="* * * * *" />
@error('custom')
<x-input-error class="mt-2" :messages="$message" />
@enderror
@ -59,7 +59,7 @@
<div class="mt-6">
<x-input-label for="keep" :value="__('Backups to Keep')" />
<x-text-input wire:model.defer="keep" id="keep" name="keep" type="text" class="mt-1 w-full" />
<x-text-input wire:model="keep" id="keep" name="keep" type="text" class="mt-1 w-full" />
@error('keep')
<x-input-error class="mt-2" :messages="$message" />
@enderror

View File

@ -1,12 +1,12 @@
<x-modal name="create-database">
<form wire:submit.prevent="create" class="p-6" x-data="{user: false, remote: false}">
<form wire:submit="create" class="p-6" x-data="{user: false, remote: false}">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('Create Database') }}
</h2>
<div class="mt-6">
<x-input-label for="name" :value="__('Name')" />
<x-text-input wire:model.defer="name" id="name" name="name" type="text" class="mt-1 w-full" />
<x-text-input wire:model="name" id="name" name="name" type="text" class="mt-1 w-full" />
@error('name')
<x-input-error class="mt-2" :messages="$message" />
@enderror
@ -14,7 +14,7 @@
<div class="mt-6">
<label for="create_user" class="inline-flex items-center">
<input id="create_user" wire:model.defer="user" type="checkbox" x-model="user" class="rounded dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800" name="create_user">
<input id="create_user" wire:model="user" type="checkbox" x-model="user" class="rounded dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800" name="create_user">
<span class="ml-2 text-sm text-gray-600 dark:text-gray-400">{{ __('Create a user for this database') }}</span>
</label>
</div>
@ -22,7 +22,7 @@
<div x-show="user">
<div class="mt-6">
<x-input-label for="db-username" :value="__('Username')" />
<x-text-input wire:model.defer="username" id="db-username" name="username" type="text" class="mt-1 w-full" />
<x-text-input wire:model="username" id="db-username" name="username" type="text" class="mt-1 w-full" />
@error('username')
<x-input-error class="mt-2" :messages="$message" />
@enderror
@ -30,7 +30,7 @@
<div class="mt-6">
<x-input-label for="db-password" :value="__('Password')" />
<x-text-input wire:model.defer="password" id="db-password" name="password" type="text" class="mt-1 w-full" />
<x-text-input wire:model="password" id="db-password" name="password" type="text" class="mt-1 w-full" />
@error('password')
<x-input-error class="mt-2" :messages="$message" />
@enderror
@ -38,7 +38,7 @@
<div class="mt-6">
<label for="db-remote" class="inline-flex items-center">
<input id="db-remote" wire:model="remote" type="checkbox" x-model="remote" class="rounded dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800" name="remote">
<input id="db-remote" wire:model.live="remote" type="checkbox" x-model="remote" class="rounded dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800" name="remote">
<span class="ml-2 text-sm text-gray-600 dark:text-gray-400">{{ __('Enable remote access') }}</span>
</label>
</div>
@ -46,7 +46,7 @@
<div x-show="remote">
<div class="mt-6">
<x-input-label for="db-host" :value="__('Host')" />
<x-text-input wire:model.defer="host" id="db-host" name="host" type="text" class="mt-1 w-full" />
<x-text-input wire:model="host" id="db-host" name="host" type="text" class="mt-1 w-full" />
<x-input-label for="db-host" :value="__('You might also need to open the database port in Firewall')" class="mt-1"/>
@error('host')
<x-input-error class="mt-2" :messages="$message" />

View File

@ -1,12 +1,12 @@
<x-modal name="create-database-user">
<form wire:submit.prevent="create" class="p-6" x-data="{remote: false}">
<form wire:submit="create" class="p-6" x-data="{remote: false}">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('Create Database User') }}
</h2>
<div class="mt-6">
<x-input-label for="user-username" :value="__('Username')" />
<x-text-input wire:model.defer="username" id="user-username" name="username" type="text" class="mt-1 w-full" />
<x-text-input wire:model="username" id="user-username" name="username" type="text" class="mt-1 w-full" />
@error('username')
<x-input-error class="mt-2" :messages="$message" />
@enderror
@ -14,7 +14,7 @@
<div class="mt-6">
<x-input-label for="user-password" :value="__('Password')" />
<x-text-input wire:model.defer="password" id="user-password" name="password" type="text" class="mt-1 w-full" />
<x-text-input wire:model="password" id="user-password" name="password" type="text" class="mt-1 w-full" />
@error('password')
<x-input-error class="mt-2" :messages="$message" />
@enderror
@ -22,7 +22,7 @@
<div class="mt-6">
<label for="user-remote" class="inline-flex items-center">
<input id="user-remote" wire:model="remote" type="checkbox" x-model="remote" class="rounded dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800" name="remote">
<input id="user-remote" wire:model.live="remote" type="checkbox" x-model="remote" class="rounded dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800" name="remote">
<span class="ml-2 text-sm text-gray-600 dark:text-gray-400">{{ __('Enable remote access') }}</span>
</label>
</div>
@ -30,7 +30,7 @@
<div x-show="remote">
<div class="mt-6">
<x-input-label for="user-host" :value="__('Host')" />
<x-text-input wire:model.defer="host" id="user-host" name="host" type="text" class="mt-1 w-full" />
<x-text-input wire:model="host" id="user-host" name="host" type="text" class="mt-1 w-full" />
<x-input-label for="user-host" :value="__('You might also need to open the database port in Firewall')" class="mt-1"/>
@error('host')
<x-input-error class="mt-2" :messages="$message" />

View File

@ -1,12 +1,12 @@
<x-modal name="database-user-password">
<form wire:submit.prevent="create" class="p-6" x-data="{remote: false}">
<form wire:submit="create" class="p-6" x-data="{remote: false}">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('View Password') }}
</h2>
<div class="mt-6">
<x-input-label :value="__('Password')" />
<x-text-input wire:model.defer="viewPassword" type="text" class="mt-1 w-full" disabled />
<x-text-input wire:model="viewPassword" type="text" class="mt-1 w-full" disabled />
</div>
<div class="mt-6 flex justify-end">

View File

@ -1,5 +1,5 @@
<x-modal name="link-database-user">
<form wire:submit.prevent="link" class="p-6">
<form wire:submit="link" class="p-6">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('Link User to Databases') }}
</h2>
@ -8,7 +8,7 @@
@foreach($databases as $database)
<div class="mb-2">
<label for="db-{{ $database->id }}" class="inline-flex items-center">
<input id="db-{{ $database->id }}" wire:model.defer="link" value="{{ $database->name }}" type="checkbox" class="rounded dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800" name="link">
<input id="db-{{ $database->id }}" wire:model="link" value="{{ $database->name }}" type="checkbox" class="rounded dark:bg-gray-900 border-gray-300 dark:border-gray-700 text-indigo-600 shadow-sm focus:ring-indigo-500 dark:focus:ring-indigo-600 dark:focus:ring-offset-gray-800" name="link">
<span class="ml-2 text-sm text-gray-600 dark:text-gray-400">{{ $database->name }}</span>
</label>
</div>

View File

@ -1,12 +1,12 @@
<x-modal name="restore-backup">
<form wire:submit.prevent="restore" class="p-6" x-data="{}">
<form wire:submit="restore" class="p-6" x-data="{}">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('Restore Backup') }}
</h2>
<div class="mt-6">
<x-input-label for="database" :value="__('Database')" />
<x-select-input wire:model="restoreDatabaseId" id="restoreDatabaseId" name="restoreDatabaseId" class="mt-1 w-full">
<x-select-input wire:model.live="restoreDatabaseId" id="restoreDatabaseId" name="restoreDatabaseId" class="mt-1 w-full">
<option value="" selected disabled>{{ __("Select") }}</option>
@foreach($databases as $db)
<option value="{{ $db->id }}" @if($restoreDatabaseId == $db->id) selected @endif>{{ $db->name }}</option>

View File

@ -4,14 +4,14 @@
</x-primary-button>
<x-modal name="create-rule">
<form wire:submit.prevent="create" class="p-6">
<form wire:submit="create" class="p-6">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('Create new Rule') }}
</h2>
<div class="mt-6">
<x-input-label for="type" :value="__('Rule Type')" />
<x-select-input wire:model.defer="type" id="type" name="type" class="mt-1 w-full">
<x-select-input wire:model="type" id="type" name="type" class="mt-1 w-full">
<option value="allow" @if($type === 'allow') selected @endif>{{ __("Allow") }}</option>
<option value="deny" @if($type === 'deny') selected @endif>{{ __("Deny") }}</option>
</x-select-input>
@ -23,7 +23,7 @@
<div class="mt-6 grid grid-cols-1 lg:grid-cols-2 gap-3">
<div>
<x-input-label for="protocol" :value="__('Protocol')" />
<x-select-input wire:model="protocol" id="protocol" name="protocol" class="mt-1 w-full">
<x-select-input wire:model.live="protocol" id="protocol" name="protocol" class="mt-1 w-full">
@foreach(config('core.firewall_protocols_port') as $key => $value)
<option value="{{ $key }}" @if($key === $protocol) selected @endif>{{ $key }}</option>
@endforeach
@ -35,7 +35,7 @@
<div>
<x-input-label for="port" :value="__('Port')" />
<x-text-input wire:model.defer="port" id="port" name="port" type="text" class="mt-1 w-full" />
<x-text-input wire:model="port" id="port" name="port" type="text" class="mt-1 w-full" />
@error('port')
<x-input-error class="mt-2" :messages="$message" />
@enderror
@ -43,7 +43,7 @@
<div>
<x-input-label for="source" :value="__('Source')" />
<x-text-input wire:model.defer="source" id="source" name="source" type="text" class="mt-1 w-full" />
<x-text-input wire:model="source" id="source" name="source" type="text" class="mt-1 w-full" />
@error('source')
<x-input-error class="mt-2" :messages="$message" />
@enderror
@ -51,7 +51,7 @@
<div>
<x-input-label for="mask" :value="__('Mask')" />
<x-text-input wire:model.defer="mask" id="mask" name="mask" type="text" class="mt-1 w-full" />
<x-text-input wire:model="mask" id="mask" name="mask" type="text" class="mt-1 w-full" />
@error('mask')
<x-input-error class="mt-2" :messages="$message" />
@enderror

View File

@ -4,14 +4,14 @@
</x-primary-button>
<x-modal name="add-channel">
<form wire:submit.prevent="add" class="p-6">
<form wire:submit="add" class="p-6">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('Add new Channel') }}
</h2>
<div class="mt-6">
<x-input-label for="provider" value="Provider" />
<x-select-input wire:model="provider" id="provider" name="provider" class="mt-1 w-full">
<x-select-input wire:model.live="provider" id="provider" name="provider" class="mt-1 w-full">
<option value="" selected disabled>{{ __("Select") }}</option>
@foreach(config('core.notification_channels_providers') as $p)
@if($p !== 'custom')
@ -26,7 +26,7 @@
<div class="mt-6">
<x-input-label for="label" :value="__('Label')" />
<x-text-input wire:model.defer="label" id="label" name="label" type="text" class="mt-1 w-full" />
<x-text-input wire:model="label" id="label" name="label" type="text" class="mt-1 w-full" />
@error('label')
<x-input-error class="mt-2" :messages="$message" />
@enderror
@ -35,7 +35,7 @@
@if($provider == \App\Enums\NotificationChannel::EMAIL)
<div class="mt-6">
<x-input-label for="email" :value="__('Email')" />
<x-text-input wire:model.defer="email" id="email" name="email" type="text" class="mt-1 w-full" />
<x-text-input wire:model="email" id="email" name="email" type="text" class="mt-1 w-full" />
@error('email')
<x-input-error class="mt-2" :messages="$message" />
@enderror
@ -45,7 +45,7 @@
@if(in_array($provider, [\App\Enums\NotificationChannel::SLACK, \App\Enums\NotificationChannel::DISCORD]))
<div class="mt-6">
<x-input-label for="webhook_url" :value="__('Webhook URL')" />
<x-text-input wire:model.defer="webhook_url" id="webhook_url" name="webhook_url" type="text" class="mt-1 w-full" />
<x-text-input wire:model="webhook_url" id="webhook_url" name="webhook_url" type="text" class="mt-1 w-full" />
@error('webhook_url')
<x-input-error class="mt-2" :messages="$message" />
@enderror
@ -55,7 +55,7 @@
@if($provider == \App\Enums\NotificationChannel::TELEGRAM)
<div class="mt-6">
<x-input-label for="bot_token" :value="__('Bot Token')" />
<x-text-input wire:model.defer="bot_token" id="bot_token" name="bot_token" type="text" class="mt-1 w-full" />
<x-text-input wire:model="bot_token" id="bot_token" name="bot_token" type="text" class="mt-1 w-full" />
@error('bot_token')
<x-input-error class="mt-2" :messages="$message" />
@enderror
@ -63,7 +63,7 @@
<div class="mt-6">
<x-input-label for="chat_id" :value="__('Chat ID')" />
<x-text-input wire:model.defer="chat_id" id="chat_id" name="chat_id" type="text" class="mt-1 w-full" />
<x-text-input wire:model="chat_id" id="chat_id" name="chat_id" type="text" class="mt-1 w-full" />
@error('chat_id')
<x-input-error class="mt-2" :messages="$message" />
@enderror

View File

@ -1,12 +1,12 @@
<x-modal name="install-extension">
<form wire:submit.prevent="installExtension" class="p-6">
<form wire:submit="installExtension" class="p-6">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('Install Extension') }}
</h2>
<div class="mt-6">
<x-input-label for="extension" value="Name" />
<x-select-input wire:model.defer="extension" name="extension" class="mt-1 w-full">
<x-select-input wire:model="extension" name="extension" class="mt-1 w-full">
<option value="" selected>{{ __("Select") }}</option>
@foreach(config('core.php_extensions') as $extension)
<option value="{{ $extension }}" @if(in_array($extension, $installedExtensions)) disabled @endif>

View File

@ -1,12 +1,12 @@
<x-modal name="update-php-ini">
<form wire:submit.prevent="saveIni" class="p-6">
<form wire:submit="saveIni" class="p-6">
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
{{ __('Update php.ini') }}
</h2>
<div class="mt-6">
<x-input-label for="ini" value="php.ini" />
<x-textarea wire:model.defer="ini" id="ini" name="ini" class="mt-1 w-full" rows="15" />
<x-textarea wire:model="ini" id="ini" name="ini" class="mt-1 w-full" rows="15" />
@error('ini')
<x-input-error class="mt-2" :messages="$message" />
@enderror

Some files were not shown because too many files have changed in this diff Show More