Compare commits

...

61 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
55bf8b8ecf fix ipv6 (#96) 2024-01-27 21:34:36 +01:00
0420babdef Bump vite from 4.2.3 to 4.5.2 (#95)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 4.2.3 to 4.5.2.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v4.5.2/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v4.5.2/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-27 21:09:10 +01:00
1c3d78a5ed installation without domain 2024-01-21 21:53:26 +01:00
8665435bc4 add blank php site (#94)
* add blank php site

* fix frontend

* fix source control check
2024-01-18 19:45:58 +01:00
0ec6a9dea2 fix db transaction usage 2024-01-14 12:56:25 +01:00
bdfda05398 update Roadmap URLs (#91) 2024-01-14 08:57:00 +01:00
919cdc6892 Bump follow-redirects from 1.15.2 to 1.15.5 (#90)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.2 to 1.15.5.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.2...v1.15.5)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-13 19:36:05 +01:00
902548e463 Fix roadmap link in README (#89) 2024-01-10 20:41:18 +01:00
2462b31f3b typo fix 2024-01-08 16:25:12 +01:00
e997d0deea WIP notifications and other refactors (#88)
* WIP notifications and other refactors
- refactor notification channels
- send notifications on events related to the servers and sites
- delete server log files on server deletion
- add telegram notification channel
- add new icons
- cache configs and icons on installation and updates
- new navbar for dark mode and settings

* discord channel

* build assets

* pint
2024-01-07 09:54:08 +01:00
f06b8f7d20 update vhost and bug fix (#87) 2024-01-05 22:07:45 +01:00
f120a570e8 Update issue templates 2024-01-05 20:38:48 +01:00
2d7f225ff2 define storage instances with phpdoc 2024-01-03 22:31:50 +01:00
31bd146239 small typo fix 2024-01-02 20:18:25 +01:00
10a6bb57a8 adding Projects feature (#85) 2024-01-02 19:50:49 +01:00
fd2244d382 update composer (#84)
* update composer
log viewer
code style format

* fix composer
2024-01-01 22:05:31 +01:00
551f1ce40e fix issue with php site creation 2024-01-01 21:47:05 +01:00
1ce92d9361 fix issue with php site creation 2024-01-01 21:44:49 +01:00
ec6e55e30c Update README.md 2024-01-01 19:49:51 +01:00
4cda14f4b8 deploy Wordpress sites via VitoDeploy (#83) 2024-01-01 16:20:57 +01:00
5e6d338bdc Bump phpseclib/phpseclib from 3.0.21 to 3.0.34 (#78)
Bumps [phpseclib/phpseclib](https://github.com/phpseclib/phpseclib) from 3.0.21 to 3.0.34.
- [Release notes](https://github.com/phpseclib/phpseclib/releases)
- [Changelog](https://github.com/phpseclib/phpseclib/blob/master/CHANGELOG.md)
- [Commits](https://github.com/phpseclib/phpseclib/compare/3.0.21...3.0.34)

---
updated-dependencies:
- dependency-name: phpseclib/phpseclib
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-07 22:40:24 +01:00
7312e3f515 Bump axios from 1.3.5 to 1.6.0 (#74)
Bumps [axios](https://github.com/axios/axios) from 1.3.5 to 1.6.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.3.5...v1.6.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-16 22:53:15 +01:00
b771db882b Update README.md 2023-11-05 11:00:31 +01:00
94977797cc Update README.md 2023-11-05 10:59:16 +01:00
c45872df55 Create CONTRIBUTING.md 2023-11-05 10:57:38 +01:00
16fae5334c Create SECURITY.md (#72) 2023-11-04 14:49:02 +01:00
7b8deddeca small bug fix to update source-control provider 2023-10-29 22:37:46 +01:00
1bf3c94358 add auto-deployment (#71)
add update source-control to site-settings
2023-10-29 22:20:15 +01:00
700cc5f44c Bump postcss from 8.4.21 to 8.4.31 (#67)
Bumps [postcss](https://github.com/postcss/postcss) from 8.4.21 to 8.4.31.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.21...8.4.31)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Saeed Vaziry <61919774+saeedvaziry@users.noreply.github.com>
2023-10-16 22:41:16 +02:00
9d13cc0756 enable php extension installation (#68) 2023-10-15 10:06:50 +02:00
f51d7900f0 build assets 2023-10-10 17:18:54 +02:00
4bd4b34d24 phpmyadmin (#66)
* add phpmyadmin

* add tests
2023-10-10 17:15:58 +02:00
7c5505be16 update laravel (#65)
* update laravel

* fix tests
2023-10-03 13:23:30 +02:00
7249cf9ed6 include unit tests in the workflows 2023-09-30 16:23:16 +02:00
8282d39722 add timezone edit to profile (#63) 2023-09-30 16:15:09 +02:00
38e23a1ceb fix consecutive deployment issue (#62)
* fix consecutive deployment issue

* fix styles
2023-09-29 23:08:56 +02:00
1e1204fe40 Gitlab Self-managed Support (#56)
* Update UI to ask for URL for Gitlab as provider.

* Create state for each source control in factory.

* Save url for SourceControl when given.

* Make Gitlab dynamically use correct URL (custom or default)

* Update SourceControlsTest to check for custom url when given.

* Style fixes.

---------

Co-authored-by: Saeed Vaziry <61919774+saeedvaziry@users.noreply.github.com>
2023-09-25 10:11:01 +02:00
7d98986f52 added FTP support to storage providers (#58)
* added FTP support to storage providers

* build and code style fix
2023-09-24 12:50:01 +02:00
2c81e324f6 Update .env.example 2023-09-23 23:18:17 +02:00
422da431ec Update .env.example 2023-09-23 23:17:37 +02:00
6c27215ea1 fix AWS image ids (#57)
* fix AWS image ids

* fix code style
2023-09-23 12:44:40 +02:00
fb840204ad login page ui alignment 2023-09-18 19:41:25 +02:00
e07e197dd9 Added Dark mode & Light mode toggle button (#50)
* wip

* Added Show Password checkbox & Added Toggle Dark Mode & Light Mode button on Dropdown menu

* resloved the PR comments

* Fixed the reload issues on theme change & rewammped  the UI of login page as per PR comments

---------

Co-authored-by: saravana sai <saravanasao002@gmail.com>
Co-authored-by: Saeed Vaziry <61919774+saeedvaziry@users.noreply.github.com>
2023-09-18 19:39:26 +02:00
3a2dba4ad3 upgrade workflows os to ubuntu 22.04 (#52) 2023-09-17 18:46:34 +02:00
b990f9ce32 remove pusher 2023-09-17 18:37:59 +02:00
a6727ff459 cleanup .env files 2023-09-17 18:22:17 +02:00
170535760f change app to vito on docker-compose to make sail works 2023-09-17 18:17:27 +02:00
648529c3bd add sails env 2023-09-17 18:10:57 +02:00
84192b7cb7 fix default key names 2023-09-17 11:08:47 +02:00
391 changed files with 7566 additions and 2964 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

View File

@ -2,7 +2,7 @@ APP_NAME=Vito
APP_ENV=local APP_ENV=local
APP_KEY= APP_KEY=
APP_DEBUG=true APP_DEBUG=true
APP_URL=http://localhost:2080 APP_URL=http://vito.test
LOG_CHANNEL=stack LOG_CHANNEL=stack
LOG_LEVEL=debug LOG_LEVEL=debug
@ -15,52 +15,24 @@ DB_USERNAME=root
DB_PASSWORD= DB_PASSWORD=
BROADCAST_DRIVER=null BROADCAST_DRIVER=null
CACHE_DRIVER=redis CACHE_DRIVER=file
FILESYSTEM_DRIVER=local FILESYSTEM_DRIVER=local
QUEUE_CONNECTION=sync QUEUE_CONNECTION=default
SESSION_DRIVER=database SESSION_DRIVER=database
SESSION_LIFETIME=120 SESSION_LIFETIME=120
MEMCACHED_HOST=127.0.0.1
REDIS_HOST=127.0.0.1 REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null 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
MAIL_FROM_ADDRESS=null MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}" MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID= SSH_PUBLIC_KEY_NAME=ssh-public.key
AWS_SECRET_ACCESS_KEY= SSH_PRIVATE_KEY_NAME=ssh-private.pem
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
FORWARD_REDIS_PORT=2060
FORWARD_DB_PORT=2070
APP_PORT=2080
HMR_PORT=2090
SENTRY_LARAVEL_DSN=
APP_SERVICE=vito
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
ABLY_KEY=
ABLY_PUBLIC_KEY=

View File

@ -14,7 +14,7 @@ DB_DATABASE=
DB_USERNAME= DB_USERNAME=
DB_PASSWORD= DB_PASSWORD=
BROADCAST_DRIVER=pusher BROADCAST_DRIVER=null
CACHE_DRIVER=file CACHE_DRIVER=file
FILESYSTEM_DRIVER=local FILESYSTEM_DRIVER=local
QUEUE_CONNECTION=default QUEUE_CONNECTION=default
@ -26,39 +26,13 @@ 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
MAIL_FROM_ADDRESS=null MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}" MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
PUSHER_APP_ID=app-id
PUSHER_APP_KEY=app-key
PUSHER_APP_SECRET=app-secret
PUSHER_HOST=soketi
PUSHER_PORT=6001
PUSHER_SCHEME=http
PUSHER_APP_CLUSTER=mt1
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
VITE_PUSHER_HOST="${PUSHER_HOST}"
VITE_PUSHER_PORT="${PUSHER_PORT}"
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
SSH_PUBLIC_KEY_NAME=ssh-public.key SSH_PUBLIC_KEY_NAME=ssh-public.key
SSH_PRIVATE_KEY_NAME=ssh-private.pem SSH_PRIVATE_KEY_NAME=ssh-private.pem
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
ABLY_KEY=
ABLY_PUBLIC_KEY=

45
.env.sail Normal file
View File

@ -0,0 +1,45 @@
APP_NAME=Vito
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://vito.test
LOG_CHANNEL=stack
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=vito
DB_USERNAME=sail
DB_PASSWORD=password
BROADCAST_DRIVER=null
CACHE_DRIVER=redis
FILESYSTEM_DRIVER=local
QUEUE_CONNECTION=default
SESSION_DRIVER=database
SESSION_LIFETIME=120
REDIS_HOST=redis
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
APP_SERVICE=vito
FORWARD_REDIS_PORT=2060
FORWARD_DB_PORT=2070
APP_PORT=2080
HMR_PORT=2090

View File

@ -14,7 +14,7 @@ DB_DATABASE=vito_test
DB_USERNAME=root DB_USERNAME=root
DB_PASSWORD= DB_PASSWORD=
BROADCAST_DRIVER=pusher BROADCAST_DRIVER=null
CACHE_DRIVER=array CACHE_DRIVER=array
FILESYSTEM_DRIVER=local FILESYSTEM_DRIVER=local
QUEUE_CONNECTION=database QUEUE_CONNECTION=database
@ -23,19 +23,5 @@ SESSION_LIFETIME=120
MAIL_MAILER=array MAIL_MAILER=array
PUSHER_APP_ID=app-id
PUSHER_APP_KEY=app-key
PUSHER_APP_SECRET=app-secret
PUSHER_HOST=soketi
PUSHER_PORT=6001
PUSHER_SCHEME=http
PUSHER_APP_CLUSTER=mt1
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
VITE_PUSHER_HOST="${PUSHER_HOST}"
VITE_PUSHER_PORT="${PUSHER_PORT}"
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
SSH_PUBLIC_KEY_NAME=ssh-public.key SSH_PUBLIC_KEY_NAME=ssh-public.key
SSH_PRIVATE_KEY_NAME=ssh-private.pem SSH_PRIVATE_KEY_NAME=ssh-private.pem

27
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View 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.

View 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/

View File

@ -8,7 +8,7 @@ on:
jobs: jobs:
code-style: code-style:
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
strategy: strategy:
fail-fast: true fail-fast: true

View File

@ -8,7 +8,7 @@ on:
jobs: jobs:
tests: tests:
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
services: services:
mysql: mysql:

23
CONTRIBUTING.md Normal file
View 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.

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,24 +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
## Contribution ## Useful Links
Feel free to open a PR - [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 ## Credits
- Laravel - Laravel
- Tailwind - Tailwindcss
- Livewire - Livewire
- Alpinejs - Alpinejs
- Vite - Vite
- Laravel Enum by BenSampo - Laravel Enum by BenSampo
- Log Viewer by Arunas Skirius
- PHPSecLib
- Laravel Blade Icons
- Guzzlehttp
- Owenvoke for `owenvoke/blade-fontawesome`
- Axios
- Toastr by CodeSeven

11
SECURITY.md Normal file
View 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)

View File

@ -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();
} }
} }

View File

@ -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);

View 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();
}
}

View 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();
}
}

View 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();
}
}

View File

@ -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'] = [

View File

@ -1,7 +1,8 @@
<?php <?php
namespace App\Actions\Database; namespace App\Actions\Service;
use App\Enums\ServiceStatus;
use App\Models\Server; use App\Models\Server;
use App\Models\Service; use App\Models\Service;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
@ -18,23 +19,21 @@ public function install(Server $server, array $input): Service
$phpMyAdmin = $server->defaultService('phpmyadmin'); $phpMyAdmin = $server->defaultService('phpmyadmin');
if ($phpMyAdmin) { if ($phpMyAdmin) {
if ($phpMyAdmin->status === 'ready') { throw ValidationException::withMessages([
throw ValidationException::withMessages([ 'allowed_ip' => __('Already installed'),
'install' => __('Already installed'), ]);
])->errorBag('installPHPMyAdmin');
}
$phpMyAdmin->delete();
} }
$phpMyAdmin = new Service([ $phpMyAdmin = new Service([
'server_id' => $server->id, 'server_id' => $server->id,
'type' => 'phpmyadmin', 'type' => 'phpmyadmin',
'type_data' => [ 'type_data' => [
'allowed_ip' => $input['allowed_ip'], 'allowed_ip' => $input['allowed_ip'],
'port' => $input['port'],
'php' => $server->defaultService('php')->version, 'php' => $server->defaultService('php')->version,
], ],
'name' => 'phpmyadmin', 'name' => 'phpmyadmin',
'version' => '5.1.2', 'version' => '5.1.2',
'status' => 'installing', 'status' => ServiceStatus::INSTALLING,
'is_default' => 1, 'is_default' => 1,
]); ]);
$phpMyAdmin->save(); $phpMyAdmin->save();
@ -50,6 +49,12 @@ private function validate(array $input): void
{ {
Validator::make($input, [ Validator::make($input, [
'allowed_ip' => 'required', 'allowed_ip' => 'required',
])->validateWithBag('installPHPMyAdmin'); 'port' => [
'required',
'numeric',
'min:1',
'max:65535',
],
])->validate();
} }
} }

View File

@ -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);

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

View 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();
}
}

View File

@ -3,6 +3,7 @@
namespace App\Actions\SourceControl; namespace App\Actions\SourceControl;
use App\Models\SourceControl; use App\Models\SourceControl;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule; use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
@ -16,6 +17,7 @@ public function connect(array $input): void
'provider' => $input['provider'], 'provider' => $input['provider'],
'profile' => $input['name'], 'profile' => $input['name'],
'access_token' => $input['token'], 'access_token' => $input['token'],
'url' => Arr::has($input, 'url') ? $input['url'] : null,
]); ]);
if (! $sourceControl->provider()->connect()) { if (! $sourceControl->provider()->connect()) {
@ -44,6 +46,11 @@ private function validate(array $input): void
'token' => [ 'token' => [
'required', 'required',
], ],
'url' => [
'nullable',
'url:http,https',
'ends_with:/',
],
]; ];
Validator::make($input, $rules)->validate(); Validator::make($input, $rules)->validate();
} }

View File

@ -21,13 +21,15 @@ public function create(User $user, array $input): void
'user_id' => $user->id, 'user_id' => $user->id,
'provider' => $input['provider'], 'provider' => $input['provider'],
'profile' => $input['name'], 'profile' => $input['name'],
'credentials' => [
'token' => $input['token'],
],
]); ]);
$this->validateProvider($input, $storageProvider->provider()->validationRules());
$storageProvider->credentials = $storageProvider->provider()->credentialData($input);
if (! $storageProvider->provider()->connect()) { if (! $storageProvider->provider()->connect()) {
throw ValidationException::withMessages([ throw ValidationException::withMessages([
'token' => __("Couldn't connect to the provider"), 'provider' => __("Couldn't connect to the provider"),
]); ]);
} }
$storageProvider->save(); $storageProvider->save();
@ -44,9 +46,11 @@ private function validate(User $user, array $input): void
'required', 'required',
Rule::unique('storage_providers', 'profile')->where('user_id', $user->id), Rule::unique('storage_providers', 'profile')->where('user_id', $user->id),
], ],
'token' => [
'required',
],
])->validate(); ])->validate();
} }
private function validateProvider(array $input, array $rules): void
{
Validator::make($input, $rules)->validate();
}
} }

View File

@ -19,6 +19,10 @@ public function update(User $user, array $input): void
Validator::make($input, [ Validator::make($input, [
'name' => ['required', 'string', 'max:255'], 'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'max:255', Rule::unique('users')->ignore($user->id)], 'email' => ['required', 'email', 'max:255', Rule::unique('users')->ignore($user->id)],
'timezone' => [
'required',
Rule::in(timezone_identifiers_list()),
],
])->validateWithBag('updateProfileInformation'); ])->validateWithBag('updateProfileInformation');
if ($input['email'] !== $user->email) { if ($input['email'] !== $user->email) {
@ -27,6 +31,7 @@ public function update(User $user, array $input): void
$user->forceFill([ $user->forceFill([
'name' => $input['name'], 'name' => $input['name'],
'email' => $input['email'], 'email' => $input['email'],
'timezone' => $input['timezone'],
])->save(); ])->save();
} }
} }
@ -39,6 +44,7 @@ protected function updateVerifiedUser(User $user, array $input): void
$user->forceFill([ $user->forceFill([
'name' => $input['name'], 'name' => $input['name'],
'email' => $input['email'], 'email' => $input['email'],
'timezone' => $input['timezone'],
])->save(); ])->save();
} }
} }

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -6,6 +6,10 @@
interface StorageProvider interface StorageProvider
{ {
public function validationRules(): array;
public function credentialData(array $input): array;
public function connect(): bool; public function connect(): bool;
public function upload(Server $server, string $src, string $dest): array; public function upload(Server $server, string $src, string $dest): array;

View File

@ -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;

View File

@ -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
View 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';
}

View File

@ -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';

View File

@ -11,6 +11,4 @@ final class SourceControl extends Enum
const GITLAB = 'gitlab'; const GITLAB = 'gitlab';
const BITBUCKET = 'bitbucket'; const BITBUCKET = 'bitbucket';
const CUSTOM = 'custom';
} }

View File

@ -6,7 +6,7 @@
final class StorageProvider extends Enum final class StorageProvider extends Enum
{ {
const GOOGLE = 'google';
const DROPBOX = 'dropbox'; const DROPBOX = 'dropbox';
const FTP = 'ftp';
} }

View File

@ -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
View 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
View 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);
}
}

View File

@ -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);

View File

@ -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,
]);
} }
} }

View 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,
]);
}
}

View 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');
}
}

View 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');
}
}

View File

@ -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());

View File

@ -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

View File

@ -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

View File

@ -14,11 +14,11 @@ class Env extends Component
public Site $site; 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 public function save(): void
@ -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

View 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');
}
}

View File

@ -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');

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 ?? [],
]); ]);
} }
} }

View File

@ -15,10 +15,13 @@ class UpdateProfileInformation extends Component
public string $email; public string $email;
public string $timezone;
public function mount(): void public function mount(): void
{ {
$this->name = auth()->user()->name; $this->name = auth()->user()->name;
$this->email = auth()->user()->email; $this->email = auth()->user()->email;
$this->timezone = auth()->user()->timezone;
} }
/** /**
@ -30,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

View 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');
}
}

View 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');
}
}

View 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(),
]);
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,
]); ]);
} }
} }

View File

@ -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

View File

@ -0,0 +1,31 @@
<?php
namespace App\Http\Livewire\Services;
use App\Actions\Service\InstallPHPMyAdmin as InstallPHPMyAdminAction;
use App\Models\Server;
use Illuminate\Contracts\View\View;
use Livewire\Component;
class InstallPHPMyAdmin extends Component
{
public Server $server;
public string $allowed_ip;
public string $port = '5433';
public function install(): void
{
app(InstallPHPMyAdminAction::class)->install($this->server, $this->all());
$this->dispatch('started');
$this->dispatch('$refresh')->to(ServicesList::class);
}
public function render(): View
{
return view('livewire.services.install-phpmyadmin');
}
}

View File

@ -3,6 +3,7 @@
namespace App\Http\Livewire\Services; namespace App\Http\Livewire\Services;
use App\Models\Server; use App\Models\Server;
use App\Models\Service;
use App\Traits\RefreshComponentOnBroadcast; use App\Traits\RefreshComponentOnBroadcast;
use Illuminate\Contracts\View\View; use Illuminate\Contracts\View\View;
use Livewire\Component; use Livewire\Component;
@ -15,6 +16,7 @@ class ServicesList extends Component
public function stop(int $id): void public function stop(int $id): void
{ {
/** @var Service $service */
$service = $this->server->services()->where('id', $id)->firstOrFail(); $service = $this->server->services()->where('id', $id)->firstOrFail();
$service->stop(); $service->stop();
@ -24,6 +26,7 @@ public function stop(int $id): void
public function start(int $id): void public function start(int $id): void
{ {
/** @var Service $service */
$service = $this->server->services()->where('id', $id)->firstOrFail(); $service = $this->server->services()->where('id', $id)->firstOrFail();
$service->start(); $service->start();
@ -33,6 +36,7 @@ public function start(int $id): void
public function restart(int $id): void public function restart(int $id): void
{ {
/** @var Service $service */
$service = $this->server->services()->where('id', $id)->firstOrFail(); $service = $this->server->services()->where('id', $id)->firstOrFail();
$service->restart(); $service->restart();
@ -40,6 +44,16 @@ public function restart(int $id): void
$this->refreshComponent([]); $this->refreshComponent([]);
} }
public function uninstall(int $id): void
{
/** @var Service $service */
$service = $this->server->services()->where('id', $id)->firstOrFail();
$service->uninstall();
$this->refreshComponent([]);
}
public function render(): View public function render(): View
{ {
return view('livewire.services.services-list', [ return view('livewire.services.services-list', [

View File

@ -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', [

View File

@ -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

View 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');
}
}

View 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');
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -14,13 +14,27 @@ class ConnectProvider extends Component
public string $token; public string $token;
public string $host;
public string $port;
public string $path = '/';
public string $username;
public string $password;
public int $ssl = 1;
public int $passive = 0;
public function connect(): void 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

View File

@ -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

View File

@ -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;

View File

@ -3,6 +3,7 @@
namespace App\Jobs\Installation; namespace App\Jobs\Installation;
use App\Actions\FirewallRule\CreateRule; use App\Actions\FirewallRule\CreateRule;
use App\Enums\ServiceStatus;
use App\Jobs\Job; use App\Jobs\Job;
use App\Models\FirewallRule; use App\Models\FirewallRule;
use App\Models\Service; use App\Models\Service;
@ -32,6 +33,9 @@ public function handle(): void
$this->downloadSource(); $this->downloadSource();
$this->setUpVHost(); $this->setUpVHost();
$this->restartPHP(); $this->restartPHP();
$this->service->update([
'status' => ServiceStatus::READY,
]);
} }
/** /**
@ -41,7 +45,7 @@ private function setUpFirewall(): void
{ {
$this->firewallRule = FirewallRule::query() $this->firewallRule = FirewallRule::query()
->where('server_id', $this->service->server_id) ->where('server_id', $this->service->server_id)
->where('port', '54331') ->where('port', $this->service->type_data['port'])
->first(); ->first();
if ($this->firewallRule) { if ($this->firewallRule) {
$this->firewallRule->source = $this->service->type_data['allowed_ip']; $this->firewallRule->source = $this->service->type_data['allowed_ip'];
@ -52,7 +56,7 @@ private function setUpFirewall(): void
[ [
'type' => 'allow', 'type' => 'allow',
'protocol' => 'tcp', 'protocol' => 'tcp',
'port' => '54331', 'port' => $this->service->type_data['port'],
'source' => $this->service->type_data['allowed_ip'], 'source' => $this->service->type_data['allowed_ip'],
'mask' => '0', 'mask' => '0',
] ]
@ -78,6 +82,7 @@ private function setUpVHost(): void
{ {
$vhost = File::get(resource_path('commands/webserver/nginx/phpmyadmin-vhost.conf')); $vhost = File::get(resource_path('commands/webserver/nginx/phpmyadmin-vhost.conf'));
$vhost = Str::replace('__php_version__', $this->service->server->defaultService('php')->version, $vhost); $vhost = Str::replace('__php_version__', $this->service->server->defaultService('php')->version, $vhost);
$vhost = Str::replace('__port__', $this->service->type_data['port'], $vhost);
$this->service->server->ssh()->exec( $this->service->server->ssh()->exec(
new CreateNginxPHPMyAdminVHostCommand($vhost), new CreateNginxPHPMyAdminVHostCommand($vhost),
'create-phpmyadmin-vhost' 'create-phpmyadmin-vhost'
@ -98,6 +103,9 @@ private function restartPHP(): void
public function failed(Throwable $throwable): Throwable public function failed(Throwable $throwable): Throwable
{ {
$this->firewallRule?->removeFromServer(); $this->firewallRule?->removeFromServer();
$this->service->update([
'status' => ServiceStatus::INSTALLATION_FAILED,
]);
throw $throwable; throw $throwable;
} }
} }

View File

@ -37,7 +37,7 @@ private function removeFirewallRule(): void
/** @var ?FirewallRule $rule */ /** @var ?FirewallRule $rule */
$rule = FirewallRule::query() $rule = FirewallRule::query()
->where('server_id', $this->service->server_id) ->where('server_id', $this->service->server_id)
->where('port', '54331') ->where('port', $this->service->type_data['port'])
->first(); ->first();
$rule?->removeFromServer(); $rule?->removeFromServer();
} }

View File

@ -3,13 +3,12 @@
namespace App\Jobs; namespace App\Jobs;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
abstract class Job implements ShouldQueue, ShouldBeUnique abstract class Job implements ShouldQueue
{ {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
} }

View File

@ -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,

View File

@ -4,7 +4,6 @@
use App\Enums\DeploymentStatus; use App\Enums\DeploymentStatus;
use App\Events\Broadcast; use App\Events\Broadcast;
use App\Helpers\SSH;
use App\Jobs\Job; use App\Jobs\Job;
use App\Models\Deployment; use App\Models\Deployment;
use App\SSHCommands\System\RunScriptCommand; use App\SSHCommands\System\RunScriptCommand;
@ -18,13 +17,11 @@ class Deploy extends Job
protected string $script; protected string $script;
protected SSH $ssh; public function __construct(Deployment $deployment, string $path)
public function __construct(Deployment $deployment, string $path, string $script)
{ {
$this->script = $deployment->deploymentScript->content;
$this->deployment = $deployment; $this->deployment = $deployment;
$this->path = $path; $this->path = $path;
$this->script = $script;
} }
/** /**
@ -32,28 +29,31 @@ public function __construct(Deployment $deployment, string $path, string $script
*/ */
public function handle(): void public function handle(): void
{ {
$this->ssh = $this->deployment->site->server->ssh(); $ssh = $this->deployment->site->server->ssh();
$this->ssh->exec( try {
new RunScriptCommand($this->path, $this->script), $ssh->exec(
'deploy', new RunScriptCommand($this->path, $this->script),
$this->deployment->site_id 'deploy',
); $this->deployment->site_id
$this->deployment->status = DeploymentStatus::FINISHED; );
$this->deployment->log_id = $this->ssh->log->id; $this->deployment->status = DeploymentStatus::FINISHED;
$this->deployment->save(); $this->deployment->log_id = $ssh->log->id;
event( $this->deployment->save();
new Broadcast('deploy-site-finished', [ event(
'deployment' => $this->deployment, new Broadcast('deploy-site-finished', [
]) 'deployment' => $this->deployment,
); ])
);
} catch (Throwable) {
$this->deployment->log_id = $ssh->log->id;
$this->deployment->save();
$this->failed();
}
} }
public function failed(): void public function failed(): void
{ {
$this->deployment->status = DeploymentStatus::FAILED; $this->deployment->status = DeploymentStatus::FAILED;
if ($this->ssh->log) {
$this->deployment->log_id = $this->ssh->log->id;
}
$this->deployment->save(); $this->deployment->save();
event( event(
new Broadcast('deploy-site-failed', [ new Broadcast('deploy-site-failed', [

View File

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

View File

@ -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);
} }
} }

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