Merge branch 'next' into feat/manage-db-using-api
This commit is contained in:
65
.github/workflows/browser-tests.yml
vendored
65
.github/workflows/browser-tests.yml
vendored
@@ -1,65 +0,0 @@
|
|||||||
name: Dusk
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ "not-existing" ]
|
|
||||||
jobs:
|
|
||||||
dusk:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
services:
|
|
||||||
redis:
|
|
||||||
image: redis
|
|
||||||
env:
|
|
||||||
REDIS_HOST: localhost
|
|
||||||
REDIS_PORT: 6379
|
|
||||||
ports:
|
|
||||||
- 6379:6379
|
|
||||||
options: >-
|
|
||||||
--health-cmd "redis-cli ping"
|
|
||||||
--health-interval 10s
|
|
||||||
--health-timeout 5s
|
|
||||||
--health-retries 5
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Set up PostgreSQL
|
|
||||||
run: |
|
|
||||||
sudo systemctl start postgresql
|
|
||||||
sudo -u postgres psql -c "CREATE DATABASE coolify;"
|
|
||||||
sudo -u postgres psql -c "CREATE USER coolify WITH PASSWORD 'password';"
|
|
||||||
sudo -u postgres psql -c "ALTER ROLE coolify SET client_encoding TO 'utf8';"
|
|
||||||
sudo -u postgres psql -c "ALTER ROLE coolify SET default_transaction_isolation TO 'read committed';"
|
|
||||||
sudo -u postgres psql -c "ALTER ROLE coolify SET timezone TO 'UTC';"
|
|
||||||
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE coolify TO coolify;"
|
|
||||||
- name: Setup PHP
|
|
||||||
uses: shivammathur/setup-php@v2
|
|
||||||
with:
|
|
||||||
php-version: '8.2'
|
|
||||||
- name: Copy .env
|
|
||||||
run: cp .env.dusk.ci .env
|
|
||||||
- name: Install Dependencies
|
|
||||||
run: composer install --no-progress --prefer-dist --optimize-autoloader
|
|
||||||
- name: Generate key
|
|
||||||
run: php artisan key:generate
|
|
||||||
- name: Install Chrome binaries
|
|
||||||
run: php artisan dusk:chrome-driver --detect
|
|
||||||
- name: Start Chrome Driver
|
|
||||||
run: ./vendor/laravel/dusk/bin/chromedriver-linux --port=4444 &
|
|
||||||
- name: Build assets
|
|
||||||
run: npm install && npm run build
|
|
||||||
- name: Run Laravel Server
|
|
||||||
run: php artisan serve --no-reload &
|
|
||||||
- name: Execute tests
|
|
||||||
run: php artisan dusk
|
|
||||||
- name: Upload Screenshots
|
|
||||||
if: failure()
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: screenshots
|
|
||||||
path: tests/Browser/screenshots
|
|
||||||
- name: Upload Console Logs
|
|
||||||
if: failure()
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: console
|
|
||||||
path: tests/Browser/console
|
|
||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
|
|
||||||
async function processIssue(issueNumber, isFromPR = false, prBaseBranch = null) {
|
async function processIssue(issueNumber, isFromPR = false, prBaseBranch = null) {
|
||||||
try {
|
try {
|
||||||
if (isFromPR && prBaseBranch !== 'main') {
|
if (isFromPR && prBaseBranch !== 'v4.x') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ jobs:
|
|||||||
if (context.eventName === 'pull_request' || context.eventName === 'pull_request_target') {
|
if (context.eventName === 'pull_request' || context.eventName === 'pull_request_target') {
|
||||||
const pr = context.payload.pull_request;
|
const pr = context.payload.pull_request;
|
||||||
await processIssue(pr.number);
|
await processIssue(pr.number);
|
||||||
if (pr.merged && pr.base.ref === 'main' && pr.body) {
|
if (pr.merged && pr.base.ref === 'v4.x' && pr.body) {
|
||||||
const issueReferences = pr.body.match(/#(\d+)/g);
|
const issueReferences = pr.body.match(/#(\d+)/g);
|
||||||
if (issueReferences) {
|
if (issueReferences) {
|
||||||
for (const reference of issueReferences) {
|
for (const reference of issueReferences) {
|
||||||
|
|||||||
2
.github/workflows/coolify-helper.yml
vendored
2
.github/workflows/coolify-helper.yml
vendored
@@ -2,7 +2,7 @@ name: Coolify Helper Image
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "main" ]
|
branches: [ "v4.x" ]
|
||||||
paths:
|
paths:
|
||||||
- .github/workflows/coolify-helper.yml
|
- .github/workflows/coolify-helper.yml
|
||||||
- docker/coolify-helper/Dockerfile
|
- docker/coolify-helper/Dockerfile
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ name: Production Build (v4)
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ["main"]
|
branches: ["v4.x"]
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- .github/workflows/coolify-helper.yml
|
- .github/workflows/coolify-helper.yml
|
||||||
- .github/workflows/coolify-helper-next.yml
|
- .github/workflows/coolify-helper-next.yml
|
||||||
|
|||||||
2
.github/workflows/coolify-realtime.yml
vendored
2
.github/workflows/coolify-realtime.yml
vendored
@@ -2,7 +2,7 @@ name: Coolify Realtime
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "main" ]
|
branches: [ "v4.x" ]
|
||||||
paths:
|
paths:
|
||||||
- .github/workflows/coolify-realtime.yml
|
- .github/workflows/coolify-realtime.yml
|
||||||
- docker/coolify-realtime/Dockerfile
|
- docker/coolify-realtime/Dockerfile
|
||||||
|
|||||||
5
.github/workflows/coolify-staging-build.yml
vendored
5
.github/workflows/coolify-staging-build.yml
vendored
@@ -2,7 +2,10 @@ name: Staging Build
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches-ignore: ["main", "v3"]
|
branches-ignore:
|
||||||
|
- v4.x
|
||||||
|
- v3.x
|
||||||
|
- '**v5.x**'
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- .github/workflows/coolify-helper.yml
|
- .github/workflows/coolify-helper.yml
|
||||||
- .github/workflows/coolify-helper-next.yml
|
- .github/workflows/coolify-helper-next.yml
|
||||||
|
|||||||
4
.github/workflows/generate-changelog.yml
vendored
4
.github/workflows/generate-changelog.yml
vendored
@@ -2,7 +2,7 @@ name: Generate Changelog
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [ v4.x ]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@@ -33,4 +33,4 @@ jobs:
|
|||||||
git config user.email 'github-actions[bot]@users.noreply.github.com'
|
git config user.email 'github-actions[bot]@users.noreply.github.com'
|
||||||
git add CHANGELOG.md
|
git add CHANGELOG.md
|
||||||
git commit -m "docs: update changelog"
|
git commit -m "docs: update changelog"
|
||||||
git push https://${{ secrets.GITHUB_TOKEN }}@github.com/${GITHUB_REPOSITORY}.git main
|
git push https://${{ secrets.GITHUB_TOKEN }}@github.com/${GITHUB_REPOSITORY}.git v4.x
|
||||||
|
|||||||
273
CHANGELOG.md
273
CHANGELOG.md
@@ -2,10 +2,100 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
## [unreleased]
|
## [4.0.0-beta.417] - 2025-05-07
|
||||||
|
|
||||||
|
### 📚 Documentation
|
||||||
|
|
||||||
|
- Update changelog
|
||||||
|
|
||||||
|
## [4.0.0-beta.416] - 2025-05-05
|
||||||
|
|
||||||
|
### 📚 Documentation
|
||||||
|
|
||||||
|
- Update changelog
|
||||||
|
- Update changelog
|
||||||
|
|
||||||
|
## [4.0.0-beta.415] - 2025-04-29
|
||||||
|
|
||||||
|
### 📚 Documentation
|
||||||
|
|
||||||
|
- Update changelog
|
||||||
|
|
||||||
|
## [4.0.0-beta.413] - 2025-04-28
|
||||||
|
|
||||||
|
### 💼 Other
|
||||||
|
|
||||||
|
- Adjust Workflows for v5 (#5689)
|
||||||
|
|
||||||
|
### 📚 Documentation
|
||||||
|
|
||||||
|
- Update changelog
|
||||||
|
- Update changelog
|
||||||
|
|
||||||
|
### ⚙️ Miscellaneous Tasks
|
||||||
|
|
||||||
|
- *(workflows)* Adjust workflow for announcement
|
||||||
|
|
||||||
|
## [4.0.0-beta.412] - 2025-04-23
|
||||||
|
|
||||||
|
### ⚙️ Miscellaneous Tasks
|
||||||
|
|
||||||
|
- *(versions)* Update coolify version to 4.0.0-beta.412 and nightly version to 4.0.0-beta.413 in configuration files
|
||||||
|
|
||||||
|
## [4.0.0-beta.411] - 2025-04-23
|
||||||
|
|
||||||
|
### 🚀 Features
|
||||||
|
|
||||||
|
- *(deployment)* Add repository_project_id handling for private GitHub apps and clean up unused Caddy label logic
|
||||||
|
- *(api)* Enhance OpenAPI specifications with token variable and additional key attributes
|
||||||
|
- *(docker)* Add HTTP Basic Authentication support and enhance hostname parsing in Docker run conversion
|
||||||
|
- *(api)* Add HTTP Basic Authentication fields to OpenAPI specifications and enhance PrivateKey model descriptions
|
||||||
|
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
|
- *(backup-edit)* Conditionally enable S3 checkbox based on available validated S3 storage
|
||||||
|
- *(source)* Update no sources found message for clarity
|
||||||
|
- *(api)* Correct middleware for service update route to ensure proper permissions
|
||||||
|
- *(api)* Handle JSON response in service creation and update methods for improved error handling
|
||||||
|
- Add 201 json code to servers validate api response
|
||||||
|
- *(docker)* Ensure password hashing only occurs when HTTP Basic Authentication is enabled
|
||||||
|
- *(docker)* Enhance hostname and GPU option validation in Docker run to compose conversion
|
||||||
|
|
||||||
|
### 🚜 Refactor
|
||||||
|
|
||||||
|
- *(jobs)* Comment out unused Caddy label handling in ApplicationDeploymentJob and simplify proxy path logic in Server model
|
||||||
|
- *(database)* Simplify database type checks in ServiceDatabase and enhance image validation in Docker helper
|
||||||
|
- *(shared)* Remove unused ray debugging statement from newParser function
|
||||||
|
- *(applications)* Remove redundant error response in create_env method
|
||||||
|
- *(api)* Restructure routes to include versioning and maintain existing feedback endpoint
|
||||||
|
- *(api)* Remove token variable from OpenAPI specifications for clarity
|
||||||
|
- *(environment-variables)* Remove protected variable checks from delete methods for cleaner logic
|
||||||
|
- *(http-basic-auth)* Rename 'http_basic_auth_enable' to 'http_basic_auth_enabled' across application files for consistency
|
||||||
|
- *(docker)* Remove debug statement and enhance hostname handling in Docker run conversion
|
||||||
|
- *(server)* Simplify proxy path logic and remove unnecessary conditions
|
||||||
|
|
||||||
|
### 📚 Documentation
|
||||||
|
|
||||||
|
- Update changelog
|
||||||
|
- Update changelog
|
||||||
|
- Update changelog
|
||||||
|
|
||||||
|
### ⚙️ Miscellaneous Tasks
|
||||||
|
|
||||||
|
- *(versions)* Update coolify version to 4.0.0-beta.411 and nightly version to 4.0.0-beta.412 in configuration files
|
||||||
|
|
||||||
|
## [4.0.0-beta.410] - 2025-04-18
|
||||||
|
|
||||||
|
### 🚀 Features
|
||||||
|
|
||||||
|
- Add HTTP Basic Authentication
|
||||||
|
- *(readme)* Add new sponsors Supadata AI and WZ-IT to the README
|
||||||
|
- *(core)* Enable magic env variables for compose based applications
|
||||||
|
|
||||||
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
|
- *(application)* Append base directory to git branch URLs for improved path handling
|
||||||
|
- *(templates)* Correct casing of "denokv" to "denoKV" in service templates JSON
|
||||||
- *(navbar)* Update error message link to use route for environment variables navigation
|
- *(navbar)* Update error message link to use route for environment variables navigation
|
||||||
- Unsend template
|
- Unsend template
|
||||||
- Replace ports with expose
|
- Replace ports with expose
|
||||||
@@ -19,14 +109,33 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
- Update changelog
|
- Update changelog
|
||||||
|
|
||||||
|
### ⚙️ Miscellaneous Tasks
|
||||||
|
|
||||||
|
- *(versions)* Bump coolify version to 4.0.0-beta.410 and update nightly version to 4.0.0-beta.411 in configuration files
|
||||||
|
- *(templates)* Update plausible and clickhouse images to latest versions and remove mail service
|
||||||
|
|
||||||
|
## [4.0.0-beta.409] - 2025-04-16
|
||||||
|
|
||||||
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
|
- *(parser)* Transform associative array labels into key=value format for better compatibility
|
||||||
|
- *(redis)* Update username and password input handling to clarify database sync requirements
|
||||||
|
- *(source)* Update connected source display to handle cases with no source connected
|
||||||
|
|
||||||
|
### 🚜 Refactor
|
||||||
|
|
||||||
|
- *(source)* Conditionally display connected source and change source options based on private key presence
|
||||||
|
|
||||||
|
### ⚙️ Miscellaneous Tasks
|
||||||
|
|
||||||
|
- *(versions)* Bump coolify version to 4.0.0-beta.409 in configuration files
|
||||||
|
|
||||||
## [4.0.0-beta.408] - 2025-04-14
|
## [4.0.0-beta.408] - 2025-04-14
|
||||||
|
|
||||||
### 🚀 Features
|
### 🚀 Features
|
||||||
|
|
||||||
- *(OpenApi)* Enhance OpenAPI specifications by adding UUID parameters for application, project, and service updates; improve deployment listing with pagination parameters; update command signature for OpenApi generation
|
- *(OpenApi)* Enhance OpenAPI specifications by adding UUID parameters for application, project, and service updates; improve deployment listing with pagination parameters; update command signature for OpenApi generation
|
||||||
- *(subscription)* Enhance subscription management with loading states and Stripe status checks
|
- *(subscription)* Enhance subscription management with loading states and Stripe status checks
|
||||||
- *(readme)* Add new sponsors Supadata AI and WZ-IT to the README
|
|
||||||
- *(core)* Enable magic env variables for compose based applications
|
|
||||||
|
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
@@ -36,11 +145,6 @@ All notable changes to this project will be documented in this file.
|
|||||||
- *(mongodb)* Also apply custom config when SSL is enabled
|
- *(mongodb)* Also apply custom config when SSL is enabled
|
||||||
- *(templates)* Correct casing of denoKV references in service templates and YAML files
|
- *(templates)* Correct casing of denoKV references in service templates and YAML files
|
||||||
- *(deployment)* Handle missing destination in deployment process to prevent errors
|
- *(deployment)* Handle missing destination in deployment process to prevent errors
|
||||||
- *(parser)* Transform associative array labels into key=value format for better compatibility
|
|
||||||
- *(redis)* Update username and password input handling to clarify database sync requirements
|
|
||||||
- *(source)* Update connected source display to handle cases with no source connected
|
|
||||||
- *(application)* Append base directory to git branch URLs for improved path handling
|
|
||||||
- *(templates)* Correct casing of "denokv" to "denoKV" in service templates JSON
|
|
||||||
|
|
||||||
### 💼 Other
|
### 💼 Other
|
||||||
|
|
||||||
@@ -52,7 +156,6 @@ All notable changes to this project will be documented in this file.
|
|||||||
- *(Dockerfile)* Remove service generation command from the build process to streamline Dockerfile and improve build efficiency
|
- *(Dockerfile)* Remove service generation command from the build process to streamline Dockerfile and improve build efficiency
|
||||||
- *(navbar-delete-team)* Simplify modal confirmation layout and enhance button styling for better user experience
|
- *(navbar-delete-team)* Simplify modal confirmation layout and enhance button styling for better user experience
|
||||||
- *(Server)* Remove debug logging from isReachableChanged method to clean up code and improve performance
|
- *(Server)* Remove debug logging from isReachableChanged method to clean up code and improve performance
|
||||||
- *(source)* Conditionally display connected source and change source options based on private key presence
|
|
||||||
|
|
||||||
### 📚 Documentation
|
### 📚 Documentation
|
||||||
|
|
||||||
@@ -65,9 +168,6 @@ All notable changes to this project will be documented in this file.
|
|||||||
- *(versions)* Update nightly version to 4.0.0-beta.410
|
- *(versions)* Update nightly version to 4.0.0-beta.410
|
||||||
- *(pre-commit)* Remove OpenAPI generation command from pre-commit hook
|
- *(pre-commit)* Remove OpenAPI generation command from pre-commit hook
|
||||||
- *(versions)* Update realtime version to 1.0.7 and bump dependencies in package.json
|
- *(versions)* Update realtime version to 1.0.7 and bump dependencies in package.json
|
||||||
- *(versions)* Bump coolify version to 4.0.0-beta.409 in configuration files
|
|
||||||
- *(versions)* Bump coolify version to 4.0.0-beta.410 and update nightly version to 4.0.0-beta.411 in configuration files
|
|
||||||
- *(templates)* Update plausible and clickhouse images to latest versions and remove mail service
|
|
||||||
|
|
||||||
## [4.0.0-beta.407] - 2025-04-09
|
## [4.0.0-beta.407] - 2025-04-09
|
||||||
|
|
||||||
@@ -128,6 +228,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
### 🚀 Features
|
### 🚀 Features
|
||||||
|
|
||||||
- *(api)* Update OpenAPI spec for services (#5448)
|
- *(api)* Update OpenAPI spec for services (#5448)
|
||||||
|
- *(proxy)* Enhance proxy handling and port conflict detection
|
||||||
|
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
@@ -140,6 +241,10 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
### 📚 Documentation
|
### 📚 Documentation
|
||||||
|
|
||||||
|
- Update changelog
|
||||||
|
- Update changelog
|
||||||
|
- Update changelog
|
||||||
|
- Update changelog
|
||||||
- Update changelog
|
- Update changelog
|
||||||
- Update changelog
|
- Update changelog
|
||||||
- Update changelog
|
- Update changelog
|
||||||
@@ -155,15 +260,12 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
### 🚀 Features
|
### 🚀 Features
|
||||||
|
|
||||||
- *(proxy)* Enhance proxy handling and port conflict detection
|
|
||||||
- *(lang)* Added Azerbaijani language updated turkish language. (#5497)
|
- *(lang)* Added Azerbaijani language updated turkish language. (#5497)
|
||||||
- *(lang)* Added Portuguese from Brazil language (#5500)
|
- *(lang)* Added Portuguese from Brazil language (#5500)
|
||||||
- *(lang)* Add Indonesian language translations (#5513)
|
- *(lang)* Add Indonesian language translations (#5513)
|
||||||
|
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
- *(database)* Custom config for MongoDB (#5471)
|
|
||||||
- *(ui)* Instance Backup settings
|
|
||||||
- *(docs)* Comment out execute for now
|
- *(docs)* Comment out execute for now
|
||||||
- *(installation)* Mount the docker config
|
- *(installation)* Mount the docker config
|
||||||
- *(installation)* Path to config file for docker login
|
- *(installation)* Path to config file for docker login
|
||||||
@@ -173,13 +275,10 @@ All notable changes to this project will be documented in this file.
|
|||||||
- *(docs)* Contribute service url (#5517)
|
- *(docs)* Contribute service url (#5517)
|
||||||
- *(proxy)* Proxy restart does not work on domain
|
- *(proxy)* Proxy restart does not work on domain
|
||||||
- *(ui)* Only show copy button on https
|
- *(ui)* Only show copy button on https
|
||||||
|
- *(database)* Custom config for MongoDB (#5471)
|
||||||
|
|
||||||
### 📚 Documentation
|
### 📚 Documentation
|
||||||
|
|
||||||
- Update changelog
|
|
||||||
- Update changelog
|
|
||||||
- Update changelog
|
|
||||||
- Update changelog
|
|
||||||
- Update changelog
|
- Update changelog
|
||||||
- Update changelog
|
- Update changelog
|
||||||
- Update changelog
|
- Update changelog
|
||||||
@@ -187,10 +286,9 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
### ⚙️ Miscellaneous Tasks
|
### ⚙️ Miscellaneous Tasks
|
||||||
|
|
||||||
- *(versions)* Bump version to 403 (#5520)
|
|
||||||
- *(versions)* Update coolify version numbers to 4.0.0-beta.403 and 4.0.0-beta.404
|
|
||||||
- *(service)* Remove unused code in Bugsink service
|
- *(service)* Remove unused code in Bugsink service
|
||||||
- *(versions)* Update version to 404
|
- *(versions)* Update version to 404
|
||||||
|
- *(versions)* Bump version to 403 (#5520)
|
||||||
- *(versions)* Bump version to 404
|
- *(versions)* Bump version to 404
|
||||||
|
|
||||||
## [4.0.0-beta.402] - 2025-04-01
|
## [4.0.0-beta.402] - 2025-04-01
|
||||||
@@ -210,6 +308,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
- *(DeployController)* Cast 'pr' query parameter to integer
|
- *(DeployController)* Cast 'pr' query parameter to integer
|
||||||
- *(deploy)* Validate team ID before deployment
|
- *(deploy)* Validate team ID before deployment
|
||||||
- *(wakapi)* Typo in env variables and add some useful variables to wakapi.yaml (#5424)
|
- *(wakapi)* Typo in env variables and add some useful variables to wakapi.yaml (#5424)
|
||||||
|
- *(ui)* Instance Backup settings
|
||||||
|
|
||||||
### 🚜 Refactor
|
### 🚜 Refactor
|
||||||
|
|
||||||
@@ -223,6 +322,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
- *(service)* Add google variables to plausible.yaml (#5429)
|
- *(service)* Add google variables to plausible.yaml (#5429)
|
||||||
- *(service)* Update authentik.yaml versions (#5373)
|
- *(service)* Update authentik.yaml versions (#5373)
|
||||||
- *(core)* Remove redocs
|
- *(core)* Remove redocs
|
||||||
|
- *(versions)* Update coolify version numbers to 4.0.0-beta.403 and 4.0.0-beta.404
|
||||||
|
|
||||||
## [4.0.0-beta.401] - 2025-03-28
|
## [4.0.0-beta.401] - 2025-03-28
|
||||||
|
|
||||||
@@ -293,14 +393,6 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
### 🚀 Features
|
### 🚀 Features
|
||||||
|
|
||||||
- *(github-source)* Enhance GitHub App configuration with manual and private key support
|
|
||||||
- *(ui)* Improve GitHub repository selection and styling
|
|
||||||
- *(database)* Implement two-step confirmation for database deletion
|
|
||||||
- *(assets)* Add new SVG logo for Coolify
|
|
||||||
- *(install)* Enhance Docker address pool configuration and validation
|
|
||||||
- *(install)* Improve Docker address pool management and service restart logic
|
|
||||||
- *(install)* Add missing env variable to install script
|
|
||||||
- *(LocalFileVolume)* Add binary file detection and update UI logic
|
|
||||||
- *(service)* Neon
|
- *(service)* Neon
|
||||||
- *(migration)* Add `ssl_certificates` table and model
|
- *(migration)* Add `ssl_certificates` table and model
|
||||||
- *(migration)* Add ssl setting to `standalone_postgresqls` table
|
- *(migration)* Add ssl setting to `standalone_postgresqls` table
|
||||||
@@ -342,6 +434,14 @@ All notable changes to this project will be documented in this file.
|
|||||||
- *(ssl)* Improve Redis and remove modes
|
- *(ssl)* Improve Redis and remove modes
|
||||||
- Full SSL support for DrangonflyDB
|
- Full SSL support for DrangonflyDB
|
||||||
- SSL notification
|
- SSL notification
|
||||||
|
- *(github-source)* Enhance GitHub App configuration with manual and private key support
|
||||||
|
- *(ui)* Improve GitHub repository selection and styling
|
||||||
|
- *(database)* Implement two-step confirmation for database deletion
|
||||||
|
- *(assets)* Add new SVG logo for Coolify
|
||||||
|
- *(install)* Enhance Docker address pool configuration and validation
|
||||||
|
- *(install)* Improve Docker address pool management and service restart logic
|
||||||
|
- *(install)* Add missing env variable to install script
|
||||||
|
- *(LocalFileVolume)* Add binary file detection and update UI logic
|
||||||
- *(templates)* Change glance for v0.7
|
- *(templates)* Change glance for v0.7
|
||||||
- *(templates)* Add Freescout service template
|
- *(templates)* Add Freescout service template
|
||||||
- *(service)* Add Evolution API template
|
- *(service)* Add Evolution API template
|
||||||
@@ -359,18 +459,6 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
- *(api)* Docker compose based apps creationg through api
|
- *(api)* Docker compose based apps creationg through api
|
||||||
- *(database)* Improve database type detection for Supabase Postgres images
|
- *(database)* Improve database type detection for Supabase Postgres images
|
||||||
- *(ui)* Correct grammatical error in 404 page
|
|
||||||
- *(seeder)* Update GitHub app name in GithubAppSeeder
|
|
||||||
- *(plane)* Update APP_RELEASE to v0.25.2 in environment configuration
|
|
||||||
- *(domain)* Dispatch refreshStatus event after successful domain update
|
|
||||||
- *(database)* Correct container name generation for service databases
|
|
||||||
- *(database)* Limit container name length for database proxy
|
|
||||||
- *(database)* Handle unsupported database types in StartDatabaseProxy
|
|
||||||
- *(database)* Simplify container name generation in StartDatabaseProxy
|
|
||||||
- *(install)* Handle potential errors in Docker address pool configuration
|
|
||||||
- *(backups)* Retention settings
|
|
||||||
- *(redis)* Set default redis_username for new instances
|
|
||||||
- *(core)* Improve instantSave logic and error handling
|
|
||||||
- *(ssl)* Permission of ssl crt and key inside the container
|
- *(ssl)* Permission of ssl crt and key inside the container
|
||||||
- *(ui)* Make sure file mounts do not showing the encrypted values
|
- *(ui)* Make sure file mounts do not showing the encrypted values
|
||||||
- *(ssl)* Make default ssl mode require not verify-full as it does not need a ca cert
|
- *(ssl)* Make default ssl mode require not verify-full as it does not need a ca cert
|
||||||
@@ -410,6 +498,18 @@ All notable changes to this project will be documented in this file.
|
|||||||
- *(ssl)* Add `--tls` arg to DrangflyDB
|
- *(ssl)* Add `--tls` arg to DrangflyDB
|
||||||
- *(notification)* Always send SSL notifications
|
- *(notification)* Always send SSL notifications
|
||||||
- *(database)* Change default value of enable_ssl to false for multiple tables
|
- *(database)* Change default value of enable_ssl to false for multiple tables
|
||||||
|
- *(ui)* Correct grammatical error in 404 page
|
||||||
|
- *(seeder)* Update GitHub app name in GithubAppSeeder
|
||||||
|
- *(plane)* Update APP_RELEASE to v0.25.2 in environment configuration
|
||||||
|
- *(domain)* Dispatch refreshStatus event after successful domain update
|
||||||
|
- *(database)* Correct container name generation for service databases
|
||||||
|
- *(database)* Limit container name length for database proxy
|
||||||
|
- *(database)* Handle unsupported database types in StartDatabaseProxy
|
||||||
|
- *(database)* Simplify container name generation in StartDatabaseProxy
|
||||||
|
- *(install)* Handle potential errors in Docker address pool configuration
|
||||||
|
- *(backups)* Retention settings
|
||||||
|
- *(redis)* Set default redis_username for new instances
|
||||||
|
- *(core)* Improve instantSave logic and error handling
|
||||||
- *(general)* Correct link to framework specific documentation
|
- *(general)* Correct link to framework specific documentation
|
||||||
- *(core)* Redirect healthcheck route for dockercompose applications
|
- *(core)* Redirect healthcheck route for dockercompose applications
|
||||||
- *(api)* Use name from request payload
|
- *(api)* Use name from request payload
|
||||||
@@ -458,7 +558,6 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
### ⚙️ Miscellaneous Tasks
|
### ⚙️ Miscellaneous Tasks
|
||||||
|
|
||||||
- *(supabase)* Update Supabase service template and Postgres image version
|
|
||||||
- *(migration)* Remove unused columns
|
- *(migration)* Remove unused columns
|
||||||
- *(ssl)* Improve code in ssl helper
|
- *(ssl)* Improve code in ssl helper
|
||||||
- *(migration)* Ssl cert and key should not be nullable
|
- *(migration)* Ssl cert and key should not be nullable
|
||||||
@@ -466,6 +565,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
- Rename ca crt folder to ssl
|
- Rename ca crt folder to ssl
|
||||||
- *(ui)* Improve valid until handling
|
- *(ui)* Improve valid until handling
|
||||||
- Improve code quality suggested by code rabbit
|
- Improve code quality suggested by code rabbit
|
||||||
|
- *(supabase)* Update Supabase service template and Postgres image version
|
||||||
- *(versions)* Update version numbers for coolify and nightly
|
- *(versions)* Update version numbers for coolify and nightly
|
||||||
|
|
||||||
## [4.0.0-beta.398] - 2025-03-01
|
## [4.0.0-beta.398] - 2025-03-01
|
||||||
@@ -858,14 +958,6 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
### 🚀 Features
|
### 🚀 Features
|
||||||
|
|
||||||
- New ServerReachabilityChanged event
|
|
||||||
- Use new ServerReachabilityChanged event instead of isDirty
|
|
||||||
- Add infomaniak oauth
|
|
||||||
- Add server disk usage check frequency
|
|
||||||
- Add environment_uuid support and update API documentation
|
|
||||||
- Add service/resource/project labels
|
|
||||||
- Add coolify.environment label
|
|
||||||
- Add database subtype
|
|
||||||
- Able to import full db backups for pg/mysql/mariadb
|
- Able to import full db backups for pg/mysql/mariadb
|
||||||
- Restore backup from server file
|
- Restore backup from server file
|
||||||
- Docker volume data cloning
|
- Docker volume data cloning
|
||||||
@@ -901,35 +993,6 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
- Fallback for copy button
|
|
||||||
- Copy the right text
|
|
||||||
- Maybe fallback is now working
|
|
||||||
- Only show copy button on secure context
|
|
||||||
- Render html on error page correctly
|
|
||||||
- Invalid API response on missing project
|
|
||||||
- Applications API response code + schema
|
|
||||||
- Applications API writing to unavailable models
|
|
||||||
- If an init script is renamed the old version is still on the server
|
|
||||||
- Oauthseeder
|
|
||||||
- Compose loading seq
|
|
||||||
- Resource clone name + volume name generation
|
|
||||||
- Update Dockerfile entrypoint path to /etc/entrypoint.d
|
|
||||||
- Debug mode
|
|
||||||
- Unreachable notifications
|
|
||||||
- Remove duplicated ServerCheckJob call
|
|
||||||
- Few fixes and use new ServerReachabilityChanged event
|
|
||||||
- Use serverStatus not just status
|
|
||||||
- Oauth seeder
|
|
||||||
- Service ui structure
|
|
||||||
- Check port 8080 and fallback to 80
|
|
||||||
- Refactor database view
|
|
||||||
- Always use docker cleanup frequency
|
|
||||||
- Advanced server UI
|
|
||||||
- Html css
|
|
||||||
- Fix domain being override when update application
|
|
||||||
- Use nixpacks predefined build variables, but still could update the default values from Coolify
|
|
||||||
- Use local monaco-editor instead of Cloudflare
|
|
||||||
- N8n timezone
|
|
||||||
- Compose envs
|
- Compose envs
|
||||||
- Scheduled tasks and backups are executed by server timezone.
|
- Scheduled tasks and backups are executed by server timezone.
|
||||||
- Show backup timezone on the UI
|
- Show backup timezone on the UI
|
||||||
@@ -1029,7 +1092,6 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
### 🚜 Refactor
|
### 🚜 Refactor
|
||||||
|
|
||||||
- Rename `coolify.environment` to `coolify.environmentName`
|
|
||||||
- Rename parameter in DatabaseBackupJob for clarity
|
- Rename parameter in DatabaseBackupJob for clarity
|
||||||
- Improve checkbox component accessibility and styling
|
- Improve checkbox component accessibility and styling
|
||||||
- Remove unused tags method from ApplicationDeploymentJob
|
- Remove unused tags method from ApplicationDeploymentJob
|
||||||
@@ -1045,9 +1107,6 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
### ⚙️ Miscellaneous Tasks
|
### ⚙️ Miscellaneous Tasks
|
||||||
|
|
||||||
- Regenerate API spec, removing notification fields
|
|
||||||
- Remove ray debugging
|
|
||||||
- Version ++
|
|
||||||
- Improve Penpot healthchecks
|
- Improve Penpot healthchecks
|
||||||
- Switch up readonly lables to make more sense
|
- Switch up readonly lables to make more sense
|
||||||
- Remove unused computed fields
|
- Remove unused computed fields
|
||||||
@@ -1071,11 +1130,44 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
### 🚀 Features
|
### 🚀 Features
|
||||||
|
|
||||||
|
- New ServerReachabilityChanged event
|
||||||
|
- Use new ServerReachabilityChanged event instead of isDirty
|
||||||
|
- Add infomaniak oauth
|
||||||
|
- Add server disk usage check frequency
|
||||||
|
- Add environment_uuid support and update API documentation
|
||||||
|
- Add service/resource/project labels
|
||||||
|
- Add coolify.environment label
|
||||||
|
- Add database subtype
|
||||||
- Migrate to new encryption options
|
- Migrate to new encryption options
|
||||||
- New encryption options
|
- New encryption options
|
||||||
|
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
|
- Render html on error page correctly
|
||||||
|
- Invalid API response on missing project
|
||||||
|
- Applications API response code + schema
|
||||||
|
- Applications API writing to unavailable models
|
||||||
|
- If an init script is renamed the old version is still on the server
|
||||||
|
- Oauthseeder
|
||||||
|
- Compose loading seq
|
||||||
|
- Resource clone name + volume name generation
|
||||||
|
- Update Dockerfile entrypoint path to /etc/entrypoint.d
|
||||||
|
- Debug mode
|
||||||
|
- Unreachable notifications
|
||||||
|
- Remove duplicated ServerCheckJob call
|
||||||
|
- Few fixes and use new ServerReachabilityChanged event
|
||||||
|
- Use serverStatus not just status
|
||||||
|
- Oauth seeder
|
||||||
|
- Service ui structure
|
||||||
|
- Check port 8080 and fallback to 80
|
||||||
|
- Refactor database view
|
||||||
|
- Always use docker cleanup frequency
|
||||||
|
- Advanced server UI
|
||||||
|
- Html css
|
||||||
|
- Fix domain being override when update application
|
||||||
|
- Use nixpacks predefined build variables, but still could update the default values from Coolify
|
||||||
|
- Use local monaco-editor instead of Cloudflare
|
||||||
|
- N8n timezone
|
||||||
- Smtp encryption
|
- Smtp encryption
|
||||||
- Bind() to 0.0.0.0:80 failed
|
- Bind() to 0.0.0.0:80 failed
|
||||||
- Oauth seeder
|
- Oauth seeder
|
||||||
@@ -1085,11 +1177,15 @@ All notable changes to this project will be documented in this file.
|
|||||||
- Error message
|
- Error message
|
||||||
- Update healthcheck and port configurations to use port 8080
|
- Update healthcheck and port configurations to use port 8080
|
||||||
|
|
||||||
## [4.0.0-beta.379] - 2024-12-13
|
### 🚜 Refactor
|
||||||
|
|
||||||
### 🐛 Bug Fixes
|
- Rename `coolify.environment` to `coolify.environmentName`
|
||||||
|
|
||||||
- Saving oauth
|
### ⚙️ Miscellaneous Tasks
|
||||||
|
|
||||||
|
- Regenerate API spec, removing notification fields
|
||||||
|
- Remove ray debugging
|
||||||
|
- Version ++
|
||||||
|
|
||||||
## [4.0.0-beta.378] - 2024-12-13
|
## [4.0.0-beta.378] - 2024-12-13
|
||||||
|
|
||||||
@@ -1098,6 +1194,11 @@ All notable changes to this project will be documented in this file.
|
|||||||
- Monaco editor light and dark mode switching
|
- Monaco editor light and dark mode switching
|
||||||
- Service status indicator + oauth saving
|
- Service status indicator + oauth saving
|
||||||
- Socialite for azure and authentik
|
- Socialite for azure and authentik
|
||||||
|
- Saving oauth
|
||||||
|
- Fallback for copy button
|
||||||
|
- Copy the right text
|
||||||
|
- Maybe fallback is now working
|
||||||
|
- Only show copy button on secure context
|
||||||
|
|
||||||
## [4.0.0-beta.377] - 2024-12-13
|
## [4.0.0-beta.377] - 2024-12-13
|
||||||
|
|
||||||
|
|||||||
2
LICENSE
2
LICENSE
@@ -186,7 +186,7 @@
|
|||||||
same "printed page" as the copyright notice for easier
|
same "printed page" as the copyright notice for easier
|
||||||
identification within third-party archives.
|
identification within third-party archives.
|
||||||
|
|
||||||
Copyright [2022] [Andras Bacsai]
|
Copyright [2025] [Andras Bacsai]
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ Thank you so much!
|
|||||||
* [Tolgee](https://tolgee.io?ref=coolify.io) - The open source localization platform
|
* [Tolgee](https://tolgee.io?ref=coolify.io) - The open source localization platform
|
||||||
* [CompAI](https://www.trycomp.ai?ref=coolify.io) - Open source compliance automation platform
|
* [CompAI](https://www.trycomp.ai?ref=coolify.io) - Open source compliance automation platform
|
||||||
* [GoldenVM](https://billing.goldenvm.com?ref=coolify.io) - Premium virtual machine hosting solutions
|
* [GoldenVM](https://billing.goldenvm.com?ref=coolify.io) - Premium virtual machine hosting solutions
|
||||||
|
* [Gozunga](https://gozunga.com?ref=coolify.io) - Seriously Simple Cloud Infrastructure
|
||||||
|
* [Macarne](https://macarne.com?ref=coolify.io) - Best IP Transit & Carrier Ethernet Solutions for Simplified Network Connectivity
|
||||||
|
|
||||||
## Small Sponsors
|
## Small Sponsors
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class StopApplication
|
|||||||
$application->stopContainers($containersToStop, $server);
|
$application->stopContainers($containersToStop, $server);
|
||||||
|
|
||||||
if ($application->build_pack === 'dockercompose') {
|
if ($application->build_pack === 'dockercompose') {
|
||||||
$application->delete_connected_networks($application->uuid);
|
$application->deleteConnectedNetworks();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($dockerCleanup) {
|
if ($dockerCleanup) {
|
||||||
|
|||||||
@@ -85,7 +85,6 @@ class RunRemoteProcess
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$processResult = $process->wait();
|
$processResult = $process->wait();
|
||||||
// $processResult = Process::timeout($timeout)->run($this->getCommand(), $this->handleOutput(...));
|
|
||||||
if ($this->activity->properties->get('status') === ProcessStatus::ERROR->value) {
|
if ($this->activity->properties->get('status') === ProcessStatus::ERROR->value) {
|
||||||
$status = ProcessStatus::ERROR;
|
$status = ProcessStatus::ERROR;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ use App\Models\StandaloneMongodb;
|
|||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
use App\Models\StandaloneRedis;
|
use App\Models\StandaloneRedis;
|
||||||
use Illuminate\Support\Facades\Process;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class StopDatabase
|
class StopDatabase
|
||||||
@@ -25,7 +24,7 @@ class StopDatabase
|
|||||||
return 'Server is not functional';
|
return 'Server is not functional';
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->stopContainer($database, $database->uuid, 300);
|
$this->stopContainer($database, $database->uuid, 30);
|
||||||
if ($isDeleteOperation) {
|
if ($isDeleteOperation) {
|
||||||
if ($dockerCleanup) {
|
if ($dockerCleanup) {
|
||||||
CleanupDocker::dispatch($server, true);
|
CleanupDocker::dispatch($server, true);
|
||||||
@@ -39,37 +38,12 @@ class StopDatabase
|
|||||||
return 'Database stopped successfully';
|
return 'Database stopped successfully';
|
||||||
}
|
}
|
||||||
|
|
||||||
private function stopContainer($database, string $containerName, int $timeout = 300): void
|
private function stopContainer($database, string $containerName, int $timeout = 30): void
|
||||||
{
|
{
|
||||||
$server = $database->destination->server;
|
$server = $database->destination->server;
|
||||||
|
instant_remote_process(command: [
|
||||||
$process = Process::timeout($timeout)->start("docker stop --time=$timeout $containerName");
|
"docker stop --time=$timeout $containerName",
|
||||||
|
"docker rm -f $containerName",
|
||||||
$startTime = time();
|
], server: $server, throwError: false);
|
||||||
while ($process->running()) {
|
|
||||||
if (time() - $startTime >= $timeout) {
|
|
||||||
$this->forceStopContainer($containerName, $server);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
usleep(100000);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->removeContainer($containerName, $server);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function forceStopContainer(string $containerName, $server): void
|
|
||||||
{
|
|
||||||
instant_remote_process(command: ["docker kill $containerName"], server: $server, throwError: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function removeContainer(string $containerName, $server): void
|
|
||||||
{
|
|
||||||
instant_remote_process(command: ["docker rm -f $containerName"], server: $server, throwError: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function deleteConnectedNetworks($uuid, $server)
|
|
||||||
{
|
|
||||||
instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false);
|
|
||||||
instant_remote_process(["docker network rm {$uuid}"], $server, false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,54 +3,27 @@
|
|||||||
namespace App\Actions\Proxy;
|
namespace App\Actions\Proxy;
|
||||||
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Carbon\Carbon;
|
|
||||||
use Illuminate\Process\InvokedProcess;
|
|
||||||
use Illuminate\Support\Facades\Process;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class StopProxy
|
class StopProxy
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Server $server, bool $forceStop = true)
|
public function handle(Server $server, bool $forceStop = true, int $timeout = 30)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$containerName = $server->isSwarm() ? 'coolify-proxy_traefik' : 'coolify-proxy';
|
$containerName = $server->isSwarm() ? 'coolify-proxy_traefik' : 'coolify-proxy';
|
||||||
$timeout = 30;
|
|
||||||
|
|
||||||
$process = $this->stopContainer($containerName, $timeout);
|
instant_remote_process(command: [
|
||||||
|
"docker stop --time=$timeout $containerName",
|
||||||
|
"docker rm -f $containerName",
|
||||||
|
], server: $server, throwError: false);
|
||||||
|
|
||||||
$startTime = Carbon::now()->getTimestamp();
|
|
||||||
while ($process->running()) {
|
|
||||||
if (Carbon::now()->getTimestamp() - $startTime >= $timeout) {
|
|
||||||
$this->forceStopContainer($containerName, $server);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
usleep(100000);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->removeContainer($containerName, $server);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
return handleError($e);
|
|
||||||
} finally {
|
|
||||||
$server->proxy->force_stop = $forceStop;
|
$server->proxy->force_stop = $forceStop;
|
||||||
$server->proxy->status = 'exited';
|
$server->proxy->status = 'exited';
|
||||||
$server->save();
|
$server->save();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function stopContainer(string $containerName, int $timeout): InvokedProcess
|
|
||||||
{
|
|
||||||
return Process::timeout($timeout)->start("docker stop --time=$timeout $containerName");
|
|
||||||
}
|
|
||||||
|
|
||||||
private function forceStopContainer(string $containerName, Server $server)
|
|
||||||
{
|
|
||||||
instant_remote_process(["docker kill $containerName"], $server, throwError: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function removeContainer(string $containerName, Server $server)
|
|
||||||
{
|
|
||||||
instant_remote_process(["docker rm -f $containerName"], $server, throwError: false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,7 +99,8 @@ class ServerCheck
|
|||||||
return data_get($value, 'Name') === '/coolify-proxy';
|
return data_get($value, 'Name') === '/coolify-proxy';
|
||||||
}
|
}
|
||||||
})->first();
|
})->first();
|
||||||
if (! $foundProxyContainer) {
|
$proxyStatus = data_get($foundProxyContainer, 'State.Status', 'exited');
|
||||||
|
if (! $foundProxyContainer || $proxyStatus !== 'running') {
|
||||||
try {
|
try {
|
||||||
$shouldStart = CheckProxy::run($this->server);
|
$shouldStart = CheckProxy::run($this->server);
|
||||||
if ($shouldStart) {
|
if ($shouldStart) {
|
||||||
|
|||||||
@@ -15,19 +15,18 @@ class StartLogDrain
|
|||||||
{
|
{
|
||||||
if ($server->settings->is_logdrain_newrelic_enabled) {
|
if ($server->settings->is_logdrain_newrelic_enabled) {
|
||||||
$type = 'newrelic';
|
$type = 'newrelic';
|
||||||
StopLogDrain::run($server);
|
|
||||||
} elseif ($server->settings->is_logdrain_highlight_enabled) {
|
} elseif ($server->settings->is_logdrain_highlight_enabled) {
|
||||||
$type = 'highlight';
|
$type = 'highlight';
|
||||||
StopLogDrain::run($server);
|
|
||||||
} elseif ($server->settings->is_logdrain_axiom_enabled) {
|
} elseif ($server->settings->is_logdrain_axiom_enabled) {
|
||||||
$type = 'axiom';
|
$type = 'axiom';
|
||||||
StopLogDrain::run($server);
|
|
||||||
} elseif ($server->settings->is_logdrain_custom_enabled) {
|
} elseif ($server->settings->is_logdrain_custom_enabled) {
|
||||||
$type = 'custom';
|
$type = 'custom';
|
||||||
StopLogDrain::run($server);
|
|
||||||
} else {
|
} else {
|
||||||
$type = 'none';
|
$type = 'none';
|
||||||
}
|
}
|
||||||
|
if ($type !== 'none') {
|
||||||
|
StopLogDrain::run($server);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if ($type === 'none') {
|
if ($type === 'none') {
|
||||||
return 'No log drain is enabled.';
|
return 'No log drain is enabled.';
|
||||||
@@ -186,7 +185,6 @@ Files:
|
|||||||
"echo '{$compose}' | base64 -d | tee $compose_path > /dev/null",
|
"echo '{$compose}' | base64 -d | tee $compose_path > /dev/null",
|
||||||
"echo '{$readme}' | base64 -d | tee $readme_path > /dev/null",
|
"echo '{$readme}' | base64 -d | tee $readme_path > /dev/null",
|
||||||
"test -f $config_path/.env && rm $config_path/.env",
|
"test -f $config_path/.env && rm $config_path/.env",
|
||||||
|
|
||||||
];
|
];
|
||||||
if ($type === 'newrelic') {
|
if ($type === 'newrelic') {
|
||||||
$add_envs_command = [
|
$add_envs_command = [
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class DeleteService
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($deleteConnectedNetworks) {
|
if ($deleteConnectedNetworks) {
|
||||||
$service->delete_connected_networks($service->uuid);
|
$service->deleteConnectedNetworks();
|
||||||
}
|
}
|
||||||
|
|
||||||
instant_remote_process(["docker rm -f $service->uuid"], $server, throwError: false);
|
instant_remote_process(["docker rm -f $service->uuid"], $server, throwError: false);
|
||||||
@@ -56,7 +56,7 @@ class DeleteService
|
|||||||
throw new \Exception($e->getMessage());
|
throw new \Exception($e->getMessage());
|
||||||
} finally {
|
} finally {
|
||||||
if ($deleteConfigurations) {
|
if ($deleteConfigurations) {
|
||||||
$service->delete_configurations();
|
$service->deleteConfigurations();
|
||||||
}
|
}
|
||||||
foreach ($service->applications()->get() as $application) {
|
foreach ($service->applications()->get() as $application) {
|
||||||
$application->forceDelete();
|
$application->forceDelete();
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class StopService
|
|||||||
$service->stopContainers($containersToStop, $server);
|
$service->stopContainers($containersToStop, $server);
|
||||||
|
|
||||||
if ($isDeleteOperation) {
|
if ($isDeleteOperation) {
|
||||||
$service->delete_connected_networks($service->uuid);
|
$service->deleteConnectedNetworks();
|
||||||
if ($dockerCleanup) {
|
if ($dockerCleanup) {
|
||||||
CleanupDocker::dispatch($server, true);
|
CleanupDocker::dispatch($server, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,17 +13,20 @@ class CleanupRedis extends Command
|
|||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$prefix = config('database.redis.options.prefix');
|
$redis = Redis::connection('horizon');
|
||||||
|
$keys = $redis->keys('*');
|
||||||
$keys = Redis::connection()->keys('*:laravel*');
|
$prefix = config('horizon.prefix');
|
||||||
collect($keys)->each(function ($key) use ($prefix) {
|
foreach ($keys as $key) {
|
||||||
$keyWithoutPrefix = str_replace($prefix, '', $key);
|
$keyWithoutPrefix = str_replace($prefix, '', $key);
|
||||||
Redis::connection()->del($keyWithoutPrefix);
|
$type = $redis->command('type', [$keyWithoutPrefix]);
|
||||||
});
|
|
||||||
|
|
||||||
$queueOverlaps = Redis::connection()->keys('*laravel-queue-overlap*');
|
if ($type === 5) {
|
||||||
collect($queueOverlaps)->each(function ($key) {
|
$data = $redis->command('hgetall', [$keyWithoutPrefix]);
|
||||||
Redis::connection()->del($key);
|
$status = data_get($data, 'status');
|
||||||
});
|
if ($status === 'completed') {
|
||||||
|
$redis->command('del', [$keyWithoutPrefix]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ class Kernel extends ConsoleKernel
|
|||||||
}
|
}
|
||||||
|
|
||||||
// $this->scheduleInstance->job(new CleanupStaleMultiplexedConnections)->hourly();
|
// $this->scheduleInstance->job(new CleanupStaleMultiplexedConnections)->hourly();
|
||||||
|
$this->scheduleInstance->command('cleanup:redis')->hourly();
|
||||||
|
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
// Instance Jobs
|
// Instance Jobs
|
||||||
|
|||||||
@@ -12,21 +12,22 @@ class ApplicationStatusChanged implements ShouldBroadcast
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
public $teamId;
|
public ?int $teamId = null;
|
||||||
|
|
||||||
public function __construct($teamId = null)
|
public function __construct($teamId = null)
|
||||||
{
|
{
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId) && auth()->check() && auth()->user()->currentTeam()) {
|
||||||
$teamId = auth()->user()->currentTeam()->id ?? null;
|
$teamId = auth()->user()->currentTeam()->id;
|
||||||
}
|
|
||||||
if (is_null($teamId)) {
|
|
||||||
throw new \Exception('Team id is null');
|
|
||||||
}
|
}
|
||||||
$this->teamId = $teamId;
|
$this->teamId = $teamId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function broadcastOn(): array
|
public function broadcastOn(): array
|
||||||
{
|
{
|
||||||
|
if (is_null($this->teamId)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
new PrivateChannel("team.{$this->teamId}"),
|
new PrivateChannel("team.{$this->teamId}"),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -12,21 +12,22 @@ class BackupCreated implements ShouldBroadcast
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
public $teamId;
|
public ?int $teamId = null;
|
||||||
|
|
||||||
public function __construct($teamId = null)
|
public function __construct($teamId = null)
|
||||||
{
|
{
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId) && auth()->check() && auth()->user()->currentTeam()) {
|
||||||
$teamId = auth()->user()->currentTeam()->id ?? null;
|
$teamId = auth()->user()->currentTeam()->id;
|
||||||
}
|
|
||||||
if (is_null($teamId)) {
|
|
||||||
throw new \Exception('Team id is null');
|
|
||||||
}
|
}
|
||||||
$this->teamId = $teamId;
|
$this->teamId = $teamId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function broadcastOn(): array
|
public function broadcastOn(): array
|
||||||
{
|
{
|
||||||
|
if (is_null($this->teamId)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
new PrivateChannel("team.{$this->teamId}"),
|
new PrivateChannel("team.{$this->teamId}"),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -12,21 +12,22 @@ class CloudflareTunnelConfigured implements ShouldBroadcast
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
public $teamId;
|
public ?int $teamId = null;
|
||||||
|
|
||||||
public function __construct($teamId = null)
|
public function __construct($teamId = null)
|
||||||
{
|
{
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId) && auth()->check() && auth()->user()->currentTeam()) {
|
||||||
$teamId = auth()->user()->currentTeam()->id ?? null;
|
$teamId = auth()->user()->currentTeam()->id;
|
||||||
}
|
|
||||||
if (is_null($teamId)) {
|
|
||||||
throw new \Exception('Team id is null');
|
|
||||||
}
|
}
|
||||||
$this->teamId = $teamId;
|
$this->teamId = $teamId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function broadcastOn(): array
|
public function broadcastOn(): array
|
||||||
{
|
{
|
||||||
|
if (is_null($this->teamId)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
new PrivateChannel("team.{$this->teamId}"),
|
new PrivateChannel("team.{$this->teamId}"),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -7,27 +7,27 @@ use Illuminate\Broadcasting\PrivateChannel;
|
|||||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
use Illuminate\Foundation\Events\Dispatchable;
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Facades\Auth;
|
|
||||||
|
|
||||||
class DatabaseProxyStopped implements ShouldBroadcast
|
class DatabaseProxyStopped implements ShouldBroadcast
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
public $teamId;
|
public ?int $teamId = null;
|
||||||
|
|
||||||
public function __construct($teamId = null)
|
public function __construct($teamId = null)
|
||||||
{
|
{
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId) && auth()->check() && auth()->user()->currentTeam()) {
|
||||||
$teamId = Auth::user()?->currentTeam()?->id ?? null;
|
$teamId = auth()->user()->currentTeam()->id;
|
||||||
}
|
|
||||||
if (is_null($teamId)) {
|
|
||||||
throw new \Exception('Team id is null');
|
|
||||||
}
|
}
|
||||||
$this->teamId = $teamId;
|
$this->teamId = $teamId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function broadcastOn(): array
|
public function broadcastOn(): array
|
||||||
{
|
{
|
||||||
|
if (is_null($this->teamId)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
new PrivateChannel("team.{$this->teamId}"),
|
new PrivateChannel("team.{$this->teamId}"),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -13,28 +13,24 @@ class DatabaseStatusChanged implements ShouldBroadcast
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
public $userId = null;
|
public int|string|null $userId = null;
|
||||||
|
|
||||||
public function __construct($userId = null)
|
public function __construct($userId = null)
|
||||||
{
|
{
|
||||||
if (is_null($userId)) {
|
if (is_null($userId)) {
|
||||||
$userId = Auth::id() ?? null;
|
$userId = Auth::id() ?? null;
|
||||||
}
|
}
|
||||||
if (is_null($userId)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->userId = $userId;
|
$this->userId = $userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function broadcastOn(): ?array
|
public function broadcastOn(): ?array
|
||||||
{
|
{
|
||||||
if (! is_null($this->userId)) {
|
if (is_null($this->userId)) {
|
||||||
return [
|
return [];
|
||||||
new PrivateChannel("user.{$this->userId}"),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return [
|
||||||
|
new PrivateChannel("user.{$this->userId}"),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,18 +12,22 @@ class FileStorageChanged implements ShouldBroadcast
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
public $teamId;
|
public ?int $teamId = null;
|
||||||
|
|
||||||
public function __construct($teamId = null)
|
public function __construct($teamId = null)
|
||||||
{
|
{
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId) && auth()->check() && auth()->user()->currentTeam()) {
|
||||||
throw new \Exception('Team id is null');
|
$teamId = auth()->user()->currentTeam()->id;
|
||||||
}
|
}
|
||||||
$this->teamId = $teamId;
|
$this->teamId = $teamId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function broadcastOn(): array
|
public function broadcastOn(): array
|
||||||
{
|
{
|
||||||
|
if (is_null($this->teamId)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
new PrivateChannel("team.{$this->teamId}"),
|
new PrivateChannel("team.{$this->teamId}"),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -12,21 +12,22 @@ class ProxyStatusChanged implements ShouldBroadcast
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
public $teamId;
|
public ?int $teamId = null;
|
||||||
|
|
||||||
public function __construct($teamId = null)
|
public function __construct($teamId = null)
|
||||||
{
|
{
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId) && auth()->check() && auth()->user()->currentTeam()) {
|
||||||
$teamId = auth()->user()->currentTeam()->id ?? null;
|
$teamId = auth()->user()->currentTeam()->id;
|
||||||
}
|
|
||||||
if (is_null($teamId)) {
|
|
||||||
throw new \Exception('Team id is null');
|
|
||||||
}
|
}
|
||||||
$this->teamId = $teamId;
|
$this->teamId = $teamId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function broadcastOn(): array
|
public function broadcastOn(): array
|
||||||
{
|
{
|
||||||
|
if (is_null($this->teamId)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
new PrivateChannel("team.{$this->teamId}"),
|
new PrivateChannel("team.{$this->teamId}"),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -12,21 +12,22 @@ class ScheduledTaskDone implements ShouldBroadcast
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
public $teamId;
|
public ?int $teamId = null;
|
||||||
|
|
||||||
public function __construct($teamId = null)
|
public function __construct($teamId = null)
|
||||||
{
|
{
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId) && auth()->check() && auth()->user()->currentTeam()) {
|
||||||
$teamId = auth()->user()->currentTeam()->id ?? null;
|
$teamId = auth()->user()->currentTeam()->id;
|
||||||
}
|
|
||||||
if (is_null($teamId)) {
|
|
||||||
throw new \Exception('Team id is null');
|
|
||||||
}
|
}
|
||||||
$this->teamId = $teamId;
|
$this->teamId = $teamId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function broadcastOn(): array
|
public function broadcastOn(): array
|
||||||
{
|
{
|
||||||
|
if (is_null($this->teamId)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
new PrivateChannel("team.{$this->teamId}"),
|
new PrivateChannel("team.{$this->teamId}"),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -13,27 +13,24 @@ class ServiceStatusChanged implements ShouldBroadcast
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
public ?string $userId = null;
|
public int|string|null $userId = null;
|
||||||
|
|
||||||
public function __construct($userId = null)
|
public function __construct($userId = null)
|
||||||
{
|
{
|
||||||
if (is_null($userId)) {
|
if (is_null($userId)) {
|
||||||
$userId = Auth::id() ?? null;
|
$userId = Auth::id() ?? null;
|
||||||
}
|
}
|
||||||
if (is_null($userId)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$this->userId = $userId;
|
$this->userId = $userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function broadcastOn(): ?array
|
public function broadcastOn(): ?array
|
||||||
{
|
{
|
||||||
if (! is_null($this->userId)) {
|
if (is_null($this->userId)) {
|
||||||
return [
|
return [];
|
||||||
new PrivateChannel("user.{$this->userId}"),
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return [
|
||||||
|
new PrivateChannel("user.{$this->userId}"),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,15 +12,21 @@ class TestEvent implements ShouldBroadcast
|
|||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
public $teamId;
|
public ?int $teamId = null;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->teamId = auth()->user()->currentTeam()->id;
|
if (auth()->check() && auth()->user()->currentTeam()) {
|
||||||
|
$this->teamId = auth()->user()->currentTeam()->id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function broadcastOn(): array
|
public function broadcastOn(): array
|
||||||
{
|
{
|
||||||
|
if (is_null($this->teamId)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
new PrivateChannel("team.{$this->teamId}"),
|
new PrivateChannel("team.{$this->teamId}"),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ use Illuminate\Foundation\Bus\Dispatchable;
|
|||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Process;
|
|
||||||
use Illuminate\Support\Sleep;
|
use Illuminate\Support\Sleep;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
@@ -1377,7 +1376,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
private function check_git_if_build_needed()
|
private function check_git_if_build_needed()
|
||||||
{
|
{
|
||||||
if ($this->source->getMorphClass() === \App\Models\GithubApp::class && $this->source->is_public === false) {
|
if (is_object($this->source) && $this->source->getMorphClass() === \App\Models\GithubApp::class && $this->source->is_public === false) {
|
||||||
$repository = githubApi($this->source, "repos/{$this->customRepository}");
|
$repository = githubApi($this->source, "repos/{$this->customRepository}");
|
||||||
$data = data_get($repository, 'data');
|
$data = data_get($repository, 'data');
|
||||||
if (isset($data->id)) {
|
if (isset($data->id)) {
|
||||||
@@ -2246,43 +2245,16 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
$this->application_deployment_queue->addLogEntry('Building docker image completed.');
|
$this->application_deployment_queue->addLogEntry('Building docker image completed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function graceful_shutdown_container(string $containerName, int $timeout = 300)
|
private function graceful_shutdown_container(string $containerName, int $timeout = 30)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$process = Process::timeout($timeout)->start("docker stop --time=$timeout $containerName");
|
$this->execute_remote_command(
|
||||||
|
["docker stop --time=$timeout $containerName", 'hidden' => true, 'ignore_errors' => true],
|
||||||
$startTime = time();
|
["docker rm -f $containerName", 'hidden' => true, 'ignore_errors' => true]
|
||||||
while ($process->running()) {
|
);
|
||||||
if (time() - $startTime >= $timeout) {
|
} catch (Exception $error) {
|
||||||
$this->execute_remote_command(
|
|
||||||
["docker kill $containerName", 'hidden' => true, 'ignore_errors' => true]
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
usleep(100000);
|
|
||||||
}
|
|
||||||
|
|
||||||
$isRunning = $this->execute_remote_command(
|
|
||||||
["docker inspect -f '{{.State.Running}}' $containerName", 'hidden' => true, 'ignore_errors' => true]
|
|
||||||
) === 'true';
|
|
||||||
|
|
||||||
if ($isRunning) {
|
|
||||||
$this->execute_remote_command(
|
|
||||||
["docker kill $containerName", 'hidden' => true, 'ignore_errors' => true]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (\Exception $error) {
|
|
||||||
$this->application_deployment_queue->addLogEntry("Error stopping container $containerName: ".$error->getMessage(), 'stderr');
|
$this->application_deployment_queue->addLogEntry("Error stopping container $containerName: ".$error->getMessage(), 'stderr');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->remove_container($containerName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function remove_container(string $containerName)
|
|
||||||
{
|
|
||||||
$this->execute_remote_command(
|
|
||||||
["docker rm -f $containerName", 'hidden' => true, 'ignore_errors' => true]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function stop_running_container(bool $force = false)
|
private function stop_running_container(bool $force = false)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class CleanupInstanceStuffsJob implements ShouldBeEncrypted, ShouldBeUnique, Sho
|
|||||||
|
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
return [(new WithoutOverlapping('cleanup-instance-stuffs'))->expireAfter(60)];
|
return [(new WithoutOverlapping('cleanup-instance-stuffs'))->dontRelease()];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
|
|||||||
@@ -390,7 +390,7 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
$commands[] = 'mkdir -p '.$this->backup_dir;
|
$commands[] = 'mkdir -p '.$this->backup_dir;
|
||||||
$backupCommand = 'docker exec';
|
$backupCommand = 'docker exec';
|
||||||
if ($this->postgres_password) {
|
if ($this->postgres_password) {
|
||||||
$backupCommand .= " -e PGPASSWORD=$this->postgres_password";
|
$backupCommand .= " -e PGPASSWORD=\"{$this->postgres_password}\"";
|
||||||
}
|
}
|
||||||
if ($this->backup->dump_all) {
|
if ($this->backup->dump_all) {
|
||||||
$backupCommand .= " $this->container_name pg_dumpall --username {$this->database->postgres_user} | gzip > $this->backup_location";
|
$backupCommand .= " $this->container_name pg_dumpall --username {$this->database->postgres_user} | gzip > $this->backup_location";
|
||||||
|
|||||||
@@ -42,10 +42,8 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$persistentStorages = collect();
|
|
||||||
switch ($this->resource->type()) {
|
switch ($this->resource->type()) {
|
||||||
case 'application':
|
case 'application':
|
||||||
$persistentStorages = $this->resource?->persistentStorages()?->get();
|
|
||||||
StopApplication::run($this->resource, previewDeployments: true);
|
StopApplication::run($this->resource, previewDeployments: true);
|
||||||
break;
|
break;
|
||||||
case 'standalone-postgresql':
|
case 'standalone-postgresql':
|
||||||
@@ -56,53 +54,52 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
case 'standalone-keydb':
|
case 'standalone-keydb':
|
||||||
case 'standalone-dragonfly':
|
case 'standalone-dragonfly':
|
||||||
case 'standalone-clickhouse':
|
case 'standalone-clickhouse':
|
||||||
$persistentStorages = $this->resource?->persistentStorages()?->get();
|
|
||||||
StopDatabase::run($this->resource, true);
|
StopDatabase::run($this->resource, true);
|
||||||
break;
|
break;
|
||||||
case 'service':
|
case 'service':
|
||||||
StopService::run($this->resource, true);
|
StopService::run($this->resource, true);
|
||||||
DeleteService::run($this->resource, $this->deleteConfigurations, $this->deleteVolumes, $this->dockerCleanup, $this->deleteConnectedNetworks);
|
DeleteService::run($this->resource, $this->deleteConfigurations, $this->deleteVolumes, $this->dockerCleanup, $this->deleteConnectedNetworks);
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->deleteVolumes && $this->resource->type() !== 'service') {
|
return;
|
||||||
$this->resource->delete_volumes($persistentStorages);
|
|
||||||
$this->resource->persistentStorages()->delete();
|
|
||||||
}
|
}
|
||||||
$isDatabase = $this->resource instanceof StandalonePostgresql
|
|
||||||
|| $this->resource instanceof StandaloneRedis
|
|
||||||
|| $this->resource instanceof StandaloneMongodb
|
|
||||||
|| $this->resource instanceof StandaloneMysql
|
|
||||||
|| $this->resource instanceof StandaloneMariadb
|
|
||||||
|| $this->resource instanceof StandaloneKeydb
|
|
||||||
|| $this->resource instanceof StandaloneDragonfly
|
|
||||||
|| $this->resource instanceof StandaloneClickhouse;
|
|
||||||
|
|
||||||
if ($this->deleteConfigurations) {
|
if ($this->deleteConfigurations) {
|
||||||
$this->resource->delete_configurations(); // rename to FileStorages
|
$this->resource->deleteConfigurations();
|
||||||
$this->resource->fileStorages()->delete();
|
|
||||||
}
|
}
|
||||||
|
if ($this->deleteVolumes) {
|
||||||
|
$this->resource->deleteVolumes();
|
||||||
|
$this->resource->persistentStorages()->delete();
|
||||||
|
}
|
||||||
|
$this->resource->fileStorages()->delete();
|
||||||
|
|
||||||
|
$isDatabase = $this->resource instanceof StandalonePostgresql
|
||||||
|
|| $this->resource instanceof StandaloneRedis
|
||||||
|
|| $this->resource instanceof StandaloneMongodb
|
||||||
|
|| $this->resource instanceof StandaloneMysql
|
||||||
|
|| $this->resource instanceof StandaloneMariadb
|
||||||
|
|| $this->resource instanceof StandaloneKeydb
|
||||||
|
|| $this->resource instanceof StandaloneDragonfly
|
||||||
|
|| $this->resource instanceof StandaloneClickhouse;
|
||||||
|
|
||||||
if ($isDatabase) {
|
if ($isDatabase) {
|
||||||
$this->resource->sslCertificates()->delete();
|
$this->resource->sslCertificates()->delete();
|
||||||
$this->resource->scheduledBackups()->delete();
|
$this->resource->scheduledBackups()->delete();
|
||||||
$this->resource->environment_variables()->delete();
|
|
||||||
$this->resource->tags()->detach();
|
$this->resource->tags()->detach();
|
||||||
}
|
}
|
||||||
|
$this->resource->environment_variables()->delete();
|
||||||
|
|
||||||
$server = data_get($this->resource, 'server') ?? data_get($this->resource, 'destination.server');
|
if ($this->deleteConnectedNetworks && $this->resource->type() === 'application') {
|
||||||
if (($this->dockerCleanup || $isDatabase) && $server) {
|
$this->resource->deleteConnectedNetworks();
|
||||||
CleanupDocker::dispatch($server, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->deleteConnectedNetworks && ! $isDatabase) {
|
|
||||||
$this->resource?->delete_connected_networks($this->resource->uuid);
|
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
throw $e;
|
throw $e;
|
||||||
} finally {
|
} finally {
|
||||||
$this->resource->forceDelete();
|
$this->resource->forceDelete();
|
||||||
if ($this->dockerCleanup) {
|
if ($this->dockerCleanup) {
|
||||||
CleanupDocker::dispatch($server, true);
|
$server = data_get($this->resource, 'server') ?? data_get($this->resource, 'destination.server');
|
||||||
|
if ($server) {
|
||||||
|
CleanupDocker::dispatch($server, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Artisan::queue('cleanup:stucked-resources');
|
Artisan::queue('cleanup:stucked-resources');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
return [(new WithoutOverlapping($this->server->uuid))->expireAfter(600)];
|
return [(new WithoutOverlapping($this->server->uuid))->dontRelease()];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __construct(public Server $server, public bool $manualCleanup = false) {}
|
public function __construct(public Server $server, public bool $manualCleanup = false) {}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ class PushServerUpdateJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
return [(new WithoutOverlapping($this->server->uuid))->expireAfter(30)];
|
return [(new WithoutOverlapping($this->server->uuid))->dontRelease()];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function backoff(): int
|
public function backoff(): int
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class RestartProxyJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
return [(new WithoutOverlapping($this->server->uuid))->expireAfter(60)];
|
return [(new WithoutOverlapping($this->server->uuid))->dontRelease()];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __construct(public Server $server) {}
|
public function __construct(public Server $server) {}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
public function middleware(): array
|
public function middleware(): array
|
||||||
{
|
{
|
||||||
return [(new WithoutOverlapping($this->server->uuid))->expireAfter(60)];
|
return [(new WithoutOverlapping($this->server->uuid))->dontRelease()];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __construct(public Server $server) {}
|
public function __construct(public Server $server) {}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class Dashboard extends Component
|
|||||||
|
|
||||||
public function navigateToProject($projectUuid)
|
public function navigateToProject($projectUuid)
|
||||||
{
|
{
|
||||||
return $this->redirect(collect($this->projects)->firstWhere('uuid', $projectUuid)->navigateTo(), true);
|
return $this->redirect(collect($this->projects)->firstWhere('uuid', $projectUuid)->navigateTo(), navigate: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ class Heading extends Component
|
|||||||
'application_uuid' => $this->parameters['application_uuid'],
|
'application_uuid' => $this->parameters['application_uuid'],
|
||||||
'deployment_uuid' => $this->deploymentUuid,
|
'deployment_uuid' => $this->deploymentUuid,
|
||||||
'environment_uuid' => $this->parameters['environment_uuid'],
|
'environment_uuid' => $this->parameters['environment_uuid'],
|
||||||
], navigate: true);
|
], navigate: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function setDeploymentUuid()
|
protected function setDeploymentUuid()
|
||||||
@@ -147,7 +147,7 @@ class Heading extends Component
|
|||||||
'application_uuid' => $this->parameters['application_uuid'],
|
'application_uuid' => $this->parameters['application_uuid'],
|
||||||
'deployment_uuid' => $this->deploymentUuid,
|
'deployment_uuid' => $this->deploymentUuid,
|
||||||
'environment_uuid' => $this->parameters['environment_uuid'],
|
'environment_uuid' => $this->parameters['environment_uuid'],
|
||||||
], navigate: true);
|
], navigate: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
|
|||||||
@@ -5,10 +5,7 @@ namespace App\Livewire\Project\Application;
|
|||||||
use App\Actions\Docker\GetContainersStatus;
|
use App\Actions\Docker\GetContainersStatus;
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
use App\Models\ApplicationPreview;
|
use App\Models\ApplicationPreview;
|
||||||
use Carbon\Carbon;
|
|
||||||
use Illuminate\Process\InvokedProcess;
|
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Process;
|
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Spatie\Url\Url;
|
use Spatie\Url\Url;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
@@ -193,13 +190,12 @@ class Previews extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$server = $this->application->destination->server;
|
$server = $this->application->destination->server;
|
||||||
$timeout = 300;
|
|
||||||
|
|
||||||
if ($this->application->destination->server->isSwarm()) {
|
if ($this->application->destination->server->isSwarm()) {
|
||||||
instant_remote_process(["docker stack rm {$this->application->uuid}-{$pull_request_id}"], $server);
|
instant_remote_process(["docker stack rm {$this->application->uuid}-{$pull_request_id}"], $server);
|
||||||
} else {
|
} else {
|
||||||
$containers = getCurrentApplicationContainerStatus($server, $this->application->id, $pull_request_id)->toArray();
|
$containers = getCurrentApplicationContainerStatus($server, $this->application->id, $pull_request_id)->toArray();
|
||||||
$this->stopContainers($containers, $server, $timeout);
|
$this->stopContainers($containers, $server);
|
||||||
}
|
}
|
||||||
|
|
||||||
GetContainersStatus::run($server);
|
GetContainersStatus::run($server);
|
||||||
@@ -215,13 +211,12 @@ class Previews extends Component
|
|||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$server = $this->application->destination->server;
|
$server = $this->application->destination->server;
|
||||||
$timeout = 300;
|
|
||||||
|
|
||||||
if ($this->application->destination->server->isSwarm()) {
|
if ($this->application->destination->server->isSwarm()) {
|
||||||
instant_remote_process(["docker stack rm {$this->application->uuid}-{$pull_request_id}"], $server);
|
instant_remote_process(["docker stack rm {$this->application->uuid}-{$pull_request_id}"], $server);
|
||||||
} else {
|
} else {
|
||||||
$containers = getCurrentApplicationContainerStatus($server, $this->application->id, $pull_request_id)->toArray();
|
$containers = getCurrentApplicationContainerStatus($server, $this->application->id, $pull_request_id)->toArray();
|
||||||
$this->stopContainers($containers, $server, $timeout);
|
$this->stopContainers($containers, $server);
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplicationPreview::where('application_id', $this->application->id)
|
ApplicationPreview::where('application_id', $this->application->id)
|
||||||
@@ -237,48 +232,14 @@ class Previews extends Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function stopContainers(array $containers, $server, int $timeout)
|
private function stopContainers(array $containers, $server, int $timeout = 30)
|
||||||
{
|
{
|
||||||
$processes = [];
|
|
||||||
foreach ($containers as $container) {
|
foreach ($containers as $container) {
|
||||||
$containerName = str_replace('/', '', $container['Names']);
|
$containerName = str_replace('/', '', $container['Names']);
|
||||||
$processes[$containerName] = $this->stopContainer($containerName, $timeout);
|
instant_remote_process(command: [
|
||||||
}
|
"docker stop --time=$timeout $containerName",
|
||||||
|
"docker rm -f $containerName",
|
||||||
$startTime = Carbon::now()->getTimestamp();
|
], server: $server, throwError: false);
|
||||||
while (count($processes) > 0) {
|
|
||||||
$finishedProcesses = array_filter($processes, function ($process) {
|
|
||||||
return ! $process->running();
|
|
||||||
});
|
|
||||||
foreach (array_keys($finishedProcesses) as $containerName) {
|
|
||||||
unset($processes[$containerName]);
|
|
||||||
$this->removeContainer($containerName, $server);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Carbon::now()->getTimestamp() - $startTime >= $timeout) {
|
|
||||||
$this->forceStopRemainingContainers(array_keys($processes), $server);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
usleep(100000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function stopContainer(string $containerName, int $timeout): InvokedProcess
|
|
||||||
{
|
|
||||||
return Process::timeout($timeout)->start("docker stop --time=$timeout $containerName");
|
|
||||||
}
|
|
||||||
|
|
||||||
private function removeContainer(string $containerName, $server)
|
|
||||||
{
|
|
||||||
instant_remote_process(["docker rm -f $containerName"], $server, throwError: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function forceStopRemainingContainers(array $containerNames, $server)
|
|
||||||
{
|
|
||||||
foreach ($containerNames as $containerName) {
|
|
||||||
instant_remote_process(["docker kill $containerName"], $server, throwError: false);
|
|
||||||
$this->removeContainer($containerName, $server);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,6 +120,8 @@ class General extends Component
|
|||||||
try {
|
try {
|
||||||
$this->database->save();
|
$this->database->save();
|
||||||
$this->dispatch('success', 'SSL configuration updated.');
|
$this->dispatch('success', 'SSL configuration updated.');
|
||||||
|
$this->db_url = $this->database->internal_db_url;
|
||||||
|
$this->db_url_public = $this->database->external_db_url;
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ class ScheduledBackups extends Component
|
|||||||
|
|
||||||
public $s3s;
|
public $s3s;
|
||||||
|
|
||||||
|
public string $custom_type = 'mysql';
|
||||||
|
|
||||||
protected $listeners = ['refreshScheduledBackups'];
|
protected $listeners = ['refreshScheduledBackups'];
|
||||||
|
|
||||||
protected $queryString = ['selectedBackupId'];
|
protected $queryString = ['selectedBackupId'];
|
||||||
@@ -49,6 +51,14 @@ class ScheduledBackups extends Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setCustomType()
|
||||||
|
{
|
||||||
|
$this->database->custom_type = $this->custom_type;
|
||||||
|
$this->database->save();
|
||||||
|
$this->dispatch('success', 'Database type set.');
|
||||||
|
$this->refreshScheduledBackups();
|
||||||
|
}
|
||||||
|
|
||||||
public function delete($scheduled_backup_id): void
|
public function delete($scheduled_backup_id): void
|
||||||
{
|
{
|
||||||
$this->database->scheduledBackups->find($scheduled_backup_id)->delete();
|
$this->database->scheduledBackups->find($scheduled_backup_id)->delete();
|
||||||
@@ -62,5 +72,6 @@ class ScheduledBackups extends Component
|
|||||||
if ($id) {
|
if ($id) {
|
||||||
$this->setSelectedBackup($id);
|
$this->setSelectedBackup($id);
|
||||||
}
|
}
|
||||||
|
$this->dispatch('refreshScheduledBackups');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,6 @@ class Index extends Component
|
|||||||
{
|
{
|
||||||
$project = collect($this->projects)->firstWhere('uuid', $projectUuid);
|
$project = collect($this->projects)->firstWhere('uuid', $projectUuid);
|
||||||
|
|
||||||
return $this->redirect($project->navigateTo(), true);
|
return $this->redirect($project->navigateTo(), navigate: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,8 +31,9 @@ class Configuration extends Component
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
"echo-private:user.{$userId},ServiceStatusChanged" => 'check_status',
|
"echo-private:user.{$userId},ServiceStatusChanged" => 'check_status',
|
||||||
'check_status',
|
|
||||||
'refreshStatus' => '$refresh',
|
'refreshStatus' => '$refresh',
|
||||||
|
'check_status',
|
||||||
|
'refreshServices',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,6 +64,13 @@ class Configuration extends Component
|
|||||||
$this->databases = $this->service->databases->sort();
|
$this->databases = $this->service->databases->sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function refreshServices()
|
||||||
|
{
|
||||||
|
$this->service->refresh();
|
||||||
|
$this->applications = $this->service->applications->sort();
|
||||||
|
$this->databases = $this->service->databases->sort();
|
||||||
|
}
|
||||||
|
|
||||||
public function restartApplication($id)
|
public function restartApplication($id)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use App\Actions\Database\StopDatabaseProxy;
|
|||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\ServiceDatabase;
|
use App\Models\ServiceDatabase;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
@@ -83,6 +84,42 @@ class Database extends Component
|
|||||||
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function convertToApplication()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$service = $this->database->service;
|
||||||
|
$serviceDatabase = $this->database;
|
||||||
|
|
||||||
|
// Check if application with same name already exists
|
||||||
|
if ($service->applications()->where('name', $serviceDatabase->name)->exists()) {
|
||||||
|
throw new \Exception('An application with this name already exists.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new parameters removing database_uuid
|
||||||
|
$redirectParams = collect($this->parameters)
|
||||||
|
->except('database_uuid')
|
||||||
|
->all();
|
||||||
|
|
||||||
|
DB::transaction(function () use ($service, $serviceDatabase) {
|
||||||
|
$service->applications()->create([
|
||||||
|
'name' => $serviceDatabase->name,
|
||||||
|
'human_name' => $serviceDatabase->human_name,
|
||||||
|
'description' => $serviceDatabase->description,
|
||||||
|
'exclude_from_status' => $serviceDatabase->exclude_from_status,
|
||||||
|
'is_log_drain_enabled' => $serviceDatabase->is_log_drain_enabled,
|
||||||
|
'image' => $serviceDatabase->image,
|
||||||
|
'service_id' => $service->id,
|
||||||
|
'is_migrated' => true,
|
||||||
|
]);
|
||||||
|
$serviceDatabase->delete();
|
||||||
|
});
|
||||||
|
|
||||||
|
return redirect()->route('project.service.configuration', $redirectParams);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
if ($this->database->is_public && ! $this->database->public_port) {
|
if ($this->database->is_public && ! $this->database->public_port) {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class Index extends Component
|
|||||||
|
|
||||||
public $s3s;
|
public $s3s;
|
||||||
|
|
||||||
protected $listeners = ['generateDockerCompose'];
|
protected $listeners = ['generateDockerCompose', 'refreshScheduledBackups' => '$refresh'];
|
||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace App\Livewire\Project\Service;
|
|||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\ServiceApplication;
|
use App\Models\ServiceApplication;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Spatie\Url\Url;
|
use Spatie\Url\Url;
|
||||||
@@ -23,7 +24,7 @@ class ServiceApplicationView extends Component
|
|||||||
'application.human_name' => 'nullable',
|
'application.human_name' => 'nullable',
|
||||||
'application.description' => 'nullable',
|
'application.description' => 'nullable',
|
||||||
'application.fqdn' => 'nullable',
|
'application.fqdn' => 'nullable',
|
||||||
'application.image' => 'required',
|
'application.image' => 'string|nullable',
|
||||||
'application.exclude_from_status' => 'required|boolean',
|
'application.exclude_from_status' => 'required|boolean',
|
||||||
'application.required_fqdn' => 'required|boolean',
|
'application.required_fqdn' => 'required|boolean',
|
||||||
'application.is_log_drain_enabled' => 'nullable|boolean',
|
'application.is_log_drain_enabled' => 'nullable|boolean',
|
||||||
@@ -73,6 +74,40 @@ class ServiceApplicationView extends Component
|
|||||||
$this->parameters = get_route_parameters();
|
$this->parameters = get_route_parameters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function convertToDatabase()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$service = $this->application->service;
|
||||||
|
$serviceApplication = $this->application;
|
||||||
|
|
||||||
|
// Check if database with same name already exists
|
||||||
|
if ($service->databases()->where('name', $serviceApplication->name)->exists()) {
|
||||||
|
throw new \Exception('A database with this name already exists.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$redirectParams = collect($this->parameters)
|
||||||
|
->except('database_uuid')
|
||||||
|
->all();
|
||||||
|
DB::transaction(function () use ($service, $serviceApplication) {
|
||||||
|
$service->databases()->create([
|
||||||
|
'name' => $serviceApplication->name,
|
||||||
|
'human_name' => $serviceApplication->human_name,
|
||||||
|
'description' => $serviceApplication->description,
|
||||||
|
'exclude_from_status' => $serviceApplication->exclude_from_status,
|
||||||
|
'is_log_drain_enabled' => $serviceApplication->is_log_drain_enabled,
|
||||||
|
'image' => $serviceApplication->image,
|
||||||
|
'service_id' => $service->id,
|
||||||
|
'is_migrated' => true,
|
||||||
|
]);
|
||||||
|
$serviceApplication->delete();
|
||||||
|
});
|
||||||
|
|
||||||
|
return redirect()->route('project.service.configuration', $redirectParams);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function submit()
|
public function submit()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ class StackForm extends Component
|
|||||||
$this->service->refresh();
|
$this->service->refresh();
|
||||||
$this->service->saveComposeConfigs();
|
$this->service->saveComposeConfigs();
|
||||||
$this->dispatch('refreshEnvs');
|
$this->dispatch('refreshEnvs');
|
||||||
|
$this->dispatch('refreshServices');
|
||||||
$notify && $this->dispatch('success', 'Service saved.');
|
$notify && $this->dispatch('success', 'Service saved.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ use Illuminate\Database\Eloquent\Casts\Attribute;
|
|||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Illuminate\Process\InvokedProcess;
|
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Process;
|
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
@@ -270,51 +268,17 @@ class Application extends BaseModel
|
|||||||
return $containers->pluck('Names')->toArray();
|
return $containers->pluck('Names')->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function stopContainers(array $containerNames, $server, int $timeout = 600)
|
public function stopContainers(array $containerNames, $server, int $timeout = 30)
|
||||||
{
|
|
||||||
$processes = [];
|
|
||||||
foreach ($containerNames as $containerName) {
|
|
||||||
$processes[$containerName] = $this->stopContainer($containerName, $server, $timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
$startTime = time();
|
|
||||||
while (count($processes) > 0) {
|
|
||||||
$finishedProcesses = array_filter($processes, function ($process) {
|
|
||||||
return ! $process->running();
|
|
||||||
});
|
|
||||||
foreach ($finishedProcesses as $containerName => $process) {
|
|
||||||
unset($processes[$containerName]);
|
|
||||||
$this->removeContainer($containerName, $server);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (time() - $startTime >= $timeout) {
|
|
||||||
$this->forceStopRemainingContainers(array_keys($processes), $server);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
usleep(100000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function stopContainer(string $containerName, $server, int $timeout): InvokedProcess
|
|
||||||
{
|
|
||||||
return Process::timeout($timeout)->start("docker stop --time=$timeout $containerName");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function removeContainer(string $containerName, $server)
|
|
||||||
{
|
|
||||||
instant_remote_process(command: ["docker rm -f $containerName"], server: $server, throwError: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function forceStopRemainingContainers(array $containerNames, $server)
|
|
||||||
{
|
{
|
||||||
foreach ($containerNames as $containerName) {
|
foreach ($containerNames as $containerName) {
|
||||||
instant_remote_process(command: ["docker kill $containerName"], server: $server, throwError: false);
|
instant_remote_process(command: [
|
||||||
$this->removeContainer($containerName, $server);
|
"docker stop --time=$timeout $containerName",
|
||||||
|
"docker rm -f $containerName",
|
||||||
|
], server: $server, throwError: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_configurations()
|
public function deleteConfigurations()
|
||||||
{
|
{
|
||||||
$server = data_get($this, 'destination.server');
|
$server = data_get($this, 'destination.server');
|
||||||
$workdir = $this->workdir();
|
$workdir = $this->workdir();
|
||||||
@@ -323,8 +287,9 @@ class Application extends BaseModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_volumes(?Collection $persistentStorages)
|
public function deleteVolumes()
|
||||||
{
|
{
|
||||||
|
$persistentStorages = $this->persistentStorages()->get() ?? collect();
|
||||||
if ($this->build_pack === 'dockercompose') {
|
if ($this->build_pack === 'dockercompose') {
|
||||||
$server = data_get($this, 'destination.server');
|
$server = data_get($this, 'destination.server');
|
||||||
instant_remote_process(["cd {$this->dirOnServer()} && docker compose down -v"], $server, false);
|
instant_remote_process(["cd {$this->dirOnServer()} && docker compose down -v"], $server, false);
|
||||||
@@ -339,8 +304,9 @@ class Application extends BaseModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_connected_networks($uuid)
|
public function deleteConnectedNetworks()
|
||||||
{
|
{
|
||||||
|
$uuid = $this->uuid;
|
||||||
$server = data_get($this, 'destination.server');
|
$server = data_get($this, 'destination.server');
|
||||||
instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false);
|
instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false);
|
||||||
instant_remote_process(["docker network rm {$uuid}"], $server, false);
|
instant_remote_process(["docker network rm {$uuid}"], $server, false);
|
||||||
|
|||||||
@@ -488,7 +488,7 @@ $schema://$host {
|
|||||||
$proxy_path = "$base_path/proxy";
|
$proxy_path = "$base_path/proxy";
|
||||||
|
|
||||||
if ($proxyType === ProxyTypes::TRAEFIK->value) {
|
if ($proxyType === ProxyTypes::TRAEFIK->value) {
|
||||||
$proxy_path = '/';
|
$proxy_path = $proxy_path.'/';
|
||||||
} elseif ($proxyType === ProxyTypes::CADDY->value) {
|
} elseif ($proxyType === ProxyTypes::CADDY->value) {
|
||||||
$proxy_path = $proxy_path.'/caddy';
|
$proxy_path = $proxy_path.'/caddy';
|
||||||
} elseif ($proxyType === ProxyTypes::NGINX->value) {
|
} elseif ($proxyType === ProxyTypes::NGINX->value) {
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ use Illuminate\Database\Eloquent\Casts\Attribute;
|
|||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Illuminate\Process\InvokedProcess;
|
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Process;
|
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use OpenApi\Attributes as OA;
|
use OpenApi\Attributes as OA;
|
||||||
use Spatie\Url\Url;
|
use Spatie\Url\Url;
|
||||||
@@ -158,51 +156,17 @@ class Service extends BaseModel
|
|||||||
return $containersToStop;
|
return $containersToStop;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function stopContainers(array $containerNames, $server, int $timeout = 300)
|
public function stopContainers(array $containerNames, $server, int $timeout = 30)
|
||||||
{
|
|
||||||
$processes = [];
|
|
||||||
foreach ($containerNames as $containerName) {
|
|
||||||
$processes[$containerName] = $this->stopContainer($containerName, $timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
$startTime = time();
|
|
||||||
while (count($processes) > 0) {
|
|
||||||
$finishedProcesses = array_filter($processes, function ($process) {
|
|
||||||
return ! $process->running();
|
|
||||||
});
|
|
||||||
foreach (array_keys($finishedProcesses) as $containerName) {
|
|
||||||
unset($processes[$containerName]);
|
|
||||||
$this->removeContainer($containerName, $server);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (time() - $startTime >= $timeout) {
|
|
||||||
$this->forceStopRemainingContainers(array_keys($processes), $server);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
usleep(100000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function stopContainer(string $containerName, int $timeout): InvokedProcess
|
|
||||||
{
|
|
||||||
return Process::timeout($timeout)->start("docker stop --time=$timeout $containerName");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function removeContainer(string $containerName, $server)
|
|
||||||
{
|
|
||||||
instant_remote_process(command: ["docker rm -f $containerName"], server: $server, throwError: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function forceStopRemainingContainers(array $containerNames, $server)
|
|
||||||
{
|
{
|
||||||
foreach ($containerNames as $containerName) {
|
foreach ($containerNames as $containerName) {
|
||||||
instant_remote_process(command: ["docker kill $containerName"], server: $server, throwError: false);
|
instant_remote_process(command: [
|
||||||
$this->removeContainer($containerName, $server);
|
"docker stop --time=$timeout $containerName",
|
||||||
|
"docker rm -f $containerName",
|
||||||
|
], server: $server, throwError: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_configurations()
|
public function deleteConfigurations()
|
||||||
{
|
{
|
||||||
$server = data_get($this, 'destination.server');
|
$server = data_get($this, 'destination.server');
|
||||||
$workdir = $this->workdir();
|
$workdir = $this->workdir();
|
||||||
@@ -211,11 +175,11 @@ class Service extends BaseModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_connected_networks($uuid)
|
public function deleteConnectedNetworks()
|
||||||
{
|
{
|
||||||
$server = data_get($this, 'destination.server');
|
$server = data_get($this, 'destination.server');
|
||||||
instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false);
|
instant_remote_process(["docker network disconnect {$this->uuid} coolify-proxy"], $server, false);
|
||||||
instant_remote_process(["docker network rm {$uuid}"], $server, false);
|
instant_remote_process(["docker network rm {$this->uuid}"], $server, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getStatusAttribute()
|
public function getStatusAttribute()
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class ServiceDatabase extends BaseModel
|
|||||||
static::deleting(function ($service) {
|
static::deleting(function ($service) {
|
||||||
$service->persistentStorages()->delete();
|
$service->persistentStorages()->delete();
|
||||||
$service->fileStorages()->delete();
|
$service->fileStorages()->delete();
|
||||||
|
$service->scheduledBackups()->delete();
|
||||||
});
|
});
|
||||||
static::saving(function ($service) {
|
static::saving(function ($service) {
|
||||||
if ($service->isDirty('status')) {
|
if ($service->isDirty('status')) {
|
||||||
@@ -77,6 +78,9 @@ class ServiceDatabase extends BaseModel
|
|||||||
|
|
||||||
public function databaseType()
|
public function databaseType()
|
||||||
{
|
{
|
||||||
|
if (filled($this->custom_type)) {
|
||||||
|
return 'standalone-'.$this->custom_type;
|
||||||
|
}
|
||||||
$image = str($this->image)->before(':');
|
$image = str($this->image)->before(':');
|
||||||
if ($image->contains('supabase/postgres')) {
|
if ($image->contains('supabase/postgres')) {
|
||||||
$finalImage = 'supabase/postgres';
|
$finalImage = 'supabase/postgres';
|
||||||
@@ -141,6 +145,7 @@ class ServiceDatabase extends BaseModel
|
|||||||
str($this->databaseType())->contains('postgres') ||
|
str($this->databaseType())->contains('postgres') ||
|
||||||
str($this->databaseType())->contains('postgis') ||
|
str($this->databaseType())->contains('postgis') ||
|
||||||
str($this->databaseType())->contains('mariadb') ||
|
str($this->databaseType())->contains('mariadb') ||
|
||||||
str($this->databaseType())->contains('mongo');
|
str($this->databaseType())->contains('mongo') ||
|
||||||
|
filled($this->custom_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
@@ -94,7 +93,7 @@ class StandaloneClickhouse extends BaseModel
|
|||||||
return database_configuration_dir()."/{$this->uuid}";
|
return database_configuration_dir()."/{$this->uuid}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_configurations()
|
public function deleteConfigurations()
|
||||||
{
|
{
|
||||||
$server = data_get($this, 'destination.server');
|
$server = data_get($this, 'destination.server');
|
||||||
$workdir = $this->workdir();
|
$workdir = $this->workdir();
|
||||||
@@ -103,8 +102,9 @@ class StandaloneClickhouse extends BaseModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_volumes(Collection $persistentStorages)
|
public function deleteVolumes()
|
||||||
{
|
{
|
||||||
|
$persistentStorages = $this->persistentStorages()->get() ?? collect();
|
||||||
if ($persistentStorages->count() === 0) {
|
if ($persistentStorages->count() === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
@@ -94,7 +93,7 @@ class StandaloneDragonfly extends BaseModel
|
|||||||
return database_configuration_dir()."/{$this->uuid}";
|
return database_configuration_dir()."/{$this->uuid}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_configurations()
|
public function deleteConfigurations()
|
||||||
{
|
{
|
||||||
$server = data_get($this, 'destination.server');
|
$server = data_get($this, 'destination.server');
|
||||||
$workdir = $this->workdir();
|
$workdir = $this->workdir();
|
||||||
@@ -103,8 +102,9 @@ class StandaloneDragonfly extends BaseModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_volumes(Collection $persistentStorages)
|
public function deleteVolumes()
|
||||||
{
|
{
|
||||||
|
$persistentStorages = $this->persistentStorages()->get() ?? collect();
|
||||||
if ($persistentStorages->count() === 0) {
|
if ($persistentStorages->count() === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
@@ -94,7 +93,7 @@ class StandaloneKeydb extends BaseModel
|
|||||||
return database_configuration_dir()."/{$this->uuid}";
|
return database_configuration_dir()."/{$this->uuid}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_configurations()
|
public function deleteConfigurations()
|
||||||
{
|
{
|
||||||
$server = data_get($this, 'destination.server');
|
$server = data_get($this, 'destination.server');
|
||||||
$workdir = $this->workdir();
|
$workdir = $this->workdir();
|
||||||
@@ -103,8 +102,9 @@ class StandaloneKeydb extends BaseModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_volumes(Collection $persistentStorages)
|
public function deleteVolumes()
|
||||||
{
|
{
|
||||||
|
$persistentStorages = $this->persistentStorages()->get() ?? collect();
|
||||||
if ($persistentStorages->count() === 0) {
|
if ($persistentStorages->count() === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
class StandaloneMariadb extends BaseModel
|
class StandaloneMariadb extends BaseModel
|
||||||
@@ -94,7 +94,7 @@ class StandaloneMariadb extends BaseModel
|
|||||||
return database_configuration_dir()."/{$this->uuid}";
|
return database_configuration_dir()."/{$this->uuid}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_configurations()
|
public function deleteConfigurations()
|
||||||
{
|
{
|
||||||
$server = data_get($this, 'destination.server');
|
$server = data_get($this, 'destination.server');
|
||||||
$workdir = $this->workdir();
|
$workdir = $this->workdir();
|
||||||
@@ -103,8 +103,9 @@ class StandaloneMariadb extends BaseModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_volumes(Collection $persistentStorages)
|
public function deleteVolumes()
|
||||||
{
|
{
|
||||||
|
$persistentStorages = $this->persistentStorages()->get() ?? collect();
|
||||||
if ($persistentStorages->count() === 0) {
|
if ($persistentStorages->count() === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -253,7 +254,7 @@ class StandaloneMariadb extends BaseModel
|
|||||||
return $this->morphMany(LocalFileVolume::class, 'resource');
|
return $this->morphMany(LocalFileVolume::class, 'resource');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destination()
|
public function destination(): MorphTo
|
||||||
{
|
{
|
||||||
return $this->morphTo();
|
return $this->morphTo();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
@@ -98,7 +97,7 @@ class StandaloneMongodb extends BaseModel
|
|||||||
return database_configuration_dir()."/{$this->uuid}";
|
return database_configuration_dir()."/{$this->uuid}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_configurations()
|
public function deleteConfigurations()
|
||||||
{
|
{
|
||||||
$server = data_get($this, 'destination.server');
|
$server = data_get($this, 'destination.server');
|
||||||
$workdir = $this->workdir();
|
$workdir = $this->workdir();
|
||||||
@@ -107,8 +106,9 @@ class StandaloneMongodb extends BaseModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_volumes(Collection $persistentStorages)
|
public function deleteVolumes()
|
||||||
{
|
{
|
||||||
|
$persistentStorages = $this->persistentStorages()->get() ?? collect();
|
||||||
if ($persistentStorages->count() === 0) {
|
if ($persistentStorages->count() === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
@@ -95,7 +94,7 @@ class StandaloneMysql extends BaseModel
|
|||||||
return database_configuration_dir()."/{$this->uuid}";
|
return database_configuration_dir()."/{$this->uuid}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_configurations()
|
public function deleteConfigurations()
|
||||||
{
|
{
|
||||||
$server = data_get($this, 'destination.server');
|
$server = data_get($this, 'destination.server');
|
||||||
$workdir = $this->workdir();
|
$workdir = $this->workdir();
|
||||||
@@ -104,8 +103,9 @@ class StandaloneMysql extends BaseModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_volumes(Collection $persistentStorages)
|
public function deleteVolumes()
|
||||||
{
|
{
|
||||||
|
$persistentStorages = $this->persistentStorages()->get() ?? collect();
|
||||||
if ($persistentStorages->count() === 0) {
|
if ($persistentStorages->count() === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
@@ -59,7 +58,7 @@ class StandalonePostgresql extends BaseModel
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_configurations()
|
public function deleteConfigurations()
|
||||||
{
|
{
|
||||||
$server = data_get($this, 'destination.server');
|
$server = data_get($this, 'destination.server');
|
||||||
$workdir = $this->workdir();
|
$workdir = $this->workdir();
|
||||||
@@ -68,8 +67,9 @@ class StandalonePostgresql extends BaseModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_volumes(Collection $persistentStorages)
|
public function deleteVolumes()
|
||||||
{
|
{
|
||||||
|
$persistentStorages = $this->persistentStorages()->get() ?? collect();
|
||||||
if ($persistentStorages->count() === 0) {
|
if ($persistentStorages->count() === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||||
use Illuminate\Database\Eloquent\Collection;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
@@ -96,7 +95,7 @@ class StandaloneRedis extends BaseModel
|
|||||||
return database_configuration_dir()."/{$this->uuid}";
|
return database_configuration_dir()."/{$this->uuid}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_configurations()
|
public function deleteConfigurations()
|
||||||
{
|
{
|
||||||
$server = data_get($this, 'destination.server');
|
$server = data_get($this, 'destination.server');
|
||||||
$workdir = $this->workdir();
|
$workdir = $this->workdir();
|
||||||
@@ -105,8 +104,9 @@ class StandaloneRedis extends BaseModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete_volumes(Collection $persistentStorages)
|
public function deleteVolumes()
|
||||||
{
|
{
|
||||||
|
$persistentStorages = $this->persistentStorages()->get() ?? collect();
|
||||||
if ($persistentStorages->count() === 0) {
|
if ($persistentStorages->count() === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Traits;
|
namespace App\Traits;
|
||||||
|
|
||||||
use DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Session;
|
use Illuminate\Support\Facades\Session;
|
||||||
|
|
||||||
trait DeletesUserSessions
|
trait DeletesUserSessions
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ function queue_application_deployment(Application $application, string $deployme
|
|||||||
// Check if there's already a deployment in progress or queued for this application and commit
|
// Check if there's already a deployment in progress or queued for this application and commit
|
||||||
$existing_deployment = ApplicationDeploymentQueue::where('application_id', $application_id)
|
$existing_deployment = ApplicationDeploymentQueue::where('application_id', $application_id)
|
||||||
->where('commit', $commit)
|
->where('commit', $commit)
|
||||||
|
->where('pull_request_id', $pull_request_id)
|
||||||
->whereIn('status', [ApplicationDeploymentStatus::IN_PROGRESS->value, ApplicationDeploymentStatus::QUEUED->value])
|
->whereIn('status', [ApplicationDeploymentStatus::IN_PROGRESS->value, ApplicationDeploymentStatus::QUEUED->value])
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
|
|||||||
@@ -2990,12 +2990,12 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
$applicationFound = ServiceApplication::where('name', $serviceName)->where('service_id', $resource->id)->first();
|
$applicationFound = ServiceApplication::where('name', $serviceName)->where('service_id', $resource->id)->first();
|
||||||
if ($applicationFound) {
|
if ($applicationFound) {
|
||||||
$savedService = $applicationFound;
|
$savedService = $applicationFound;
|
||||||
$savedService = ServiceDatabase::firstOrCreate([
|
// $savedService = ServiceDatabase::firstOrCreate([
|
||||||
'name' => $applicationFound->name,
|
// 'name' => $applicationFound->name,
|
||||||
'image' => $applicationFound->image,
|
// 'image' => $applicationFound->image,
|
||||||
'service_id' => $applicationFound->service_id,
|
// 'service_id' => $applicationFound->service_id,
|
||||||
]);
|
// ]);
|
||||||
$applicationFound->delete();
|
// $applicationFound->delete();
|
||||||
} else {
|
} else {
|
||||||
$savedService = ServiceDatabase::firstOrCreate([
|
$savedService = ServiceDatabase::firstOrCreate([
|
||||||
'name' => $serviceName,
|
'name' => $serviceName,
|
||||||
@@ -3248,12 +3248,12 @@ function newParser(Application|Service $resource, int $pull_request_id = 0, ?int
|
|||||||
$applicationFound = ServiceApplication::where('name', $serviceName)->where('image', $image)->where('service_id', $resource->id)->first();
|
$applicationFound = ServiceApplication::where('name', $serviceName)->where('image', $image)->where('service_id', $resource->id)->first();
|
||||||
if ($applicationFound) {
|
if ($applicationFound) {
|
||||||
$savedService = $applicationFound;
|
$savedService = $applicationFound;
|
||||||
$savedService = ServiceDatabase::firstOrCreate([
|
// $savedService = ServiceDatabase::firstOrCreate([
|
||||||
'name' => $applicationFound->name,
|
// 'name' => $applicationFound->name,
|
||||||
'image' => $applicationFound->image,
|
// 'image' => $applicationFound->image,
|
||||||
'service_id' => $applicationFound->service_id,
|
// 'service_id' => $applicationFound->service_id,
|
||||||
]);
|
// ]);
|
||||||
$applicationFound->delete();
|
// $applicationFound->delete();
|
||||||
} else {
|
} else {
|
||||||
$savedService = ServiceDatabase::firstOrCreate([
|
$savedService = ServiceDatabase::firstOrCreate([
|
||||||
'name' => $serviceName,
|
'name' => $serviceName,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
'coolify' => [
|
'coolify' => [
|
||||||
'version' => '4.0.0-beta.413',
|
'version' => '4.0.0-beta.418',
|
||||||
'helper_version' => '1.0.8',
|
'helper_version' => '1.0.8',
|
||||||
'realtime_version' => '1.0.8',
|
'realtime_version' => '1.0.8',
|
||||||
'self_hosted' => env('SELF_HOSTED', true),
|
'self_hosted' => env('SELF_HOSTED', true),
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('service_applications', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_migrated')->default(false);
|
||||||
|
});
|
||||||
|
Schema::table('service_databases', function (Blueprint $table) {
|
||||||
|
$table->boolean('is_migrated')->default(false);
|
||||||
|
$table->string('custom_type')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('service_applications', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_migrated');
|
||||||
|
});
|
||||||
|
Schema::table('service_databases', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('is_migrated');
|
||||||
|
$table->dropColumn('custom_type');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -19,7 +19,7 @@ class ApplicationSeeder extends Seeder
|
|||||||
'fqdn' => 'http://nodejs.127.0.0.1.sslip.io',
|
'fqdn' => 'http://nodejs.127.0.0.1.sslip.io',
|
||||||
'repository_project_id' => 603035348,
|
'repository_project_id' => 603035348,
|
||||||
'git_repository' => 'coollabsio/coolify-examples',
|
'git_repository' => 'coollabsio/coolify-examples',
|
||||||
'git_branch' => 'main',
|
'git_branch' => 'v4.x',
|
||||||
'base_directory' => '/nodejs',
|
'base_directory' => '/nodejs',
|
||||||
'build_pack' => 'nixpacks',
|
'build_pack' => 'nixpacks',
|
||||||
'ports_exposes' => '3000',
|
'ports_exposes' => '3000',
|
||||||
@@ -34,7 +34,7 @@ class ApplicationSeeder extends Seeder
|
|||||||
'fqdn' => 'http://dockerfile.127.0.0.1.sslip.io',
|
'fqdn' => 'http://dockerfile.127.0.0.1.sslip.io',
|
||||||
'repository_project_id' => 603035348,
|
'repository_project_id' => 603035348,
|
||||||
'git_repository' => 'coollabsio/coolify-examples',
|
'git_repository' => 'coollabsio/coolify-examples',
|
||||||
'git_branch' => 'main',
|
'git_branch' => 'v4.x',
|
||||||
'base_directory' => '/dockerfile',
|
'base_directory' => '/dockerfile',
|
||||||
'build_pack' => 'dockerfile',
|
'build_pack' => 'dockerfile',
|
||||||
'ports_exposes' => '80',
|
'ports_exposes' => '80',
|
||||||
@@ -48,7 +48,7 @@ class ApplicationSeeder extends Seeder
|
|||||||
'name' => 'Pure Dockerfile Example',
|
'name' => 'Pure Dockerfile Example',
|
||||||
'fqdn' => 'http://pure-dockerfile.127.0.0.1.sslip.io',
|
'fqdn' => 'http://pure-dockerfile.127.0.0.1.sslip.io',
|
||||||
'git_repository' => 'coollabsio/coolify',
|
'git_repository' => 'coollabsio/coolify',
|
||||||
'git_branch' => 'main',
|
'git_branch' => 'v4.x',
|
||||||
'git_commit_sha' => 'HEAD',
|
'git_commit_sha' => 'HEAD',
|
||||||
'build_pack' => 'dockerfile',
|
'build_pack' => 'dockerfile',
|
||||||
'ports_exposes' => '80',
|
'ports_exposes' => '80',
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ services:
|
|||||||
retries: 10
|
retries: 10
|
||||||
timeout: 2s
|
timeout: 2s
|
||||||
soketi:
|
soketi:
|
||||||
image: '${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.6'
|
image: '${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.8'
|
||||||
ports:
|
ports:
|
||||||
- "${SOKETI_PORT:-6001}:6001"
|
- "${SOKETI_PORT:-6001}:6001"
|
||||||
- "6002:6002"
|
- "6002:6002"
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ services:
|
|||||||
retries: 10
|
retries: 10
|
||||||
timeout: 2s
|
timeout: 2s
|
||||||
soketi:
|
soketi:
|
||||||
image: '${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.6'
|
image: '${REGISTRY_URL:-ghcr.io}/coollabsio/coolify-realtime:1.0.8'
|
||||||
ports:
|
ports:
|
||||||
- "${SOKETI_PORT:-6001}:6001"
|
- "${SOKETI_PORT:-6001}:6001"
|
||||||
- "6002:6002"
|
- "6002:6002"
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"coolify": {
|
"coolify": {
|
||||||
"v4": {
|
"v4": {
|
||||||
"version": "4.0.0-beta.413"
|
"version": "4.0.0-beta.418"
|
||||||
},
|
},
|
||||||
"nightly": {
|
"nightly": {
|
||||||
"version": "4.0.0-beta.414"
|
"version": "4.0.0-beta.419"
|
||||||
},
|
},
|
||||||
"helper": {
|
"helper": {
|
||||||
"version": "1.0.8"
|
"version": "1.0.8"
|
||||||
|
|||||||
86
package-lock.json
generated
86
package-lock.json
generated
@@ -22,7 +22,7 @@
|
|||||||
"pusher-js": "8.4.0",
|
"pusher-js": "8.4.0",
|
||||||
"tailwind-scrollbar": "^3.1.0",
|
"tailwind-scrollbar": "^3.1.0",
|
||||||
"tailwindcss": "3.4.17",
|
"tailwindcss": "3.4.17",
|
||||||
"vite": "^6.2.6",
|
"vite": "^6.3.4",
|
||||||
"vue": "3.5.13"
|
"vue": "3.5.13"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2931,6 +2931,51 @@
|
|||||||
"node": ">=0.8"
|
"node": ">=0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tinyglobby": {
|
||||||
|
"version": "0.2.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
|
||||||
|
"integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"fdir": "^6.4.4",
|
||||||
|
"picomatch": "^4.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tinyglobby/node_modules/fdir": {
|
||||||
|
"version": "6.4.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
|
||||||
|
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"picomatch": "^3 || ^4"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"picomatch": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/tinyglobby/node_modules/picomatch": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/to-regex-range": {
|
"node_modules/to-regex-range": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
@@ -2994,15 +3039,18 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "6.2.6",
|
"version": "6.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.4.tgz",
|
||||||
"integrity": "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==",
|
"integrity": "sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"esbuild": "^0.25.0",
|
"esbuild": "^0.25.0",
|
||||||
|
"fdir": "^6.4.4",
|
||||||
|
"picomatch": "^4.0.2",
|
||||||
"postcss": "^8.5.3",
|
"postcss": "^8.5.3",
|
||||||
"rollup": "^4.30.1"
|
"rollup": "^4.34.9",
|
||||||
|
"tinyglobby": "^0.2.13"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"vite": "bin/vite.js"
|
"vite": "bin/vite.js"
|
||||||
@@ -3076,6 +3124,34 @@
|
|||||||
"picomatch": "^2.3.1"
|
"picomatch": "^2.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vite/node_modules/fdir": {
|
||||||
|
"version": "6.4.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
|
||||||
|
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"picomatch": "^3 || ^4"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"picomatch": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite/node_modules/picomatch": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vue": {
|
"node_modules/vue": {
|
||||||
"version": "3.5.13",
|
"version": "3.5.13",
|
||||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz",
|
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz",
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
"pusher-js": "8.4.0",
|
"pusher-js": "8.4.0",
|
||||||
"tailwind-scrollbar": "^3.1.0",
|
"tailwind-scrollbar": "^3.1.0",
|
||||||
"tailwindcss": "3.4.17",
|
"tailwindcss": "3.4.17",
|
||||||
"vite": "^6.2.6",
|
"vite": "^6.3.4",
|
||||||
"vue": "3.5.13"
|
"vue": "3.5.13"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -123,7 +123,7 @@
|
|||||||
<ul role="list" class="flex flex-col h-full space-y-1.5">
|
<ul role="list" class="flex flex-col h-full space-y-1.5">
|
||||||
@if (isSubscribed() || !isCloud())
|
@if (isSubscribed() || !isCloud())
|
||||||
<li>
|
<li>
|
||||||
<a title="Dashboard" href="/" wire:navigate
|
<a title="Dashboard" href="/"
|
||||||
class="{{ request()->is('/') ? 'menu-item-active menu-item' : 'menu-item' }}">
|
class="{{ request()->is('/') ? 'menu-item-active menu-item' : 'menu-item' }}">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" fill="none" viewBox="0 0 24 24"
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" fill="none" viewBox="0 0 24 24"
|
||||||
stroke="currentColor">
|
stroke="currentColor">
|
||||||
@@ -134,7 +134,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a title="Projects" wire:navigate
|
<a title="Projects"
|
||||||
class="{{ request()->is('project/*') || request()->is('projects') ? 'menu-item menu-item-active' : 'menu-item' }}"
|
class="{{ request()->is('project/*') || request()->is('projects') ? 'menu-item menu-item-active' : 'menu-item' }}"
|
||||||
href="/projects">
|
href="/projects">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24"
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24"
|
||||||
@@ -149,7 +149,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a title="Servers" wire:navigate
|
<a title="Servers"
|
||||||
class="{{ request()->is('server/*') || request()->is('servers') ? 'menu-item menu-item-active' : 'menu-item' }}"
|
class="{{ request()->is('server/*') || request()->is('servers') ? 'menu-item menu-item-active' : 'menu-item' }}"
|
||||||
href="/servers">
|
href="/servers">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24"
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24"
|
||||||
@@ -168,7 +168,7 @@
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<a title="Sources" wire:navigate
|
<a title="Sources"
|
||||||
class="{{ request()->is('source*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
class="{{ request()->is('source*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
||||||
href="{{ route('source.all') }}">
|
href="{{ route('source.all') }}">
|
||||||
<svg class="icon" viewBox="0 0 15 15" xmlns="http://www.w3.org/2000/svg">
|
<svg class="icon" viewBox="0 0 15 15" xmlns="http://www.w3.org/2000/svg">
|
||||||
@@ -179,7 +179,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a title="Destinations" wire:navigate
|
<a title="Destinations"
|
||||||
class="{{ request()->is('destination*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
class="{{ request()->is('destination*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
||||||
href="{{ route('destination.index') }}">
|
href="{{ route('destination.index') }}">
|
||||||
|
|
||||||
@@ -192,7 +192,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a title="S3 Storages" wire:navigate
|
<a title="S3 Storages"
|
||||||
class="{{ request()->is('storages*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
class="{{ request()->is('storages*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
||||||
href="{{ route('storage.index') }}">
|
href="{{ route('storage.index') }}">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24">
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24">
|
||||||
@@ -207,7 +207,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a title="Shared variables" wire:navigate
|
<a title="Shared variables"
|
||||||
class="{{ request()->is('shared-variables*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
class="{{ request()->is('shared-variables*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
||||||
href="{{ route('shared-variables.index') }}">
|
href="{{ route('shared-variables.index') }}">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24">
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24">
|
||||||
@@ -222,7 +222,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a title="Notifications" wire:navigate
|
<a title="Notifications"
|
||||||
class="{{ request()->is('notifications*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
class="{{ request()->is('notifications*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
||||||
href="{{ route('notifications.email') }}">
|
href="{{ route('notifications.email') }}">
|
||||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
@@ -234,7 +234,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a title="Keys & Tokens" wire:navigate
|
<a title="Keys & Tokens"
|
||||||
class="{{ request()->is('security*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
class="{{ request()->is('security*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
||||||
href="{{ route('security.private-key.index') }}">
|
href="{{ route('security.private-key.index') }}">
|
||||||
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
@@ -246,7 +246,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a title="Tags" wire:navigate
|
<a title="Tags"
|
||||||
class="{{ request()->is('tags*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
class="{{ request()->is('tags*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
||||||
href="{{ route('tags.show') }}">
|
href="{{ route('tags.show') }}">
|
||||||
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
@@ -275,7 +275,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a title="Profile" wire:navigate
|
<a title="Profile"
|
||||||
class="{{ request()->is('profile*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
class="{{ request()->is('profile*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
||||||
href="{{ route('profile') }}">
|
href="{{ route('profile') }}">
|
||||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
||||||
@@ -290,7 +290,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a title="Teams" wire:navigate
|
<a title="Teams"
|
||||||
class="{{ request()->is('team*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
class="{{ request()->is('team*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
||||||
href="{{ route('team.index') }}">
|
href="{{ route('team.index') }}">
|
||||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
||||||
@@ -309,7 +309,7 @@
|
|||||||
</li>
|
</li>
|
||||||
@if (isCloud())
|
@if (isCloud())
|
||||||
<li>
|
<li>
|
||||||
<a title="Subscription" wire:navigate
|
<a title="Subscription"
|
||||||
class="{{ request()->is('subscription*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
class="{{ request()->is('subscription*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
||||||
href="{{ route('subscription.show') }}">
|
href="{{ route('subscription.show') }}">
|
||||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
@@ -324,7 +324,7 @@
|
|||||||
@if (isInstanceAdmin())
|
@if (isInstanceAdmin())
|
||||||
<li>
|
<li>
|
||||||
|
|
||||||
<a title="Settings" wire:navigate
|
<a title="Settings"
|
||||||
class="{{ request()->is('settings*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
class="{{ request()->is('settings*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
||||||
href="/settings">
|
href="/settings">
|
||||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
||||||
@@ -343,7 +343,7 @@
|
|||||||
@if (isCloud() || isDev())
|
@if (isCloud() || isDev())
|
||||||
@if (isInstanceAdmin() || session('impersonating'))
|
@if (isInstanceAdmin() || session('impersonating'))
|
||||||
<li>
|
<li>
|
||||||
<a wire:navigate title="Admin" class="menu-item" href="/admin">
|
<a title="Admin" class="menu-item" href="/admin">
|
||||||
<svg class="text-pink-600 icon" viewBox="0 0 256 256"
|
<svg class="text-pink-600 icon" viewBox="0 0 256 256"
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill="currentColor"
|
<path fill="currentColor"
|
||||||
@@ -363,7 +363,7 @@
|
|||||||
@endpersist
|
@endpersist
|
||||||
@endif
|
@endif
|
||||||
<li>
|
<li>
|
||||||
<a title="Onboarding" wire:navigate
|
<a title="Onboarding"
|
||||||
class="{{ request()->is('onboarding*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
class="{{ request()->is('onboarding*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
||||||
href="{{ route('onboarding') }}">
|
href="{{ route('onboarding') }}">
|
||||||
<svg class="icon" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
|
<svg class="icon" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
|||||||
@@ -4,27 +4,22 @@
|
|||||||
<div class="navbar-main">
|
<div class="navbar-main">
|
||||||
<nav class="flex items-center gap-6 min-h-10">
|
<nav class="flex items-center gap-6 min-h-10">
|
||||||
<a class="{{ request()->routeIs('notifications.email') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('notifications.email') ? 'dark:text-white' : '' }}"
|
||||||
wire:navigate
|
|
||||||
href="{{ route('notifications.email') }}">
|
href="{{ route('notifications.email') }}">
|
||||||
<button>Email</button>
|
<button>Email</button>
|
||||||
</a>
|
</a>
|
||||||
<a class="{{ request()->routeIs('notifications.discord') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('notifications.discord') ? 'dark:text-white' : '' }}"
|
||||||
wire:navigate
|
|
||||||
href="{{ route('notifications.discord') }}">
|
href="{{ route('notifications.discord') }}">
|
||||||
<button>Discord</button>
|
<button>Discord</button>
|
||||||
</a>
|
</a>
|
||||||
<a class="{{ request()->routeIs('notifications.telegram') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('notifications.telegram') ? 'dark:text-white' : '' }}"
|
||||||
wire:navigate
|
|
||||||
href="{{ route('notifications.telegram') }}">
|
href="{{ route('notifications.telegram') }}">
|
||||||
<button>Telegram</button>
|
<button>Telegram</button>
|
||||||
</a>
|
</a>
|
||||||
<a class="{{ request()->routeIs('notifications.slack') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('notifications.slack') ? 'dark:text-white' : '' }}"
|
||||||
wire:navigate
|
|
||||||
href="{{ route('notifications.slack') }}">
|
href="{{ route('notifications.slack') }}">
|
||||||
<button>Slack</button>
|
<button>Slack</button>
|
||||||
</a>
|
</a>
|
||||||
<a class="{{ request()->routeIs('notifications.pushover') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('notifications.pushover') ? 'dark:text-white' : '' }}"
|
||||||
wire:navigate
|
|
||||||
href="{{ route('notifications.pushover') }}">
|
href="{{ route('notifications.pushover') }}">
|
||||||
<button>Pushover</button>
|
<button>Pushover</button>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
<li class="inline-flex items-center">
|
<li class="inline-flex items-center">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<a class="text-xs truncate lg:text-sm"
|
<a class="text-xs truncate lg:text-sm"
|
||||||
wire:navigate
|
|
||||||
href="{{ route('project.show', ['project_uuid' => data_get($resource, 'environment.project.uuid')]) }}">
|
href="{{ route('project.show', ['project_uuid' => data_get($resource, 'environment.project.uuid')]) }}">
|
||||||
{{ data_get($resource, 'environment.project.name', 'Undefined Name') }}</a>
|
{{ data_get($resource, 'environment.project.name', 'Undefined Name') }}</a>
|
||||||
<svg aria-hidden="true" class="w-4 h-4 mx-1 font-bold dark:text-warning" fill="currentColor"
|
<svg aria-hidden="true" class="w-4 h-4 mx-1 font-bold dark:text-warning" fill="currentColor"
|
||||||
@@ -22,7 +21,6 @@
|
|||||||
<li>
|
<li>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<a class="text-xs truncate lg:text-sm"
|
<a class="text-xs truncate lg:text-sm"
|
||||||
wire:navigate
|
|
||||||
href="{{ route('project.resource.index', [
|
href="{{ route('project.resource.index', [
|
||||||
'environment_uuid' => data_get($resource, 'environment.uuid'),
|
'environment_uuid' => data_get($resource, 'environment.uuid'),
|
||||||
'project_uuid' => data_get($resource, 'environment.project.uuid'),
|
'project_uuid' => data_get($resource, 'environment.project.uuid'),
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
<div class="subtitle">Security related settings.</div>
|
<div class="subtitle">Security related settings.</div>
|
||||||
<div class="navbar-main">
|
<div class="navbar-main">
|
||||||
<nav class="flex items-center gap-6 scrollbar min-h-10">
|
<nav class="flex items-center gap-6 scrollbar min-h-10">
|
||||||
<a wire:navigate href="{{ route('security.private-key.index') }}">
|
<a href="{{ route('security.private-key.index') }}">
|
||||||
<button>Private Keys</button>
|
<button>Private Keys</button>
|
||||||
</a>
|
</a>
|
||||||
<a wire:navigate href="{{ route('security.api-tokens') }}">
|
<a href="{{ route('security.api-tokens') }}">
|
||||||
<button>API tokens</button>
|
<button>API tokens</button>
|
||||||
</a>
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
<div class="subtitle">{{ data_get($server, 'name') }}</div>
|
<div class="subtitle">{{ data_get($server, 'name') }}</div>
|
||||||
<div class="navbar-main">
|
<div class="navbar-main">
|
||||||
<nav class="flex items-center gap-6 overflow-x-scroll sm:overflow-x-hidden scrollbar min-h-10 whitespace-nowrap">
|
<nav class="flex items-center gap-6 overflow-x-scroll sm:overflow-x-hidden scrollbar min-h-10 whitespace-nowrap">
|
||||||
<a wire:navigate class="{{ request()->routeIs('server.show') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('server.show') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('server.show', [
|
href="{{ route('server.show', [
|
||||||
'server_uuid' => data_get($server, 'uuid'),
|
'server_uuid' => data_get($server, 'uuid'),
|
||||||
]) }}">
|
]) }}">
|
||||||
@@ -26,14 +26,14 @@
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
@if (!$server->isSwarmWorker() && !$server->settings->is_build_server)
|
@if (!$server->isSwarmWorker() && !$server->settings->is_build_server)
|
||||||
<a wire:navigate class="{{ request()->routeIs('server.proxy') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('server.proxy') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('server.proxy', [
|
href="{{ route('server.proxy', [
|
||||||
'server_uuid' => data_get($server, 'uuid'),
|
'server_uuid' => data_get($server, 'uuid'),
|
||||||
]) }}">
|
]) }}">
|
||||||
<button>Proxy</button>
|
<button>Proxy</button>
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
<a wire:navigate class="{{ request()->routeIs('server.resources') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('server.resources') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('server.resources', [
|
href="{{ route('server.resources', [
|
||||||
'server_uuid' => data_get($server, 'uuid'),
|
'server_uuid' => data_get($server, 'uuid'),
|
||||||
]) }}">
|
]) }}">
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
@if ($server->proxySet())
|
@if ($server->proxySet())
|
||||||
<div class="flex flex-col items-start gap-2 min-w-fit">
|
<div class="flex flex-col items-start gap-2 min-w-fit">
|
||||||
<a wire:navigate class="{{ request()->routeIs('server.proxy') ? 'menu-item menu-item-active' : 'menu-item' }}"
|
<a class="{{ request()->routeIs('server.proxy') ? 'menu-item menu-item-active' : 'menu-item' }}"
|
||||||
href="{{ route('server.proxy', $parameters) }}">
|
href="{{ route('server.proxy', $parameters) }}">
|
||||||
<button>Configuration</button>
|
<button>Configuration</button>
|
||||||
</a>
|
</a>
|
||||||
<a wire:navigate class="{{ request()->routeIs('server.proxy.dynamic-confs') ? 'menu-item menu-item-active' : 'menu-item' }}"
|
<a class="{{ request()->routeIs('server.proxy.dynamic-confs') ? 'menu-item menu-item-active' : 'menu-item' }}"
|
||||||
href="{{ route('server.proxy.dynamic-confs', $parameters) }}">
|
href="{{ route('server.proxy.dynamic-confs', $parameters) }}">
|
||||||
<button>Dynamic Configurations</button>
|
<button>Dynamic Configurations</button>
|
||||||
</a>
|
</a>
|
||||||
<a wire:navigate class="{{ request()->routeIs('server.proxy.logs') ? 'menu-item menu-item-active' : 'menu-item' }}"
|
<a class="{{ request()->routeIs('server.proxy.logs') ? 'menu-item menu-item-active' : 'menu-item' }}"
|
||||||
href="{{ route('server.proxy.logs', $parameters) }}">
|
href="{{ route('server.proxy.logs', $parameters) }}">
|
||||||
<button>Logs</button>
|
<button>Logs</button>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -1,34 +1,34 @@
|
|||||||
<div class="flex flex-col items-start gap-2 min-w-fit">
|
<div class="flex flex-col items-start gap-2 min-w-fit">
|
||||||
<a wire:navigate class="menu-item {{ $activeMenu === 'general' ? 'menu-item-active' : '' }}"
|
<a class="menu-item {{ $activeMenu === 'general' ? 'menu-item-active' : '' }}"
|
||||||
href="{{ route('server.show', ['server_uuid' => $server->uuid]) }}">General</a>
|
href="{{ route('server.show', ['server_uuid' => $server->uuid]) }}">General</a>
|
||||||
@if ($server->isFunctional())
|
@if ($server->isFunctional())
|
||||||
<a wire:navigate class="menu-item {{ $activeMenu === 'advanced' ? 'menu-item-active' : '' }}"
|
<a class="menu-item {{ $activeMenu === 'advanced' ? 'menu-item-active' : '' }}"
|
||||||
href="{{ route('server.advanced', ['server_uuid' => $server->uuid]) }}">Advanced
|
href="{{ route('server.advanced', ['server_uuid' => $server->uuid]) }}">Advanced
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
<a wire:navigate class="menu-item {{ $activeMenu === 'private-key' ? 'menu-item-active' : '' }}"
|
<a class="menu-item {{ $activeMenu === 'private-key' ? 'menu-item-active' : '' }}"
|
||||||
href="{{ route('server.private-key', ['server_uuid' => $server->uuid]) }}">Private Key
|
href="{{ route('server.private-key', ['server_uuid' => $server->uuid]) }}">Private Key
|
||||||
</a>
|
</a>
|
||||||
@if (!$server->isLocalhost())
|
@if (!$server->isLocalhost())
|
||||||
<a wire:navigate class="menu-item {{ $activeMenu === 'cloudflare-tunnels' ? 'menu-item-active' : '' }}"
|
<a class="menu-item {{ $activeMenu === 'cloudflare-tunnels' ? 'menu-item-active' : '' }}"
|
||||||
href="{{ route('server.cloudflare-tunnels', ['server_uuid' => $server->uuid]) }}">Cloudflare
|
href="{{ route('server.cloudflare-tunnels', ['server_uuid' => $server->uuid]) }}">Cloudflare
|
||||||
Tunnels</a>
|
Tunnels</a>
|
||||||
@endif
|
@endif
|
||||||
@if ($server->isFunctional())
|
@if ($server->isFunctional())
|
||||||
<a wire:navigate class="menu-item {{ $activeMenu === 'docker-cleanup' ? 'menu-item-active' : '' }}"
|
<a class="menu-item {{ $activeMenu === 'docker-cleanup' ? 'menu-item-active' : '' }}"
|
||||||
href="{{ route('server.docker-cleanup', ['server_uuid' => $server->uuid]) }}">Docker Cleanup
|
href="{{ route('server.docker-cleanup', ['server_uuid' => $server->uuid]) }}">Docker Cleanup
|
||||||
</a>
|
</a>
|
||||||
<a wire:navigate class="menu-item {{ $activeMenu === 'destinations' ? 'menu-item-active' : '' }}"
|
<a class="menu-item {{ $activeMenu === 'destinations' ? 'menu-item-active' : '' }}"
|
||||||
href="{{ route('server.destinations', ['server_uuid' => $server->uuid]) }}">Destinations
|
href="{{ route('server.destinations', ['server_uuid' => $server->uuid]) }}">Destinations
|
||||||
</a>
|
</a>
|
||||||
<a wire:navigate class="menu-item {{ $activeMenu === 'log-drains' ? 'menu-item-active' : '' }}"
|
<a class="menu-item {{ $activeMenu === 'log-drains' ? 'menu-item-active' : '' }}"
|
||||||
href="{{ route('server.log-drains', ['server_uuid' => $server->uuid]) }}">Log
|
href="{{ route('server.log-drains', ['server_uuid' => $server->uuid]) }}">Log
|
||||||
Drains</a>
|
Drains</a>
|
||||||
<a class="menu-item {{ $activeMenu === 'metrics' ? 'menu-item-active' : '' }}"
|
<a class="menu-item {{ $activeMenu === 'metrics' ? 'menu-item-active' : '' }}"
|
||||||
href="{{ route('server.charts', ['server_uuid' => $server->uuid]) }}">Metrics</a>
|
href="{{ route('server.charts', ['server_uuid' => $server->uuid]) }}">Metrics</a>
|
||||||
@endif
|
@endif
|
||||||
@if (!$server->isLocalhost())
|
@if (!$server->isLocalhost())
|
||||||
<a wire:navigate class="menu-item {{ $activeMenu === 'danger' ? 'menu-item-active' : '' }}"
|
<a class="menu-item {{ $activeMenu === 'danger' ? 'menu-item-active' : '' }}"
|
||||||
href="{{ route('server.delete', ['server_uuid' => $server->uuid]) }}">Danger</a>
|
href="{{ route('server.delete', ['server_uuid' => $server->uuid]) }}">Danger</a>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,20 +4,18 @@
|
|||||||
<div class="navbar-main">
|
<div class="navbar-main">
|
||||||
<nav class="flex items-center gap-6 min-h-10 whitespace-nowrap">
|
<nav class="flex items-center gap-6 min-h-10 whitespace-nowrap">
|
||||||
<a class="{{ request()->routeIs('settings.index') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('settings.index') ? 'dark:text-white' : '' }}"
|
||||||
wire:navigate
|
|
||||||
href="{{ route('settings.index') }}">
|
href="{{ route('settings.index') }}">
|
||||||
<button>Configuration</button>
|
<button>Configuration</button>
|
||||||
</a>
|
</a>
|
||||||
<a class="{{ request()->routeIs('settings.backup') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('settings.backup') ? 'dark:text-white' : '' }}"
|
||||||
wire:navigate
|
|
||||||
href="{{ route('settings.backup') }}">
|
href="{{ route('settings.backup') }}">
|
||||||
<button>Backup</button>
|
<button>Backup</button>
|
||||||
</a>
|
</a>
|
||||||
<a class="{{ request()->routeIs('settings.email') ? 'dark:text-white' : '' }}" wire:navigate
|
<a class="{{ request()->routeIs('settings.email') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('settings.email') }}">
|
href="{{ route('settings.email') }}">
|
||||||
<button>Transactional Email</button>
|
<button>Transactional Email</button>
|
||||||
</a>
|
</a>
|
||||||
<a class="{{ request()->routeIs('settings.oauth') ? 'dark:text-white' : '' }}" wire:navigate
|
<a class="{{ request()->routeIs('settings.oauth') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('settings.oauth') }}">
|
href="{{ route('settings.oauth') }}">
|
||||||
<button>OAuth</button>
|
<button>OAuth</button>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -8,16 +8,15 @@
|
|||||||
<div class="subtitle">Team wide configurations.</div>
|
<div class="subtitle">Team wide configurations.</div>
|
||||||
<div class="navbar-main">
|
<div class="navbar-main">
|
||||||
<nav class="flex items-center gap-6 min-h-10">
|
<nav class="flex items-center gap-6 min-h-10">
|
||||||
<a class="{{ request()->routeIs('team.index') ? 'dark:text-white' : '' }}" wire:navigate
|
<a class="{{ request()->routeIs('team.index') ? 'dark:text-white' : '' }}" href="{{ route('team.index') }}">
|
||||||
href="{{ route('team.index') }}">
|
|
||||||
<button>General</button>
|
<button>General</button>
|
||||||
</a>
|
</a>
|
||||||
<a class="{{ request()->routeIs('team.member.index') ? 'dark:text-white' : '' }}" wire:navigate
|
<a class="{{ request()->routeIs('team.member.index') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('team.member.index') }}">
|
href="{{ route('team.member.index') }}">
|
||||||
<button>Members</button>
|
<button>Members</button>
|
||||||
</a>
|
</a>
|
||||||
@if (isInstanceAdmin())
|
@if (isInstanceAdmin())
|
||||||
<a class="{{ request()->routeIs('team.admin-view') ? 'dark:text-white' : '' }}" wire:navigate
|
<a class="{{ request()->routeIs('team.admin-view') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('team.admin-view') }}">
|
href="{{ route('team.admin-view') }}">
|
||||||
<button>Admin View</button>
|
<button>Admin View</button>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -83,6 +83,9 @@
|
|||||||
|
|
||||||
function checkTheme() {
|
function checkTheme() {
|
||||||
theme = localStorage.theme
|
theme = localStorage.theme
|
||||||
|
if (theme == 'system') {
|
||||||
|
theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
||||||
|
}
|
||||||
if (theme == 'dark') {
|
if (theme == 'dark') {
|
||||||
baseColor = '#FCD452'
|
baseColor = '#FCD452'
|
||||||
textColor = '#ffffff'
|
textColor = '#ffffff'
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-center gap-2 text-xs font-bold">
|
<div class="flex items-center justify-center gap-2 text-xs font-bold">
|
||||||
@if ($project->environments->first())
|
@if ($project->environments->first())
|
||||||
<a class="hover:underline" wire:navigate wire:click.stop
|
<a class="hover:underline" wire:click.stop
|
||||||
href="{{ route('project.resource.create', [
|
href="{{ route('project.resource.create', [
|
||||||
'project_uuid' => $project->uuid,
|
'project_uuid' => $project->uuid,
|
||||||
'environment_uuid' => $project->environments->first()->uuid,
|
'environment_uuid' => $project->environments->first()->uuid,
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
<span class="p-2 font-bold">+ Add Resource</span>
|
<span class="p-2 font-bold">+ Add Resource</span>
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
<a class="hover:underline" wire:navigate wire:click.stop
|
<a class="hover:underline" wire:click.stop
|
||||||
href="{{ route('project.edit', ['project_uuid' => $project->uuid]) }}">
|
href="{{ route('project.edit', ['project_uuid' => $project->uuid]) }}">
|
||||||
Settings
|
Settings
|
||||||
</a>
|
</a>
|
||||||
@@ -54,8 +54,7 @@
|
|||||||
<x-modal-input buttonTitle="Add" title="New Project">
|
<x-modal-input buttonTitle="Add" title="New Project">
|
||||||
<livewire:project.add-empty />
|
<livewire:project.add-empty />
|
||||||
</x-modal-input> your first project or
|
</x-modal-input> your first project or
|
||||||
go to the <a class="underline dark:text-white" wire:navigate
|
go to the <a class="underline dark:text-white" href="{{ route('onboarding') }}">onboarding</a> page.
|
||||||
href="{{ route('onboarding') }}">onboarding</a> page.
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
@@ -66,7 +65,7 @@
|
|||||||
@if ($servers->count() > 0)
|
@if ($servers->count() > 0)
|
||||||
<div class="grid grid-cols-1 gap-2 xl:grid-cols-2">
|
<div class="grid grid-cols-1 gap-2 xl:grid-cols-2">
|
||||||
@foreach ($servers as $server)
|
@foreach ($servers as $server)
|
||||||
<a wire:navigate href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}"
|
<a href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}"
|
||||||
@class([
|
@class([
|
||||||
'gap-2 border cursor-pointer box group',
|
'gap-2 border cursor-pointer box group',
|
||||||
'border-transparent' => $server->settings->is_reachable,
|
'border-transparent' => $server->settings->is_reachable,
|
||||||
@@ -103,8 +102,7 @@
|
|||||||
<livewire:security.private-key.create from="server" />
|
<livewire:security.private-key.create from="server" />
|
||||||
</x-modal-input> a private key
|
</x-modal-input> a private key
|
||||||
or
|
or
|
||||||
go to the <a class="underline dark:text-white" wire:navigate
|
go to the <a class="underline dark:text-white" href="{{ route('onboarding') }}">onboarding</a>
|
||||||
href="{{ route('onboarding') }}">onboarding</a>
|
|
||||||
page.
|
page.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -116,8 +114,7 @@
|
|||||||
<livewire:server.create />
|
<livewire:server.create />
|
||||||
</x-modal-input> your first server
|
</x-modal-input> your first server
|
||||||
or
|
or
|
||||||
go to the <a class="underline dark:text-white" wire:navigate
|
go to the <a class="underline dark:text-white" href="{{ route('onboarding') }}">onboarding</a>
|
||||||
href="{{ route('onboarding') }}">onboarding</a>
|
|
||||||
page.
|
page.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -142,12 +139,11 @@
|
|||||||
<h4 class="pb-2">{{ $serverName }}</h4>
|
<h4 class="pb-2">{{ $serverName }}</h4>
|
||||||
<div class="grid grid-cols-1 gap-2 lg:grid-cols-3">
|
<div class="grid grid-cols-1 gap-2 lg:grid-cols-3">
|
||||||
@foreach ($deployments as $deployment)
|
@foreach ($deployments as $deployment)
|
||||||
<a wire:navigate href="{{ data_get($deployment, 'deployment_url') }}"
|
<a href="{{ data_get($deployment, 'deployment_url') }}" @class([
|
||||||
@class([
|
'gap-2 cursor-pointer box group border-l-2 border-dotted',
|
||||||
'gap-2 cursor-pointer box group border-l-2 border-dotted',
|
'dark:border-coolgray-300' => data_get($deployment, 'status') === 'queued',
|
||||||
'dark:border-coolgray-300' => data_get($deployment, 'status') === 'queued',
|
'border-yellow-500' => data_get($deployment, 'status') === 'in_progress',
|
||||||
'border-yellow-500' => data_get($deployment, 'status') === 'in_progress',
|
])>
|
||||||
])>
|
|
||||||
<div class="flex flex-col justify-center mx-6">
|
<div class="flex flex-col justify-center mx-6">
|
||||||
<div class="box-title">
|
<div class="box-title">
|
||||||
{{ data_get($deployment, 'application_name') }}
|
{{ data_get($deployment, 'application_name') }}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
@forelse ($servers as $server)
|
@forelse ($servers as $server)
|
||||||
@forelse ($server->destinations() as $destination)
|
@forelse ($server->destinations() as $destination)
|
||||||
@if ($destination->getMorphClass() === 'App\Models\StandaloneDocker')
|
@if ($destination->getMorphClass() === 'App\Models\StandaloneDocker')
|
||||||
<a class="box group" wire:navigate
|
<a class="box group"
|
||||||
href="{{ route('destination.show', ['destination_uuid' => data_get($destination, 'uuid')]) }}">
|
href="{{ route('destination.show', ['destination_uuid' => data_get($destination, 'uuid')]) }}">
|
||||||
<div class="flex flex-col mx-6">
|
<div class="flex flex-col mx-6">
|
||||||
<div class="box-title">{{ $destination->name }}</div>
|
<div class="box-title">{{ $destination->name }}</div>
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
@if ($destination->getMorphClass() === 'App\Models\SwarmDocker')
|
@if ($destination->getMorphClass() === 'App\Models\SwarmDocker')
|
||||||
<a class="box group" wire:navigate
|
<a class="box group"
|
||||||
href="{{ route('destination.show', ['destination_uuid' => data_get($destination, 'uuid')]) }}">
|
href="{{ route('destination.show', ['destination_uuid' => data_get($destination, 'uuid')]) }}">
|
||||||
<div class="flex flex-col mx-6">
|
<div class="flex flex-col mx-6">
|
||||||
<div class="box-title">{{ $destination->name }}</div>
|
<div class="box-title">{{ $destination->name }}</div>
|
||||||
|
|||||||
@@ -9,30 +9,27 @@
|
|||||||
<div class="flex flex-col h-full gap-8 pt-6 sm:flex-row">
|
<div class="flex flex-col h-full gap-8 pt-6 sm:flex-row">
|
||||||
<div class="flex flex-col items-start gap-2 min-w-fit">
|
<div class="flex flex-col items-start gap-2 min-w-fit">
|
||||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.application.configuration', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
|
href="{{ route('project.application.configuration', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">General</a>
|
||||||
wire:navigate>General</a>
|
|
||||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.application.advanced', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
|
href="{{ route('project.application.advanced', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Advanced</a>
|
||||||
wire:navigate>Advanced</a>
|
|
||||||
@if ($application->destination->server->isSwarm())
|
@if ($application->destination->server->isSwarm())
|
||||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.application.swarm', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
|
href="{{ route('project.application.swarm', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Swarm
|
||||||
wire:navigate>Swarm Configuration</a>
|
Configuration</a>
|
||||||
@endif
|
@endif
|
||||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.application.environment-variables', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
|
href="{{ route('project.application.environment-variables', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Environment
|
||||||
wire:navigate>Environment Variables</a>
|
Variables</a>
|
||||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.application.persistent-storage', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
|
href="{{ route('project.application.persistent-storage', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Persistent
|
||||||
wire:navigate>Persistent Storage</a>
|
Storage</a>
|
||||||
@if ($application->git_based())
|
@if ($application->git_based())
|
||||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.application.source', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
|
href="{{ route('project.application.source', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Git
|
||||||
wire:navigate>Git Source</a>
|
Source</a>
|
||||||
@endif
|
@endif
|
||||||
<a class="menu-item flex items-center gap-2" wire:current.exact="menu-item-active"
|
<a class="menu-item flex items-center gap-2" wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.application.servers', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
|
href="{{ route('project.application.servers', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Servers
|
||||||
wire:navigate>Servers
|
|
||||||
@if (str($application->status)->contains('degraded'))
|
@if (str($application->status)->contains('degraded'))
|
||||||
<span title="Some servers are unavailable">
|
<span title="Some servers are unavailable">
|
||||||
<svg class="w-4 h-4 text-error" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
|
<svg class="w-4 h-4 text-error" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
|
||||||
@@ -51,36 +48,32 @@
|
|||||||
@endif
|
@endif
|
||||||
</a>
|
</a>
|
||||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.application.scheduled-tasks.show', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
|
href="{{ route('project.application.scheduled-tasks.show', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Scheduled
|
||||||
wire:navigate>Scheduled Tasks</a>
|
Tasks</a>
|
||||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.application.webhooks', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
|
href="{{ route('project.application.webhooks', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Webhooks</a>
|
||||||
wire:navigate>Webhooks</a>
|
|
||||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.application.preview-deployments', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
|
href="{{ route('project.application.preview-deployments', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Preview
|
||||||
wire:navigate>Preview Deployments</a>
|
Deployments</a>
|
||||||
@if ($application->build_pack !== 'dockercompose')
|
@if ($application->build_pack !== 'dockercompose')
|
||||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.application.healthcheck', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
|
href="{{ route('project.application.healthcheck', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Healthcheck</a>
|
||||||
wire:navigate>Healthcheck</a>
|
|
||||||
@endif
|
@endif
|
||||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.application.rollback', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
|
href="{{ route('project.application.rollback', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Rollback</a>
|
||||||
wire:navigate>Rollback</a>
|
|
||||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.application.resource-limits', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
|
href="{{ route('project.application.resource-limits', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Resource
|
||||||
wire:navigate>Resource Limits</a>
|
Limits</a>
|
||||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.application.resource-operations', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
|
href="{{ route('project.application.resource-operations', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Resource
|
||||||
wire:navigate>Resource Operations</a>
|
Operations</a>
|
||||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.application.metrics', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Metrics</a>
|
href="{{ route('project.application.metrics', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Metrics</a>
|
||||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.application.tags', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
|
href="{{ route('project.application.tags', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Tags</a>
|
||||||
wire:navigate>Tags</a>
|
|
||||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.application.danger', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
|
href="{{ route('project.application.danger', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Danger
|
||||||
wire:navigate>Danger Zone</a>
|
Zone</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
@if ($currentRoute === 'project.application.configuration')
|
@if ($currentRoute === 'project.application.configuration')
|
||||||
|
|||||||
@@ -3,18 +3,21 @@
|
|||||||
<h1>Deployments</h1>
|
<h1>Deployments</h1>
|
||||||
<livewire:project.shared.configuration-checker :resource="$application" />
|
<livewire:project.shared.configuration-checker :resource="$application" />
|
||||||
<livewire:project.application.heading :application="$application" />
|
<livewire:project.application.heading :application="$application" />
|
||||||
<div class="flex flex-col gap-2 pb-10" @if (!$skip) wire:poll.5000ms='reload_deployments' @endif>
|
<div class="flex flex-col gap-2 pb-10"
|
||||||
|
@if (!$skip) wire:poll.5000ms='reload_deployments' @endif>
|
||||||
<div class="flex items-end gap-2 pt-4">
|
<div class="flex items-end gap-2 pt-4">
|
||||||
<h2>Deployments <span class="text-xs">({{ $deployments_count }})</span></h2>
|
<h2>Deployments <span class="text-xs">({{ $deployments_count }})</span></h2>
|
||||||
@if ($deployments_count > 0)
|
@if ($deployments_count > 0)
|
||||||
<x-forms.button disabled="{{ !$show_prev }}" wire:click="previous_page('{{ $default_take }}')">
|
<x-forms.button disabled="{{ !$show_prev }}" wire:click="previous_page('{{ $default_take }}')">
|
||||||
<svg class="w-6 h-6" viewBox="0 0 24 24">
|
<svg class="w-6 h-6" viewBox="0 0 24 24">
|
||||||
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m14 6l-6 6l6 6z" />
|
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
stroke-width="2" d="m14 6l-6 6l6 6z" />
|
||||||
</svg>
|
</svg>
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
<x-forms.button disabled="{{ !$show_next }}" wire:click="next_page('{{ $default_take }}')">
|
<x-forms.button disabled="{{ !$show_next }}" wire:click="next_page('{{ $default_take }}')">
|
||||||
<svg class="w-6 h-6" viewBox="0 0 24 24">
|
<svg class="w-6 h-6" viewBox="0 0 24 24">
|
||||||
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m10 18l6-6l-6-6z" />
|
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
stroke-width="2" d="m10 18l6-6l-6-6z" />
|
||||||
</svg>
|
</svg>
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
@endif
|
@endif
|
||||||
@@ -28,44 +31,57 @@
|
|||||||
@forelse ($deployments as $deployment)
|
@forelse ($deployments as $deployment)
|
||||||
<div @class([
|
<div @class([
|
||||||
'p-2 border-l-2 bg-white dark:bg-coolgray-100',
|
'p-2 border-l-2 bg-white dark:bg-coolgray-100',
|
||||||
'border-blue-500/50 border-dashed' => data_get($deployment, 'status') === 'in_progress',
|
'border-blue-500/50 border-dashed' =>
|
||||||
'border-purple-500/50 border-dashed' => data_get($deployment, 'status') === 'queued',
|
data_get($deployment, 'status') === 'in_progress',
|
||||||
'border-white border-dashed' => data_get($deployment, 'status') === 'cancelled-by-user',
|
'border-purple-500/50 border-dashed' =>
|
||||||
|
data_get($deployment, 'status') === 'queued',
|
||||||
|
'border-white border-dashed' =>
|
||||||
|
data_get($deployment, 'status') === 'cancelled-by-user',
|
||||||
'border-error' => data_get($deployment, 'status') === 'failed',
|
'border-error' => data_get($deployment, 'status') === 'failed',
|
||||||
'border-success' => data_get($deployment, 'status') === 'finished',
|
'border-success' => data_get($deployment, 'status') === 'finished',
|
||||||
])>
|
])>
|
||||||
<a href="{{ $current_url . '/' . data_get($deployment, 'deployment_uuid') }}" wire:navigate class="block">
|
<a href="{{ $current_url . '/' . data_get($deployment, 'deployment_uuid') }}" class="block">
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<div class="flex items-center gap-2 mb-2">
|
<div class="flex items-center gap-2 mb-2">
|
||||||
<span @class([
|
<span @class([
|
||||||
'px-3 py-1 rounded-md text-xs font-medium shadow-sm',
|
'px-3 py-1 rounded-md text-xs font-medium shadow-sm',
|
||||||
'bg-blue-100/80 text-blue-700 dark:bg-blue-500/20 dark:text-blue-300' => data_get($deployment, 'status') === 'in_progress',
|
'bg-blue-100/80 text-blue-700 dark:bg-blue-500/20 dark:text-blue-300' =>
|
||||||
'bg-purple-100/80 text-purple-700 dark:bg-purple-500/20 dark:text-purple-300' => data_get($deployment, 'status') === 'queued',
|
data_get($deployment, 'status') === 'in_progress',
|
||||||
'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-200' => data_get($deployment, 'status') === 'failed',
|
'bg-purple-100/80 text-purple-700 dark:bg-purple-500/20 dark:text-purple-300' =>
|
||||||
'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-200' => data_get($deployment, 'status') === 'finished',
|
data_get($deployment, 'status') === 'queued',
|
||||||
'bg-gray-100 text-gray-700 dark:bg-gray-600/30 dark:text-gray-300' => data_get($deployment, 'status') === 'cancelled-by-user',
|
'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-200' =>
|
||||||
|
data_get($deployment, 'status') === 'failed',
|
||||||
|
'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-200' =>
|
||||||
|
data_get($deployment, 'status') === 'finished',
|
||||||
|
'bg-gray-100 text-gray-700 dark:bg-gray-600/30 dark:text-gray-300' =>
|
||||||
|
data_get($deployment, 'status') === 'cancelled-by-user',
|
||||||
])>
|
])>
|
||||||
@php
|
@php
|
||||||
$statusText = match(data_get($deployment, 'status')) {
|
$statusText = match (data_get($deployment, 'status')) {
|
||||||
'finished' => 'Success',
|
'finished' => 'Success',
|
||||||
'in_progress' => 'In Progress',
|
'in_progress' => 'In Progress',
|
||||||
'cancelled-by-user' => 'Cancelled',
|
'cancelled-by-user' => 'Cancelled',
|
||||||
'queued' => 'Queued',
|
'queued' => 'Queued',
|
||||||
default => ucfirst(data_get($deployment, 'status'))
|
default => ucfirst(data_get($deployment, 'status')),
|
||||||
};
|
};
|
||||||
@endphp
|
@endphp
|
||||||
{{ $statusText }}
|
{{ $statusText }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@if(data_get($deployment, 'status') !== 'queued')
|
@if (data_get($deployment, 'status') !== 'queued')
|
||||||
<div class="text-gray-600 dark:text-gray-400 text-sm">
|
<div class="text-gray-600 dark:text-gray-400 text-sm">
|
||||||
Started: {{ formatDateInServerTimezone(data_get($deployment, 'created_at'), data_get($application, 'destination.server')) }}
|
Started:
|
||||||
@if($deployment->status !== 'in_progress' && $deployment->status !== 'cancelled-by-user')
|
{{ formatDateInServerTimezone(data_get($deployment, 'created_at'), data_get($application, 'destination.server')) }}
|
||||||
<br>Ended: {{ formatDateInServerTimezone(data_get($deployment, 'finished_at'), data_get($application, 'destination.server')) }}
|
@if ($deployment->status !== 'in_progress' && $deployment->status !== 'cancelled-by-user')
|
||||||
<br>Duration: {{ calculateDuration(data_get($deployment, 'created_at'), data_get($deployment, 'finished_at')) }}
|
<br>Ended:
|
||||||
<br>Finished {{ \Carbon\Carbon::parse(data_get($deployment, 'finished_at'))->diffForHumans() }}
|
{{ formatDateInServerTimezone(data_get($deployment, 'finished_at'), data_get($application, 'destination.server')) }}
|
||||||
|
<br>Duration:
|
||||||
|
{{ calculateDuration(data_get($deployment, 'created_at'), data_get($deployment, 'finished_at')) }}
|
||||||
|
<br>Finished
|
||||||
|
{{ \Carbon\Carbon::parse(data_get($deployment, 'finished_at'))->diffForHumans() }}
|
||||||
@elseif($deployment->status === 'in_progress')
|
@elseif($deployment->status === 'in_progress')
|
||||||
<br>Running for: {{ calculateDuration(data_get($deployment, 'created_at'), now()) }}
|
<br>Running for:
|
||||||
|
{{ calculateDuration(data_get($deployment, 'created_at'), now()) }}
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
@@ -75,14 +91,13 @@
|
|||||||
<div x-data="{ expanded: false }">
|
<div x-data="{ expanded: false }">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="font-medium">Commit:</span>
|
<span class="font-medium">Commit:</span>
|
||||||
<a wire:navigate.prevent
|
<a href="{{ $application->gitCommitLink(data_get($deployment, 'commit')) }}"
|
||||||
href="{{ $application->gitCommitLink(data_get($deployment, 'commit')) }}"
|
target="_blank" class="underline">
|
||||||
target="_blank"
|
|
||||||
class="underline">
|
|
||||||
{{ substr(data_get($deployment, 'commit'), 0, 7) }}
|
{{ substr(data_get($deployment, 'commit'), 0, 7) }}
|
||||||
</a>
|
</a>
|
||||||
@if (!$deployment->commitMessage())
|
@if (!$deployment->commitMessage())
|
||||||
<span class="bg-gray-200/70 dark:bg-gray-600/20 px-2 py-0.5 rounded-md text-xs text-gray-800 dark:text-gray-100 border border-gray-400/30">
|
<span
|
||||||
|
class="bg-gray-200/70 dark:bg-gray-600/20 px-2 py-0.5 rounded-md text-xs text-gray-800 dark:text-gray-100 border border-gray-400/30">
|
||||||
@if (data_get($deployment, 'is_webhook'))
|
@if (data_get($deployment, 'is_webhook'))
|
||||||
Webhook
|
Webhook
|
||||||
@if (data_get($deployment, 'pull_request_id'))
|
@if (data_get($deployment, 'pull_request_id'))
|
||||||
@@ -101,21 +116,24 @@
|
|||||||
@endif
|
@endif
|
||||||
@if ($deployment->commitMessage())
|
@if ($deployment->commitMessage())
|
||||||
<span class="text-gray-600 dark:text-gray-400">-</span>
|
<span class="text-gray-600 dark:text-gray-400">-</span>
|
||||||
<a wire:navigate.prevent
|
<a href="{{ $application->gitCommitLink(data_get($deployment, 'commit')) }}"
|
||||||
href="{{ $application->gitCommitLink(data_get($deployment, 'commit')) }}"
|
target="_blank"
|
||||||
target="_blank"
|
class="text-gray-600 dark:text-gray-400 truncate max-w-md underline">
|
||||||
class="text-gray-600 dark:text-gray-400 truncate max-w-md underline">
|
|
||||||
{{ Str::before($deployment->commitMessage(), "\n") }}
|
{{ Str::before($deployment->commitMessage(), "\n") }}
|
||||||
</a>
|
</a>
|
||||||
@if ($deployment->commitMessage() !== Str::before($deployment->commitMessage(), "\n"))
|
@if ($deployment->commitMessage() !== Str::before($deployment->commitMessage(), "\n"))
|
||||||
<button @click="expanded = !expanded"
|
<button @click="expanded = !expanded"
|
||||||
class="text-gray-600 dark:text-gray-400 flex items-center gap-1">
|
class="text-gray-600 dark:text-gray-400 flex items-center gap-1">
|
||||||
<svg x-bind:class="{'rotate-180': expanded}" class="w-4 h-4 transition-transform" viewBox="0 0 24 24">
|
<svg x-bind:class="{ 'rotate-180': expanded }"
|
||||||
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m6 9l6 6l6-6"/>
|
class="w-4 h-4 transition-transform" viewBox="0 0 24 24">
|
||||||
|
<path fill="none" stroke="currentColor"
|
||||||
|
stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
stroke-width="2" d="m6 9l6 6l6-6" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
@endif
|
@endif
|
||||||
<span class="bg-gray-200/70 dark:bg-gray-600/20 px-2 py-0.5 rounded-md text-xs text-gray-800 dark:text-gray-100 border border-gray-400/30">
|
<span
|
||||||
|
class="bg-gray-200/70 dark:bg-gray-600/20 px-2 py-0.5 rounded-md text-xs text-gray-800 dark:text-gray-100 border border-gray-400/30">
|
||||||
@if (data_get($deployment, 'is_webhook'))
|
@if (data_get($deployment, 'is_webhook'))
|
||||||
Webhook
|
Webhook
|
||||||
@if (data_get($deployment, 'pull_request_id'))
|
@if (data_get($deployment, 'pull_request_id'))
|
||||||
@@ -134,11 +152,10 @@
|
|||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
@if ($deployment->commitMessage())
|
@if ($deployment->commitMessage())
|
||||||
<div x-show="expanded"
|
<div x-show="expanded" x-transition:enter="transition ease-out duration-200"
|
||||||
x-transition:enter="transition ease-out duration-200"
|
x-transition:enter-start="opacity-0 transform -translate-y-2"
|
||||||
x-transition:enter-start="opacity-0 transform -translate-y-2"
|
x-transition:enter-end="opacity-100 transform translate-y-0"
|
||||||
x-transition:enter-end="opacity-100 transform translate-y-0"
|
class="mt-2 ml-4 text-gray-600 dark:text-gray-400">
|
||||||
class="mt-2 ml-4 text-gray-600 dark:text-gray-400">
|
|
||||||
{{ Str::after($deployment->commitMessage(), "\n") }}
|
{{ Str::after($deployment->commitMessage(), "\n") }}
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|||||||
@@ -3,20 +3,20 @@
|
|||||||
<div class="navbar-main">
|
<div class="navbar-main">
|
||||||
<nav class="flex flex-shrink-0 gap-6 items-center whitespace-nowrap scrollbar min-h-10">
|
<nav class="flex flex-shrink-0 gap-6 items-center whitespace-nowrap scrollbar min-h-10">
|
||||||
<a class="{{ request()->routeIs('project.application.configuration') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.application.configuration') ? 'dark:text-white' : '' }}"
|
||||||
wire:navigate href="{{ route('project.application.configuration', $parameters) }}">
|
href="{{ route('project.application.configuration', $parameters) }}">
|
||||||
Configuration
|
Configuration
|
||||||
</a>
|
</a>
|
||||||
<a class="{{ request()->routeIs('project.application.deployment.index') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.application.deployment.index') ? 'dark:text-white' : '' }}"
|
||||||
wire:navigate href="{{ route('project.application.deployment.index', $parameters) }}">
|
href="{{ route('project.application.deployment.index', $parameters) }}">
|
||||||
<button>Deployments</button>
|
<button>Deployments</button>
|
||||||
</a>
|
</a>
|
||||||
<a class="{{ request()->routeIs('project.application.logs') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.application.logs') ? 'dark:text-white' : '' }}"
|
||||||
wire:navigate href="{{ route('project.application.logs', $parameters) }}">
|
href="{{ route('project.application.logs', $parameters) }}">
|
||||||
<button>Logs</button>
|
<button>Logs</button>
|
||||||
</a>
|
</a>
|
||||||
@if (!$application->destination->server->isSwarm())
|
@if (!$application->destination->server->isSwarm())
|
||||||
<a class="{{ request()->routeIs('project.application.command') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.application.command') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('project.application.command', $parameters) }}">
|
href="{{ route('project.application.command', $parameters) }}">
|
||||||
<button>Terminal</button>
|
<button>Terminal</button>
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
@@ -117,7 +117,8 @@
|
|||||||
@script
|
@script
|
||||||
<script>
|
<script>
|
||||||
$wire.$on('stopEvent', () => {
|
$wire.$on('stopEvent', () => {
|
||||||
$wire.$dispatch('info', 'Stopping application.');
|
$wire.$dispatch('info',
|
||||||
|
'Gracefully stopping application, it could take a while depending on the application.');
|
||||||
$wire.$call('stop');
|
$wire.$call('stop');
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -8,37 +8,33 @@
|
|||||||
<div class="flex flex-col h-full gap-8 pt-6 sm:flex-row">
|
<div class="flex flex-col h-full gap-8 pt-6 sm:flex-row">
|
||||||
<div class="flex flex-col items-start gap-2 min-w-fit">
|
<div class="flex flex-col items-start gap-2 min-w-fit">
|
||||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.database.configuration', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}"
|
href="{{ route('project.database.configuration', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}">General</a>
|
||||||
wire:navigate>General</a>
|
|
||||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.database.environment-variables', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}"
|
href="{{ route('project.database.environment-variables', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}">Environment
|
||||||
wire:navigate>Environment Variables</a>
|
Variables</a>
|
||||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.database.servers', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}"
|
href="{{ route('project.database.servers', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}">Servers</a>
|
||||||
wire:navigate>Servers</a>
|
|
||||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.database.persistent-storage', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}"
|
href="{{ route('project.database.persistent-storage', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}">Persistent
|
||||||
wire:navigate>Persistent Storage</a>
|
Storage</a>
|
||||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.database.import-backups', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}">Import
|
href="{{ route('project.database.import-backups', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}">Import
|
||||||
Backups</a>
|
Backups</a>
|
||||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.database.webhooks', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}"
|
href="{{ route('project.database.webhooks', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}">Webhooks</a>
|
||||||
wire:navigate>Webhooks</a>
|
|
||||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.database.resource-limits', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}"
|
href="{{ route('project.database.resource-limits', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}">Resource
|
||||||
wire:navigate>Resource Limits</a>
|
Limits</a>
|
||||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.database.resource-operations', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}"
|
href="{{ route('project.database.resource-operations', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}">Resource
|
||||||
wire:navigate>Resource Operations</a>
|
Operations</a>
|
||||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.database.metrics', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}">Metrics</a>
|
href="{{ route('project.database.metrics', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}">Metrics</a>
|
||||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.database.tags', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}"
|
href="{{ route('project.database.tags', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}">Tags</a>
|
||||||
wire:navigate>Tags</a>
|
|
||||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.database.danger', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}"
|
href="{{ route('project.database.danger', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}">Danger
|
||||||
wire:navigate>Danger Zone</a>
|
Zone</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
@if ($currentRoute === 'project.database.configuration')
|
@if ($currentRoute === 'project.database.configuration')
|
||||||
|
|||||||
@@ -9,12 +9,12 @@
|
|||||||
<div class="navbar-main">
|
<div class="navbar-main">
|
||||||
<nav
|
<nav
|
||||||
class="flex overflow-x-scroll flex-shrink-0 gap-6 items-center whitespace-nowrap sm:overflow-x-hidden scrollbar min-h-10">
|
class="flex overflow-x-scroll flex-shrink-0 gap-6 items-center whitespace-nowrap sm:overflow-x-hidden scrollbar min-h-10">
|
||||||
<a wire:navigate class="{{ request()->routeIs('project.database.configuration') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.database.configuration') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('project.database.configuration', $parameters) }}">
|
href="{{ route('project.database.configuration', $parameters) }}">
|
||||||
<button>Configuration</button>
|
<button>Configuration</button>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a wire:navigate class="{{ request()->routeIs('project.database.logs') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.database.logs') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('project.database.logs', $parameters) }}">
|
href="{{ route('project.database.logs', $parameters) }}">
|
||||||
<button>Logs</button>
|
<button>Logs</button>
|
||||||
</a>
|
</a>
|
||||||
@@ -27,8 +27,7 @@
|
|||||||
$database->getMorphClass() === 'App\Models\StandaloneMongodb' ||
|
$database->getMorphClass() === 'App\Models\StandaloneMongodb' ||
|
||||||
$database->getMorphClass() === 'App\Models\StandaloneMysql' ||
|
$database->getMorphClass() === 'App\Models\StandaloneMysql' ||
|
||||||
$database->getMorphClass() === 'App\Models\StandaloneMariadb')
|
$database->getMorphClass() === 'App\Models\StandaloneMariadb')
|
||||||
<a wire:navigate
|
<a class="{{ request()->routeIs('project.database.backup.index') ? 'dark:text-white' : '' }}"
|
||||||
class="{{ request()->routeIs('project.database.backup.index') ? 'dark:text-white' : '' }}"
|
|
||||||
href="{{ route('project.database.backup.index', $parameters) }}">
|
href="{{ route('project.database.backup.index', $parameters) }}">
|
||||||
<button>Backups</button>
|
<button>Backups</button>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -1,34 +1,52 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
@forelse($database->scheduledBackups as $backup)
|
@if ($database->is_migrated && blank($database->custom_type))
|
||||||
@if ($type == 'database')
|
<div>
|
||||||
<a class="box"
|
<div>Select the type of
|
||||||
wire:navigate
|
database to enable automated backups.</div>
|
||||||
href="{{ route('project.database.backup.execution', [...$parameters, 'backup_uuid' => $backup->uuid]) }}">
|
<div class="pb-4"> If your database is not listed, automated backups are not supported.</div>
|
||||||
<div class="flex flex-col">
|
<form wire:submit="setCustomType" class="flex gap-2 items-end">
|
||||||
<div>Frequency: {{ $backup->frequency }}
|
<div class="w-96">
|
||||||
({{ data_get($backup->server(), 'settings.server_timezone', 'Instance timezone') }})
|
<x-forms.select label="Type" id="custom_type">
|
||||||
</div>
|
<option selected value="mysql">MySQL</option>
|
||||||
<div>Last backup: {{ data_get($backup->latest_log, 'status', 'No backup yet') }}</div>
|
<option value="mariadb">MariaDB</option>
|
||||||
|
<option value="postgresql">PostgreSQL</option>
|
||||||
|
<option value="mongodb">MongoDB</option>
|
||||||
|
</x-forms.select>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
<x-forms.button type="submit">Set</x-forms.button>
|
||||||
@else
|
</form>
|
||||||
<div class="box" wire:navigate wire:click="setSelectedBackup('{{ data_get($backup, 'id') }}')">
|
</div>
|
||||||
<div @class([
|
@else
|
||||||
'border-coollabs' =>
|
@forelse($database->scheduledBackups as $backup)
|
||||||
data_get($backup, 'id') === data_get($selectedBackup, 'id'),
|
@if ($type == 'database')
|
||||||
'flex flex-col border-l-2 border-transparent',
|
<a class="box"
|
||||||
])>
|
href="{{ route('project.database.backup.execution', [...$parameters, 'backup_uuid' => $backup->uuid]) }}">
|
||||||
<div>Frequency: {{ $backup->frequency }}
|
<div class="flex flex-col">
|
||||||
({{ data_get($backup->server(), 'settings.server_timezone', 'Instance timezone') }})
|
<div>Frequency: {{ $backup->frequency }}
|
||||||
|
({{ data_get($backup->server(), 'settings.server_timezone', 'Instance timezone') }})
|
||||||
|
</div>
|
||||||
|
<div>Last backup: {{ data_get($backup->latest_log, 'status', 'No backup yet') }}</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
@else
|
||||||
|
<div class="box" wire:click="setSelectedBackup('{{ data_get($backup, 'id') }}')">
|
||||||
|
<div @class([
|
||||||
|
'border-coollabs' =>
|
||||||
|
data_get($backup, 'id') === data_get($selectedBackup, 'id'),
|
||||||
|
'flex flex-col border-l-2 border-transparent',
|
||||||
|
])>
|
||||||
|
<div>Frequency: {{ $backup->frequency }}
|
||||||
|
({{ data_get($backup->server(), 'settings.server_timezone', 'Instance timezone') }})
|
||||||
|
</div>
|
||||||
|
<div>Last backup: {{ data_get($backup->latest_log, 'status', 'No backup yet') }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>Last backup: {{ data_get($backup->latest_log, 'status', 'No backup yet') }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
@endif
|
||||||
@endif
|
@empty
|
||||||
@empty
|
<div>No scheduled backups configured.</div>
|
||||||
<div>No scheduled backups configured.</div>
|
@endforelse
|
||||||
@endforelse
|
@endif
|
||||||
</div>
|
</div>
|
||||||
@if ($type === 'service-database' && $selectedBackup)
|
@if ($type === 'service-database' && $selectedBackup)
|
||||||
<div class="pt-10">
|
<div class="pt-10">
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<ol class="flex flex-wrap items-center gap-y-1">
|
<ol class="flex flex-wrap items-center gap-y-1">
|
||||||
<li class="inline-flex items-center">
|
<li class="inline-flex items-center">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<a class="text-xs truncate lg:text-sm" wire:navigate
|
<a class="text-xs truncate lg:text-sm"
|
||||||
href="{{ route('project.show', ['project_uuid' => $project->uuid]) }}">
|
href="{{ route('project.show', ['project_uuid' => $project->uuid]) }}">
|
||||||
{{ $project->name }}</a>
|
{{ $project->name }}</a>
|
||||||
<svg aria-hidden="true" class="w-4 h-4 mx-1 font-bold dark:text-warning" fill="currentColor"
|
<svg aria-hidden="true" class="w-4 h-4 mx-1 font-bold dark:text-warning" fill="currentColor"
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<a class="text-xs truncate lg:text-sm" wire:navigate
|
<a class="text-xs truncate lg:text-sm"
|
||||||
href="{{ route('project.resource.index', ['environment_uuid' => $environment->uuid, 'project_uuid' => $project->uuid]) }}">
|
href="{{ route('project.resource.index', ['environment_uuid' => $environment->uuid, 'project_uuid' => $project->uuid]) }}">
|
||||||
{{ $environment->name }}
|
{{ $environment->name }}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -24,11 +24,10 @@
|
|||||||
<div x-text="project.description"></div>
|
<div x-text="project.description"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-center gap-2 pt-4 pb-2 mr-4 text-xs lg:py-0 lg:justify-normal">
|
<div
|
||||||
<a class="mx-4 font-bold hover:underline"
|
class="flex items-center justify-center gap-2 pt-4 pb-2 mr-4 text-xs lg:py-0 lg:justify-normal">
|
||||||
wire:navigate
|
<a class="mx-4 font-bold hover:underline" wire:click.stop
|
||||||
wire:click.stop
|
:href="`/project/${project.uuid}/edit`">
|
||||||
:href="`/project/${project.uuid}/edit`">
|
|
||||||
Settings
|
Settings
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -103,7 +103,7 @@
|
|||||||
:src='service.logo'
|
:src='service.logo'
|
||||||
x-on:error.window="$event.target.src = service.logo_github_url"
|
x-on:error.window="$event.target.src = service.logo_github_url"
|
||||||
onerror="this.onerror=null; this.src=this.getAttribute('data-fallback');"
|
onerror="this.onerror=null; this.src=this.getAttribute('data-fallback');"
|
||||||
x-on:error="$event.target.src = 'coolify-logo.svg'"
|
x-on:error="$event.target.src = '/coolify-logo.svg'"
|
||||||
:data-fallback='service.logo_github_url' />
|
:data-fallback='service.logo_github_url' />
|
||||||
</template>
|
</template>
|
||||||
</x-slot:logo>
|
</x-slot:logo>
|
||||||
|
|||||||
@@ -6,15 +6,15 @@
|
|||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<h1>Resources</h1>
|
<h1>Resources</h1>
|
||||||
@if ($environment->isEmpty())
|
@if ($environment->isEmpty())
|
||||||
<a class="button" wire:navigate
|
<a class="button"
|
||||||
href="{{ route('project.clone-me', ['project_uuid' => data_get($project, 'uuid'), 'environment_uuid' => data_get($environment, 'uuid')]) }}">
|
href="{{ route('project.clone-me', ['project_uuid' => data_get($project, 'uuid'), 'environment_uuid' => data_get($environment, 'uuid')]) }}">
|
||||||
Clone
|
Clone
|
||||||
</a>
|
</a>
|
||||||
@else
|
@else
|
||||||
<a href="{{ route('project.resource.create', ['project_uuid' => data_get($parameters, 'project_uuid'), 'environment_uuid' => data_get($environment, 'uuid')]) }}"
|
<a href="{{ route('project.resource.create', ['project_uuid' => data_get($parameters, 'project_uuid'), 'environment_uuid' => data_get($environment, 'uuid')]) }}"
|
||||||
wire:navigate class="button">+
|
class="button">+
|
||||||
New</a>
|
New</a>
|
||||||
<a class="button" wire:navigate
|
<a class="button"
|
||||||
href="{{ route('project.clone-me', ['project_uuid' => data_get($project, 'uuid'), 'environment_uuid' => data_get($environment, 'uuid')]) }}">
|
href="{{ route('project.clone-me', ['project_uuid' => data_get($project, 'uuid'), 'environment_uuid' => data_get($environment, 'uuid')]) }}">
|
||||||
Clone
|
Clone
|
||||||
</a>
|
</a>
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
<nav class="flex pt-2 pb-6">
|
<nav class="flex pt-2 pb-6">
|
||||||
<ol class="flex items-center">
|
<ol class="flex items-center">
|
||||||
<li class="inline-flex items-center">
|
<li class="inline-flex items-center">
|
||||||
<a class="text-xs truncate lg:text-sm" wire:navigate
|
<a class="text-xs truncate lg:text-sm"
|
||||||
href="{{ route('project.show', ['project_uuid' => data_get($parameters, 'project_uuid')]) }}">
|
href="{{ route('project.show', ['project_uuid' => data_get($parameters, 'project_uuid')]) }}">
|
||||||
{{ $project->name }}</a>
|
{{ $project->name }}</a>
|
||||||
</li>
|
</li>
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
@if ($environment->isEmpty())
|
@if ($environment->isEmpty())
|
||||||
<a wire:navigate href="{{ route('project.resource.create', ['project_uuid' => data_get($parameters, 'project_uuid'), 'environment_uuid' => data_get($environment, 'uuid')]) }}"
|
<a href="{{ route('project.resource.create', ['project_uuid' => data_get($parameters, 'project_uuid'), 'environment_uuid' => data_get($environment, 'uuid')]) }}"
|
||||||
class="items-center justify-center box">+ Add New Resource</a>
|
class="items-center justify-center box">+ Add New Resource</a>
|
||||||
@else
|
@else
|
||||||
<div x-data="searchComponent()">
|
<div x-data="searchComponent()">
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
class="grid grid-cols-1 gap-4 pt-4 lg:grid-cols-2 xl:grid-cols-3">
|
class="grid grid-cols-1 gap-4 pt-4 lg:grid-cols-2 xl:grid-cols-3">
|
||||||
<template x-for="item in filteredApplications" :key="item.uuid">
|
<template x-for="item in filteredApplications" :key="item.uuid">
|
||||||
<span>
|
<span>
|
||||||
<a class="h-24 box group" wire:navigate :href="item.hrefLink">
|
<a class="h-24 box group" :href="item.hrefLink">
|
||||||
<div class="flex flex-col w-full">
|
<div class="flex flex-col w-full">
|
||||||
<div class="flex gap-2 px-4">
|
<div class="flex gap-2 px-4">
|
||||||
<div class="pb-2 truncate box-title" x-text="item.name"></div>
|
<div class="pb-2 truncate box-title" x-text="item.name"></div>
|
||||||
@@ -90,15 +90,10 @@
|
|||||||
<div
|
<div
|
||||||
class="flex flex-wrap gap-1 pt-1 group-hover:dark:text-white group-hover:text-black group min-h-6">
|
class="flex flex-wrap gap-1 pt-1 group-hover:dark:text-white group-hover:text-black group min-h-6">
|
||||||
<template x-for="tag in item.tags">
|
<template x-for="tag in item.tags">
|
||||||
<a :href="`/tags/${tag.name}`"
|
<a :href="`/tags/${tag.name}`" class="tag" x-text="tag.name">
|
||||||
wire:navigate
|
|
||||||
class="tag"
|
|
||||||
x-text="tag.name">
|
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
<a :href="`${item.hrefLink}/tags`"
|
<a :href="`${item.hrefLink}/tags`" class="add-tag">
|
||||||
wire:navigate
|
|
||||||
class="add-tag">
|
|
||||||
Add tag
|
Add tag
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -112,7 +107,7 @@
|
|||||||
class="grid grid-cols-1 gap-4 pt-4 lg:grid-cols-2 xl:grid-cols-3">
|
class="grid grid-cols-1 gap-4 pt-4 lg:grid-cols-2 xl:grid-cols-3">
|
||||||
<template x-for="item in filteredDatabases" :key="item.uuid">
|
<template x-for="item in filteredDatabases" :key="item.uuid">
|
||||||
<span>
|
<span>
|
||||||
<a class="h-24 box group" wire:navigate :href="item.hrefLink">
|
<a class="h-24 box group" :href="item.hrefLink">
|
||||||
<div class="flex flex-col w-full">
|
<div class="flex flex-col w-full">
|
||||||
<div class="flex gap-2 px-4">
|
<div class="flex gap-2 px-4">
|
||||||
<div class="pb-2 truncate box-title" x-text="item.name"></div>
|
<div class="pb-2 truncate box-title" x-text="item.name"></div>
|
||||||
@@ -141,15 +136,10 @@
|
|||||||
<div
|
<div
|
||||||
class="flex flex-wrap gap-1 pt-1 group-hover:dark:text-white group-hover:text-black group min-h-6">
|
class="flex flex-wrap gap-1 pt-1 group-hover:dark:text-white group-hover:text-black group min-h-6">
|
||||||
<template x-for="tag in item.tags">
|
<template x-for="tag in item.tags">
|
||||||
<a :href="`/tags/${tag.name}`"
|
<a :href="`/tags/${tag.name}`" class="tag" x-text="tag.name">
|
||||||
wire:navigate
|
|
||||||
class="tag"
|
|
||||||
x-text="tag.name">
|
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
<a :href="`${item.hrefLink}/tags`"
|
<a :href="`${item.hrefLink}/tags`" class="add-tag">
|
||||||
wire:navigate
|
|
||||||
class="add-tag">
|
|
||||||
Add tag
|
Add tag
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -163,7 +153,7 @@
|
|||||||
class="grid grid-cols-1 gap-4 pt-4 lg:grid-cols-2 xl:grid-cols-3">
|
class="grid grid-cols-1 gap-4 pt-4 lg:grid-cols-2 xl:grid-cols-3">
|
||||||
<template x-for="item in filteredServices" :key="item.uuid">
|
<template x-for="item in filteredServices" :key="item.uuid">
|
||||||
<span>
|
<span>
|
||||||
<a class="h-24 box group" wire:navigate :href="item.hrefLink">
|
<a class="h-24 box group" :href="item.hrefLink">
|
||||||
<div class="flex flex-col w-full">
|
<div class="flex flex-col w-full">
|
||||||
<div class="flex gap-2 px-4">
|
<div class="flex gap-2 px-4">
|
||||||
<div class="pb-2 truncate box-title" x-text="item.name"></div>
|
<div class="pb-2 truncate box-title" x-text="item.name"></div>
|
||||||
@@ -192,15 +182,10 @@
|
|||||||
<div
|
<div
|
||||||
class="flex flex-wrap gap-1 pt-1 group-hover:dark:text-white group-hover:text-black group min-h-6">
|
class="flex flex-wrap gap-1 pt-1 group-hover:dark:text-white group-hover:text-black group min-h-6">
|
||||||
<template x-for="tag in item.tags">
|
<template x-for="tag in item.tags">
|
||||||
<a :href="`/tags/${tag.name}`"
|
<a :href="`/tags/${tag.name}`" class="tag" x-text="tag.name">
|
||||||
wire:navigate
|
|
||||||
class="tag"
|
|
||||||
x-text="tag.name">
|
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
<a :href="`${item.hrefLink}/tags`"
|
<a :href="`${item.hrefLink}/tags`" class="add-tag">
|
||||||
wire:navigate
|
|
||||||
class="add-tag">
|
|
||||||
Add tag
|
Add tag
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,31 +9,28 @@
|
|||||||
<a class="menu-item sm:min-w-fit" target="_blank" href="{{ $service->documentation() }}">Documentation
|
<a class="menu-item sm:min-w-fit" target="_blank" href="{{ $service->documentation() }}">Documentation
|
||||||
<x-external-link /></a>
|
<x-external-link /></a>
|
||||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.service.configuration', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid]) }}"
|
href="{{ route('project.service.configuration', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid]) }}">General</a>
|
||||||
wire:navigate>General</a>
|
|
||||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.service.environment-variables', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid]) }}"
|
href="{{ route('project.service.environment-variables', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid]) }}">Environment
|
||||||
wire:navigate>Environment Variables</a>
|
Variables</a>
|
||||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.service.storages', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid]) }}"
|
href="{{ route('project.service.storages', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid]) }}">Persistent
|
||||||
wire:navigate>Persistent Storages</a>
|
Storages</a>
|
||||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.service.scheduled-tasks.show', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid]) }}"
|
href="{{ route('project.service.scheduled-tasks.show', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid]) }}">Scheduled
|
||||||
wire:navigate>Scheduled Tasks</a>
|
Tasks</a>
|
||||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.service.webhooks', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid]) }}"
|
href="{{ route('project.service.webhooks', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid]) }}">Webhooks</a>
|
||||||
wire:navigate>Webhooks</a>
|
|
||||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.service.resource-operations', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid]) }}"
|
href="{{ route('project.service.resource-operations', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid]) }}">Resource
|
||||||
wire:navigate>Resource Operations</a>
|
Operations</a>
|
||||||
|
|
||||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.service.tags', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid]) }}"
|
href="{{ route('project.service.tags', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid]) }}">Tags</a>
|
||||||
wire:navigate>Tags</a>
|
|
||||||
|
|
||||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||||
href="{{ route('project.service.danger', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid]) }}"
|
href="{{ route('project.service.danger', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid]) }}">Danger
|
||||||
wire:navigate>Danger Zone</a>
|
Zone</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
@if ($currentRoute === 'project.service.configuration')
|
@if ($currentRoute === 'project.service.configuration')
|
||||||
@@ -42,7 +39,7 @@
|
|||||||
<div class="grid grid-cols-1 gap-2 pt-4 xl:grid-cols-1" wire:poll.10000ms="check_status">
|
<div class="grid grid-cols-1 gap-2 pt-4 xl:grid-cols-1" wire:poll.10000ms="check_status">
|
||||||
@foreach ($applications as $application)
|
@foreach ($applications as $application)
|
||||||
<div @class([
|
<div @class([
|
||||||
'border-l border-dashed border-red-500 ' => str(
|
'border-l border-dashed border-red-500' => str(
|
||||||
$application->status)->contains(['exited']),
|
$application->status)->contains(['exited']),
|
||||||
'border-l border-dashed border-success' => str(
|
'border-l border-dashed border-success' => str(
|
||||||
$application->status)->contains(['running']),
|
$application->status)->contains(['running']),
|
||||||
@@ -95,8 +92,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex items-center px-4">
|
<div class="flex items-center px-4">
|
||||||
<a class="mx-4 text-xs font-bold hover:underline"
|
<a class="mx-4 text-xs font-bold hover:underline"
|
||||||
href="{{ route('project.service.index', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid, 'stack_service_uuid' => $application->uuid]) }}"
|
href="{{ route('project.service.index', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid, 'stack_service_uuid' => $application->uuid]) }}">
|
||||||
wire:navigate>
|
|
||||||
Settings
|
Settings
|
||||||
</a>
|
</a>
|
||||||
@if (str($application->status)->contains('running'))
|
@if (str($application->status)->contains('running'))
|
||||||
@@ -142,16 +138,14 @@
|
|||||||
<div class="text-xs">{{ $database->status }}</div>
|
<div class="text-xs">{{ $database->status }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center px-4">
|
<div class="flex items-center px-4">
|
||||||
@if ($database->isBackupSolutionAvailable())
|
@if ($database->isBackupSolutionAvailable() || $database->is_migrated)
|
||||||
<a class="mx-4 text-xs font-bold hover:underline"
|
<a class="mx-4 text-xs font-bold hover:underline"
|
||||||
href="{{ route('project.service.index', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid, 'stack_service_uuid' => $database->uuid]) }}#backups"
|
href="{{ route('project.service.index', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid, 'stack_service_uuid' => $database->uuid]) }}#backups">
|
||||||
wire:navigate>
|
|
||||||
Backups
|
Backups
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
<a class="mx-4 text-xs font-bold hover:underline"
|
<a class="mx-4 text-xs font-bold hover:underline"
|
||||||
href="{{ route('project.service.index', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid, 'stack_service_uuid' => $database->uuid]) }}"
|
href="{{ route('project.service.index', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid, 'stack_service_uuid' => $database->uuid]) }}">
|
||||||
wire:navigate>
|
|
||||||
Settings
|
Settings
|
||||||
</a>
|
</a>
|
||||||
@if (str($database->status)->contains('running'))
|
@if (str($database->status)->contains('running'))
|
||||||
@@ -177,8 +171,8 @@
|
|||||||
<div class="pb-4">Persistent storage to preserve data between deployments.</div>
|
<div class="pb-4">Persistent storage to preserve data between deployments.</div>
|
||||||
<div class="pb-4 dark:text-warning text-coollabs">If you would like to add a volume, you must add it to
|
<div class="pb-4 dark:text-warning text-coollabs">If you would like to add a volume, you must add it to
|
||||||
your compose file (<a class="underline"
|
your compose file (<a class="underline"
|
||||||
href="{{ route('project.service.configuration', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid]) }}"
|
href="{{ route('project.service.configuration', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'service_uuid' => $service->uuid]) }}">General
|
||||||
wire:navigate>General tab</a>).</div>
|
tab</a>).</div>
|
||||||
@foreach ($applications as $application)
|
@foreach ($applications as $application)
|
||||||
<livewire:project.service.storage wire:key="application-{{ $application->id }}"
|
<livewire:project.service.storage wire:key="application-{{ $application->id }}"
|
||||||
:resource="$application" />
|
:resource="$application" />
|
||||||
|
|||||||
@@ -7,6 +7,11 @@
|
|||||||
<h2>{{ Str::headline($database->name) }}</h2>
|
<h2>{{ Str::headline($database->name) }}</h2>
|
||||||
@endif
|
@endif
|
||||||
<x-forms.button type="submit">Save</x-forms.button>
|
<x-forms.button type="submit">Save</x-forms.button>
|
||||||
|
<x-modal-confirmation wire:click="convertToApplication" title="Convert to Application"
|
||||||
|
buttonTitle="Convert to Application" submitAction="convertToApplication" :actions="['The selected resource will be converted to an application.']"
|
||||||
|
confirmationText="{{ Str::headline($database->name) }}"
|
||||||
|
confirmationLabel="Please confirm the execution of the actions by entering the Service Database Name below"
|
||||||
|
shortConfirmationLabel="Service Database Name" step3ButtonText="Permanently Delete" />
|
||||||
<x-modal-confirmation title="Confirm Service Database Deletion?" buttonTitle="Delete" isErrorButton
|
<x-modal-confirmation title="Confirm Service Database Deletion?" buttonTitle="Delete" isErrorButton
|
||||||
submitAction="delete" :actions="['The selected service database container will be stopped and permanently deleted.']" confirmationText="{{ Str::headline($database->name) }}"
|
submitAction="delete" :actions="['The selected service database container will be stopped and permanently deleted.']" confirmationText="{{ Str::headline($database->name) }}"
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Service Database Name below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Service Database Name below"
|
||||||
@@ -18,7 +23,7 @@
|
|||||||
<x-forms.input label="Description" id="database.description"></x-forms.input>
|
<x-forms.input label="Description" id="database.description"></x-forms.input>
|
||||||
<x-forms.input required
|
<x-forms.input required
|
||||||
helper="You can change the image you would like to deploy.<br><br><span class='dark:text-warning'>WARNING. You could corrupt your data. Only do it if you know what you are doing.</span>"
|
helper="You can change the image you would like to deploy.<br><br><span class='dark:text-warning'>WARNING. You could corrupt your data. Only do it if you know what you are doing.</span>"
|
||||||
label="Image Tag" id="database.image"></x-forms.input>
|
label="Image" id="database.image"></x-forms.input>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-end gap-2">
|
<div class="flex items-end gap-2">
|
||||||
<x-forms.input placeholder="5432" disabled="{{ $database->is_public }}" id="database.public_port"
|
<x-forms.input placeholder="5432" disabled="{{ $database->is_public }}" id="database.public_port"
|
||||||
|
|||||||
@@ -3,17 +3,16 @@
|
|||||||
<div class="flex flex-col h-full gap-8 pt-6 sm:flex-row">
|
<div class="flex flex-col h-full gap-8 pt-6 sm:flex-row">
|
||||||
<div class="flex flex-col items-start gap-2 min-w-fit">
|
<div class="flex flex-col items-start gap-2 min-w-fit">
|
||||||
<a class="menu-item"
|
<a class="menu-item"
|
||||||
class="{{ request()->routeIs('project.service.configuration') ? 'menu-item-active' : '' }}" wire:navigate
|
class="{{ request()->routeIs('project.service.configuration') ? 'menu-item-active' : '' }}"
|
||||||
href="{{ route('project.service.configuration', [...$parameters, 'stack_service_uuid' => null]) }}">
|
href="{{ route('project.service.configuration', [...$parameters, 'stack_service_uuid' => null]) }}">
|
||||||
<button><- Back</button>
|
<button><- Back</button>
|
||||||
</a>
|
</a>
|
||||||
<a class="menu-item" :class="activeTab === 'general' && 'menu-item-active'"
|
<a class="menu-item" :class="activeTab === 'general' && 'menu-item-active'"
|
||||||
@click.prevent="activeTab = 'general'; window.location.hash = 'general'; if(window.location.search) window.location.search = ''"
|
@click.prevent="activeTab = 'general'; window.location.hash = 'general'; if(window.location.search) window.location.search = ''"
|
||||||
wire:navigate href="#">General</a>
|
href="#">General</a>
|
||||||
@if ($serviceDatabase?->isBackupSolutionAvailable())
|
@if ($serviceDatabase?->isBackupSolutionAvailable() || $serviceDatabase?->is_migrated)
|
||||||
<a :class="activeTab === 'backups' && 'menu-item-active'" class="menu-item"
|
<a :class="activeTab === 'backups' && 'menu-item-active'" class="menu-item"
|
||||||
@click.prevent="activeTab = 'backups'; window.location.hash = 'backups'" wire:navigate
|
@click.prevent="activeTab = 'backups'; window.location.hash = 'backups'" href="#backups">Backups</a>
|
||||||
href="#backups">Backups</a>
|
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
@@ -34,18 +33,20 @@
|
|||||||
<div x-cloak x-show="activeTab === 'general'" class="h-full">
|
<div x-cloak x-show="activeTab === 'general'" class="h-full">
|
||||||
<livewire:project.service.database :database="$serviceDatabase" />
|
<livewire:project.service.database :database="$serviceDatabase" />
|
||||||
</div>
|
</div>
|
||||||
@if ($serviceDatabase->isBackupSolutionAvailable())
|
@if ($serviceDatabase?->isBackupSolutionAvailable() || $serviceDatabase?->is_migrated)
|
||||||
<div x-cloak x-show="activeTab === 'backups'">
|
<div x-cloak x-show="activeTab === 'backups'">
|
||||||
<div class="flex gap-2 ">
|
<div class="flex gap-2 ">
|
||||||
<h2 class="pb-4">Scheduled Backups</h2>
|
<h2 class="pb-4">Scheduled Backups</h2>
|
||||||
<x-modal-input buttonTitle="+ Add" title="New Scheduled Backup">
|
@if (filled($serviceDatabase->custom_type) || !$serviceDatabase->is_migrated)
|
||||||
<livewire:project.database.create-scheduled-backup :database="$serviceDatabase" />
|
<x-modal-input buttonTitle="+ Add" title="New Scheduled Backup">
|
||||||
</x-modal-input>
|
<livewire:project.database.create-scheduled-backup :database="$serviceDatabase" />
|
||||||
|
</x-modal-input>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<livewire:project.database.scheduled-backups :database="$serviceDatabase" />
|
<livewire:project.database.scheduled-backups :database="$serviceDatabase" />
|
||||||
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
@endisset
|
||||||
@endisset
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|||||||
@@ -10,11 +10,11 @@
|
|||||||
<x-resources.breadcrumbs :resource="$service" :parameters="$parameters" />
|
<x-resources.breadcrumbs :resource="$service" :parameters="$parameters" />
|
||||||
<div class="navbar-main" x-data">
|
<div class="navbar-main" x-data">
|
||||||
<nav class="flex flex-shrink-0 gap-6 items-center whitespace-nowrap scrollbar min-h-10">
|
<nav class="flex flex-shrink-0 gap-6 items-center whitespace-nowrap scrollbar min-h-10">
|
||||||
<a wire:navigate class="{{ request()->routeIs('project.service.configuration') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.service.configuration') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('project.service.configuration', $parameters) }}">
|
href="{{ route('project.service.configuration', $parameters) }}">
|
||||||
<button>Configuration</button>
|
<button>Configuration</button>
|
||||||
</a>
|
</a>
|
||||||
<a wire:navigate class="{{ request()->routeIs('project.service.logs') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.service.logs') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('project.service.logs', $parameters) }}">
|
href="{{ route('project.service.logs', $parameters) }}">
|
||||||
<button>Logs</button>
|
<button>Logs</button>
|
||||||
</a>
|
</a>
|
||||||
@@ -129,7 +129,7 @@
|
|||||||
<div class="flex flex-wrap order-first gap-2 items-center sm:order-last">
|
<div class="flex flex-wrap order-first gap-2 items-center sm:order-last">
|
||||||
<div class="text-error">
|
<div class="text-error">
|
||||||
Unable to deploy. <a class="underline font-bold cursor-pointer"
|
Unable to deploy. <a class="underline font-bold cursor-pointer"
|
||||||
href="{{ route('project.service.environment-variables', $parameters) }}" wire:navigate>
|
href="{{ route('project.service.environment-variables', $parameters) }}">
|
||||||
Required environment variables missing.</a>
|
Required environment variables missing.</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -138,7 +138,7 @@
|
|||||||
@script
|
@script
|
||||||
<script>
|
<script>
|
||||||
$wire.$on('stopEvent', () => {
|
$wire.$on('stopEvent', () => {
|
||||||
$wire.$dispatch('info', 'Stopping service.');
|
$wire.$dispatch('info', 'Gracefully stopping service, it could take a while depending on the service.');
|
||||||
$wire.$call('stop');
|
$wire.$call('stop');
|
||||||
});
|
});
|
||||||
$wire.$on('startEvent', async () => {
|
$wire.$on('startEvent', async () => {
|
||||||
|
|||||||
@@ -7,11 +7,15 @@
|
|||||||
<h2>{{ Str::headline($application->name) }}</h2>
|
<h2>{{ Str::headline($application->name) }}</h2>
|
||||||
@endif
|
@endif
|
||||||
<x-forms.button type="submit">Save</x-forms.button>
|
<x-forms.button type="submit">Save</x-forms.button>
|
||||||
<x-modal-confirmation title="Confirm Service Application Deletion?" buttonTitle="Delete" isErrorButton
|
<x-modal-confirmation wire:click="convertToDatabase" title="Convert to Database"
|
||||||
submitAction="delete" {{-- :checkboxes="$checkboxes" --}} :actions="['The selected service application container will be stopped and permanently deleted.']"
|
buttonTitle="Convert to Database" submitAction="convertToDatabase" :actions="['The selected resource will be converted to a service database.']"
|
||||||
confirmationText="{{ Str::headline($application->name) }}"
|
confirmationText="{{ Str::headline($application->name) }}"
|
||||||
confirmationLabel="Please confirm the execution of the actions by entering the Service Application Name below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Service Application Name below"
|
||||||
shortConfirmationLabel="Service Application Name" step3ButtonText="Permanently Delete" />
|
shortConfirmationLabel="Service Application Name" step3ButtonText="Permanently Delete" />
|
||||||
|
<x-modal-confirmation title="Confirm Service Application Deletion?" buttonTitle="Delete" isErrorButton
|
||||||
|
submitAction="delete" :actions="['The selected service application container will be stopped and permanently deleted.']" confirmationText="{{ Str::headline($application->name) }}"
|
||||||
|
confirmationLabel="Please confirm the execution of the actions by entering the Service Application Name below"
|
||||||
|
shortConfirmationLabel="Service Application Name" step3ButtonText="Permanently Delete" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
@@ -30,7 +34,7 @@
|
|||||||
helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io,https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. "></x-forms.input>
|
helper="You can specify one domain with path or more with comma. You can specify a port to bind the domain to.<br><br><span class='text-helper'>Example</span><br>- http://app.coolify.io,https://cloud.coolify.io/dashboard<br>- http://app.coolify.io/api/v3<br>- http://app.coolify.io:3000 -> app.coolify.io will point to port 3000 inside the container. "></x-forms.input>
|
||||||
@endif
|
@endif
|
||||||
@endif
|
@endif
|
||||||
<x-forms.input required
|
<x-forms.input
|
||||||
helper="You can change the image you would like to deploy.<br><br><span class='dark:text-warning'>WARNING. You could corrupt your data. Only do it if you know what you are doing.</span>"
|
helper="You can change the image you would like to deploy.<br><br><span class='dark:text-warning'>WARNING. You could corrupt your data. Only do it if you know what you are doing.</span>"
|
||||||
label="Image" id="application.image"></x-forms.input>
|
label="Image" id="application.image"></x-forms.input>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
@elseif(!$resource->destination->server->isMetricsEnabled())
|
@elseif(!$resource->destination->server->isMetricsEnabled())
|
||||||
<div class="alert alert-warning">Metrics are only available for servers with Sentinel & Metrics enabled!</div>
|
<div class="alert alert-warning">Metrics are only available for servers with Sentinel & Metrics enabled!</div>
|
||||||
<div> Go to <a class="underline dark:text-white"
|
<div> Go to <a class="underline dark:text-white"
|
||||||
wire:navigate href="{{ route('server.show', $resource->destination->server->uuid) }}">Server settings</a> to
|
href="{{ route('server.show', $resource->destination->server->uuid) }}">Server settings</a> to
|
||||||
enable
|
enable
|
||||||
it.</div>
|
it.</div>
|
||||||
@else
|
@else
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user