Compare commits
47 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
083668857d | ||
|
10ac40a295 | ||
|
9cf6925652 | ||
|
3aa9a8973b | ||
|
a04abdcca9 | ||
|
7238918f35 | ||
|
68c933b09e | ||
|
fdf832e9c7 | ||
|
e2a5d71134 | ||
|
5f87e99a61 | ||
|
2d6b84c737 | ||
|
6a97eaad12 | ||
|
24cac5feba | ||
|
f335adaaed | ||
|
3ea111478e | ||
|
633cd4ca7c | ||
|
0903cc537f | ||
|
bf2b4dfa44 | ||
|
714d059745 | ||
|
57c71fa451 | ||
|
deb8b3319d | ||
|
9b2d0e08e0 | ||
|
ae661990de | ||
|
239f9e9b31 | ||
|
c34013f7e2 | ||
|
eda724cf6c | ||
|
1c27ca0ce4 | ||
|
977c6c10f4 | ||
|
44a0d61d0e | ||
|
0a31199ecd | ||
|
fce392c7ed | ||
|
21598780a6 | ||
|
0154e9eff8 | ||
|
376ee8d3d7 | ||
|
e9fad4f1a1 | ||
|
8aba1f0795 | ||
|
6f6a839c8e | ||
|
ddcd7d21e6 | ||
|
1babe6d91b | ||
|
b60e437d66 | ||
|
e7448f5e6b | ||
|
eed96f4134 | ||
|
3ff76e16ca | ||
|
eb226f1a39 | ||
|
94004acdfe | ||
|
3e21cb29f6 | ||
|
a2a2a69eb8 |
32
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
32
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
@ -1,28 +1,17 @@
|
|||||||
name: Bug report
|
name: Bug report
|
||||||
description: "Create a new bug report."
|
description: Create a new bug report
|
||||||
title: "[Bug]: "
|
title: '[Bug]: '
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: >-
|
|
||||||
# 💎 Bounty program (with
|
|
||||||
[algora.io](https://console.algora.io/org/coollabsio/bounties/new))
|
|
||||||
|
|
||||||
|
|
||||||
If you would like to prioritize the issue resolution, you can add bounty
|
|
||||||
to this issue.
|
|
||||||
|
|
||||||
|
|
||||||
Click [here](https://console.algora.io/org/coollabsio/bounties/new) to
|
|
||||||
get started.
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Description
|
label: Description
|
||||||
description: A clear and concise description of the problem
|
description: A clear and concise description of the problem
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Minimal Reproduction (if possible, example repository)
|
label: Minimal Reproduction (if possible, example repository)
|
||||||
description: Please provide a step by step guide to reproduce the issue.
|
description: Please provide a step by step guide to reproduce the issue
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
@ -32,15 +21,6 @@ body:
|
|||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: Version
|
label: Version
|
||||||
description: Coolify's version (see top of your screen).
|
description: Coolify's version (see bottom left corner).
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Cloud?
|
|
||||||
description: "Are you using the cloud version of Coolify?"
|
|
||||||
options:
|
|
||||||
- label: 'Yes'
|
|
||||||
required: false
|
|
||||||
- label: 'No'
|
|
||||||
required: false
|
|
||||||
|
1
.github/pull_request_template.md
vendored
1
.github/pull_request_template.md
vendored
@ -1 +0,0 @@
|
|||||||
> Always use `next` branch as destination branch for PRs, not `main`
|
|
20
.github/workflows/coolify-helper-next.yml
vendored
20
.github/workflows/coolify-helper-next.yml
vendored
@ -18,15 +18,15 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v3
|
||||||
with:
|
with:
|
||||||
no-cache: true
|
no-cache: true
|
||||||
context: .
|
context: .
|
||||||
@ -40,15 +40,15 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v3
|
||||||
with:
|
with:
|
||||||
no-cache: true
|
no-cache: true
|
||||||
context: .
|
context: .
|
||||||
@ -64,13 +64,13 @@ jobs:
|
|||||||
needs: [ amd64, aarch64 ]
|
needs: [ amd64, aarch64 ]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v2
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v2
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
|
20
.github/workflows/coolify-helper.yml
vendored
20
.github/workflows/coolify-helper.yml
vendored
@ -18,15 +18,15 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v3
|
||||||
with:
|
with:
|
||||||
no-cache: true
|
no-cache: true
|
||||||
context: .
|
context: .
|
||||||
@ -40,15 +40,15 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v3
|
||||||
with:
|
with:
|
||||||
no-cache: true
|
no-cache: true
|
||||||
context: .
|
context: .
|
||||||
@ -64,13 +64,13 @@ jobs:
|
|||||||
needs: [ amd64, aarch64 ]
|
needs: [ amd64, aarch64 ]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v2
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v2
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
|
20
.github/workflows/coolify-testing-host.yml
vendored
20
.github/workflows/coolify-testing-host.yml
vendored
@ -18,15 +18,15 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v3
|
||||||
with:
|
with:
|
||||||
no-cache: true
|
no-cache: true
|
||||||
context: .
|
context: .
|
||||||
@ -40,15 +40,15 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build image and push to registry
|
- name: Build image and push to registry
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v3
|
||||||
with:
|
with:
|
||||||
no-cache: true
|
no-cache: true
|
||||||
context: .
|
context: .
|
||||||
@ -64,13 +64,13 @@ jobs:
|
|||||||
needs: [ amd64, aarch64 ]
|
needs: [ amd64, aarch64 ]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v2
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v2
|
||||||
- name: Login to ghcr.io
|
- name: Login to ghcr.io
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
|
4
.github/workflows/development-build.yml
vendored
4
.github/workflows/development-build.yml
vendored
@ -26,7 +26,7 @@ jobs:
|
|||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: docker/prod/Dockerfile
|
file: docker/prod-ssu/Dockerfile
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
|
||||||
@ -47,7 +47,7 @@ jobs:
|
|||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: docker/prod/Dockerfile
|
file: docker/prod-ssu/Dockerfile
|
||||||
platforms: linux/aarch64
|
platforms: linux/aarch64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}-aarch64
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}-aarch64
|
||||||
|
2
.github/workflows/docker-image.yml
vendored
2
.github/workflows/docker-image.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Cache Docker layers
|
- name: Cache Docker layers
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
|
25
.github/workflows/fix-php-code-style-issues.yml
vendored
25
.github/workflows/fix-php-code-style-issues.yml
vendored
@ -1,25 +0,0 @@
|
|||||||
name: Fix PHP code style issues
|
|
||||||
|
|
||||||
on: [push]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
php-code-styling:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 5
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
ref: ${{ github.head_ref }}
|
|
||||||
|
|
||||||
- name: Fix PHP code style issues
|
|
||||||
uses: aglipanci/laravel-pint-action@2.4
|
|
||||||
|
|
||||||
- name: Commit changes
|
|
||||||
uses: stefanzweifel/git-auto-commit-action@v5
|
|
||||||
with:
|
|
||||||
commit_message: Fix styling
|
|
6
.github/workflows/production-build.yml
vendored
6
.github/workflows/production-build.yml
vendored
@ -3,8 +3,6 @@ name: Production Build (v4)
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ["main"]
|
branches: ["main"]
|
||||||
paths-ignore:
|
|
||||||
- templates/service-templates.json
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: ghcr.io
|
REGISTRY: ghcr.io
|
||||||
@ -29,7 +27,7 @@ jobs:
|
|||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: docker/prod/Dockerfile
|
file: docker/prod-ssu/Dockerfile
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}
|
||||||
@ -51,7 +49,7 @@ jobs:
|
|||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: docker/prod/Dockerfile
|
file: docker/prod-ssu/Dockerfile
|
||||||
platforms: linux/aarch64
|
platforms: linux/aarch64
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.VERSION }}-aarch64
|
||||||
|
@ -3,7 +3,7 @@ tasks:
|
|||||||
# Fix because of https://github.com/gitpod-io/gitpod/issues/16614
|
# Fix because of https://github.com/gitpod-io/gitpod/issues/16614
|
||||||
before: sudo curl -o /usr/local/bin/docker-compose -fsSL https://github.com/docker/compose/releases/download/v2.16.0/docker-compose-linux-$(uname -m)
|
before: sudo curl -o /usr/local/bin/docker-compose -fsSL https://github.com/docker/compose/releases/download/v2.16.0/docker-compose-linux-$(uname -m)
|
||||||
init: |
|
init: |
|
||||||
cp .env.development.example .env &&
|
cp .env.example .env &&
|
||||||
sed -i "s#APP_URL=http://localhost#APP_URL=$(gp url 8000)#g" .env
|
sed -i "s#APP_URL=http://localhost#APP_URL=$(gp url 8000)#g" .env
|
||||||
sed -i "s#USERID=#USERID=33333#g" .env
|
sed -i "s#USERID=#USERID=33333#g" .env
|
||||||
sed -i "s#GROUPID=#GROUPID=33333#g" .env
|
sed -i "s#GROUPID=#GROUPID=33333#g" .env
|
||||||
@ -20,7 +20,7 @@ tasks:
|
|||||||
echo "Waiting for Sail environment to boot up."
|
echo "Waiting for Sail environment to boot up."
|
||||||
gp sync-await spin-is-ready
|
gp sync-await spin-is-ready
|
||||||
./vendor/bin/spin exec vite npm install
|
./vendor/bin/spin exec vite npm install
|
||||||
./vendor/bin/spin exec vite npm run dev -- --host
|
./vendor/bin/spin exec vite npm run dev
|
||||||
|
|
||||||
- name: Laravel Queue Worker, listening to code changes
|
- name: Laravel Queue Worker, listening to code changes
|
||||||
command: |
|
command: |
|
||||||
|
22
.tinkerwell/snippets/DeleteUser.php
Normal file
22
.tinkerwell/snippets/DeleteUser.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
$email = 'test@example.com';
|
||||||
|
$user = User::whereEmail($email)->first();
|
||||||
|
$teams = $user->teams;
|
||||||
|
foreach ($teams as $team) {
|
||||||
|
$servers = $team->servers;
|
||||||
|
if ($servers->count() > 0) {
|
||||||
|
foreach ($servers as $server) {
|
||||||
|
dump($server);
|
||||||
|
$server->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dump($team);
|
||||||
|
$team->delete();
|
||||||
|
}
|
||||||
|
if ($user) {
|
||||||
|
dump($user);
|
||||||
|
$user->delete();
|
||||||
|
}
|
@ -1,34 +0,0 @@
|
|||||||
# Contributing
|
|
||||||
|
|
||||||
> "First, thanks for considering to contribute to my project.
|
|
||||||
It really means a lot!" - [@andrasbacsai](https://github.com/andrasbacsai)
|
|
||||||
|
|
||||||
You can ask for guidance anytime on our
|
|
||||||
[Discord server](https://coollabs.io/discord) in the `#contribution` channel.
|
|
||||||
|
|
||||||
## Code Contribution
|
|
||||||
|
|
||||||
### 1) Setup your development environment
|
|
||||||
|
|
||||||
- You need to have Docker Engine (or equivalent) [installed](https://docs.docker.com/engine/install/) on your system.
|
|
||||||
- For better DX, install [Spin](https://serversideup.net/open-source/spin/).
|
|
||||||
|
|
||||||
### 2) Set your environment variables
|
|
||||||
|
|
||||||
- Copy [.env.development.example](./.env.development.example) to .env.
|
|
||||||
|
|
||||||
## 3) Start & setup Coolify
|
|
||||||
|
|
||||||
- Run `spin up` - You can notice that errors will be thrown. Don't worry.
|
|
||||||
- If you see weird permission errors, especially on Mac, run `sudo spin up` instead.
|
|
||||||
|
|
||||||
### 4) Start development
|
|
||||||
You can login your Coolify instance at `localhost:8000` with `test@example.com` and `password`.
|
|
||||||
|
|
||||||
Your horizon (Laravel scheduler): `localhost:8000/horizon` - Only reachable if you logged in with root user.
|
|
||||||
|
|
||||||
Mails are caught by Mailpit: `localhost:8025`
|
|
||||||
|
|
||||||
## New Service Contribution
|
|
||||||
Check out the docs [here](https://coolify.io/docs/knowledge-base/add-a-service).
|
|
||||||
|
|
16
SECURITY.md
16
SECURITY.md
@ -1,16 +0,0 @@
|
|||||||
# Security Policy
|
|
||||||
|
|
||||||
## Supported Versions
|
|
||||||
|
|
||||||
Use this section to tell people about which versions of your project are
|
|
||||||
currently being supported with security updates.
|
|
||||||
|
|
||||||
| Version | Supported |
|
|
||||||
| ------- | ------------------ |
|
|
||||||
| > 4 | :white_check_mark: |
|
|
||||||
| 3 | :x: |
|
|
||||||
|
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
|
||||||
|
|
||||||
If you have any vulnerability please report at hi@coollabs.io
|
|
@ -1,16 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Application;
|
|
||||||
|
|
||||||
use App\Models\Application;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
|
|
||||||
class LoadComposeFile
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
public function handle(Application $application)
|
|
||||||
{
|
|
||||||
$application->loadComposeFile();
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,17 +3,17 @@
|
|||||||
namespace App\Actions\Application;
|
namespace App\Actions\Application;
|
||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
|
use App\Models\StandaloneDocker;
|
||||||
|
use App\Notifications\Application\StatusChanged;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class StopApplication
|
class StopApplication
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
public function handle(Application $application)
|
||||||
public function handle(Application $application, bool $previewDeployments = false)
|
|
||||||
{
|
{
|
||||||
if ($application->destination->server->isSwarm()) {
|
if ($application->destination->server->isSwarm()) {
|
||||||
instant_remote_process(["docker stack rm {$application->uuid}"], $application->destination->server);
|
instant_remote_process(["docker stack rm {$application->uuid}"], $application->destination->server);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,15 +23,10 @@ class StopApplication
|
|||||||
$servers->push($server);
|
$servers->push($server);
|
||||||
});
|
});
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
if (! $server->isFunctional()) {
|
if (!$server->isFunctional()) {
|
||||||
return 'Server is not functional';
|
return 'Server is not functional';
|
||||||
}
|
}
|
||||||
if ($previewDeployments) {
|
$containers = getCurrentApplicationContainerStatus($server, $application->id, 0);
|
||||||
$containers = getCurrentApplicationContainerStatus($server, $application->id, includePullrequests: true);
|
|
||||||
} else {
|
|
||||||
$containers = getCurrentApplicationContainerStatus($server, $application->id, 0);
|
|
||||||
}
|
|
||||||
ray($containers);
|
|
||||||
if ($containers->count() > 0) {
|
if ($containers->count() > 0) {
|
||||||
foreach ($containers as $container) {
|
foreach ($containers as $container) {
|
||||||
$containerName = data_get($container, 'Names');
|
$containerName = data_get($container, 'Names');
|
||||||
@ -43,12 +38,6 @@ class StopApplication
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($application->build_pack === 'dockercompose') {
|
|
||||||
// remove network
|
|
||||||
$uuid = $application->uuid;
|
|
||||||
instant_remote_process(["docker network disconnect {$uuid} coolify-proxy"], $server, false);
|
|
||||||
instant_remote_process(["docker network rm {$uuid}"], $server, false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,13 +9,12 @@ use Lorisleiva\Actions\Concerns\AsAction;
|
|||||||
class StopApplicationOneServer
|
class StopApplicationOneServer
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Application $application, Server $server)
|
public function handle(Application $application, Server $server)
|
||||||
{
|
{
|
||||||
if ($application->destination->server->isSwarm()) {
|
if ($application->destination->server->isSwarm()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (! $server->isFunctional()) {
|
if (!$server->isFunctional()) {
|
||||||
return 'Server is not functional';
|
return 'Server is not functional';
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@ -33,7 +32,6 @@ class StopApplicationOneServer
|
|||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
|
|
||||||
return $e->getMessage();
|
return $e->getMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
namespace App\Actions\CoolifyTask;
|
namespace App\Actions\CoolifyTask;
|
||||||
|
|
||||||
use App\Data\CoolifyTaskArgs;
|
use App\Data\CoolifyTaskArgs;
|
||||||
use App\Enums\ActivityTypes;
|
|
||||||
use App\Jobs\CoolifyTask;
|
use App\Jobs\CoolifyTask;
|
||||||
use Spatie\Activitylog\Models\Activity;
|
use Spatie\Activitylog\Models\Activity;
|
||||||
|
|
||||||
@ -15,7 +14,6 @@ use Spatie\Activitylog\Models\Activity;
|
|||||||
class PrepareCoolifyTask
|
class PrepareCoolifyTask
|
||||||
{
|
{
|
||||||
protected Activity $activity;
|
protected Activity $activity;
|
||||||
|
|
||||||
protected CoolifyTaskArgs $remoteProcessArgs;
|
protected CoolifyTaskArgs $remoteProcessArgs;
|
||||||
|
|
||||||
public function __construct(CoolifyTaskArgs $remoteProcessArgs)
|
public function __construct(CoolifyTaskArgs $remoteProcessArgs)
|
||||||
@ -30,31 +28,20 @@ class PrepareCoolifyTask
|
|||||||
->withProperties($properties)
|
->withProperties($properties)
|
||||||
->performedOn($remoteProcessArgs->model)
|
->performedOn($remoteProcessArgs->model)
|
||||||
->event($remoteProcessArgs->type)
|
->event($remoteProcessArgs->type)
|
||||||
->log('[]');
|
->log("[]");
|
||||||
} else {
|
} else {
|
||||||
$this->activity = activity()
|
$this->activity = activity()
|
||||||
->withProperties($remoteProcessArgs->toArray())
|
->withProperties($remoteProcessArgs->toArray())
|
||||||
->event($remoteProcessArgs->type)
|
->event($remoteProcessArgs->type)
|
||||||
->log('[]');
|
->log("[]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __invoke(): Activity
|
public function __invoke(): Activity
|
||||||
{
|
{
|
||||||
$job = new CoolifyTask(
|
$job = new CoolifyTask($this->activity, ignore_errors: $this->remoteProcessArgs->ignore_errors, call_event_on_finish: $this->remoteProcessArgs->call_event_on_finish);
|
||||||
activity: $this->activity,
|
dispatch($job);
|
||||||
ignore_errors: $this->remoteProcessArgs->ignore_errors,
|
|
||||||
call_event_on_finish: $this->remoteProcessArgs->call_event_on_finish,
|
|
||||||
call_event_data: $this->remoteProcessArgs->call_event_data,
|
|
||||||
);
|
|
||||||
if ($this->remoteProcessArgs->type === ActivityTypes::COMMAND->value) {
|
|
||||||
ray('Dispatching a high priority job');
|
|
||||||
dispatch($job)->onQueue('high');
|
|
||||||
} else {
|
|
||||||
dispatch($job);
|
|
||||||
}
|
|
||||||
$this->activity->refresh();
|
$this->activity->refresh();
|
||||||
|
|
||||||
return $this->activity;
|
return $this->activity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,6 @@ class RunRemoteProcess
|
|||||||
|
|
||||||
public $call_event_on_finish = null;
|
public $call_event_on_finish = null;
|
||||||
|
|
||||||
public $call_event_data = null;
|
|
||||||
|
|
||||||
protected $time_start;
|
protected $time_start;
|
||||||
|
|
||||||
protected $current_time;
|
protected $current_time;
|
||||||
@ -36,10 +34,10 @@ class RunRemoteProcess
|
|||||||
/**
|
/**
|
||||||
* Create a new job instance.
|
* Create a new job instance.
|
||||||
*/
|
*/
|
||||||
public function __construct(Activity $activity, bool $hide_from_output = false, bool $ignore_errors = false, $call_event_on_finish = null, $call_event_data = null)
|
public function __construct(Activity $activity, bool $hide_from_output = false, bool $ignore_errors = false, $call_event_on_finish = null)
|
||||||
{
|
{
|
||||||
|
|
||||||
if ($activity->getExtraProperty('type') !== ActivityTypes::INLINE->value && $activity->getExtraProperty('type') !== ActivityTypes::COMMAND->value) {
|
if ($activity->getExtraProperty('type') !== ActivityTypes::INLINE->value) {
|
||||||
throw new \RuntimeException('Incompatible Activity to run a remote command.');
|
throw new \RuntimeException('Incompatible Activity to run a remote command.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +45,6 @@ class RunRemoteProcess
|
|||||||
$this->hide_from_output = $hide_from_output;
|
$this->hide_from_output = $hide_from_output;
|
||||||
$this->ignore_errors = $ignore_errors;
|
$this->ignore_errors = $ignore_errors;
|
||||||
$this->call_event_on_finish = $call_event_on_finish;
|
$this->call_event_on_finish = $call_event_on_finish;
|
||||||
$this->call_event_data = $call_event_data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function decodeOutput(?Activity $activity = null): string
|
public static function decodeOutput(?Activity $activity = null): string
|
||||||
@ -60,7 +57,7 @@ class RunRemoteProcess
|
|||||||
$decoded = json_decode(
|
$decoded = json_decode(
|
||||||
data_get($activity, 'description'),
|
data_get($activity, 'description'),
|
||||||
associative: true,
|
associative: true,
|
||||||
flags: JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE
|
flags: JSON_THROW_ON_ERROR
|
||||||
);
|
);
|
||||||
} catch (\JsonException $exception) {
|
} catch (\JsonException $exception) {
|
||||||
return '';
|
return '';
|
||||||
@ -69,7 +66,7 @@ class RunRemoteProcess
|
|||||||
return collect($decoded)
|
return collect($decoded)
|
||||||
->sortBy(fn ($i) => $i['order'])
|
->sortBy(fn ($i) => $i['order'])
|
||||||
->map(fn ($i) => $i['output'])
|
->map(fn ($i) => $i['output'])
|
||||||
->implode('');
|
->implode("");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __invoke(): ProcessResult
|
public function __invoke(): ProcessResult
|
||||||
@ -91,7 +88,7 @@ class RunRemoteProcess
|
|||||||
if ($processResult->exitCode() == 0) {
|
if ($processResult->exitCode() == 0) {
|
||||||
$status = ProcessStatus::FINISHED;
|
$status = ProcessStatus::FINISHED;
|
||||||
}
|
}
|
||||||
if ($processResult->exitCode() != 0 && ! $this->ignore_errors) {
|
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
||||||
$status = ProcessStatus::ERROR;
|
$status = ProcessStatus::ERROR;
|
||||||
}
|
}
|
||||||
// if (($processResult->exitCode() == 0 && $this->is_finished) || $this->activity->properties->get('status') === ProcessStatus::FINISHED->value) {
|
// if (($processResult->exitCode() == 0 && $this->is_finished) || $this->activity->properties->get('status') === ProcessStatus::FINISHED->value) {
|
||||||
@ -109,25 +106,18 @@ class RunRemoteProcess
|
|||||||
'status' => $status->value,
|
'status' => $status->value,
|
||||||
]);
|
]);
|
||||||
$this->activity->save();
|
$this->activity->save();
|
||||||
if ($processResult->exitCode() != 0 && ! $this->ignore_errors) {
|
if ($processResult->exitCode() != 0 && !$this->ignore_errors) {
|
||||||
throw new \RuntimeException($processResult->errorOutput(), $processResult->exitCode());
|
throw new \RuntimeException($processResult->errorOutput(), $processResult->exitCode());
|
||||||
}
|
}
|
||||||
if ($this->call_event_on_finish) {
|
if ($this->call_event_on_finish) {
|
||||||
try {
|
try {
|
||||||
if ($this->call_event_data) {
|
event(resolve("App\\Events\\$this->call_event_on_finish", [
|
||||||
event(resolve("App\\Events\\$this->call_event_on_finish", [
|
'userId' => $this->activity->causer_id,
|
||||||
'data' => $this->call_event_data,
|
]));
|
||||||
]));
|
|
||||||
} else {
|
|
||||||
event(resolve("App\\Events\\$this->call_event_on_finish", [
|
|
||||||
'userId' => $this->activity->causer_id,
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
ray($e);
|
ray($e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $processResult;
|
return $processResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,7 +155,8 @@ class RunRemoteProcess
|
|||||||
|
|
||||||
public function encodeOutput($type, $output)
|
public function encodeOutput($type, $output)
|
||||||
{
|
{
|
||||||
$outputStack = json_decode($this->activity->description, associative: true, flags: JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
|
$outputStack = json_decode($this->activity->description, associative: true, flags: JSON_THROW_ON_ERROR);
|
||||||
|
|
||||||
$outputStack[] = [
|
$outputStack[] = [
|
||||||
'type' => $type,
|
'type' => $type,
|
||||||
'output' => $output,
|
'output' => $output,
|
||||||
@ -174,16 +165,15 @@ class RunRemoteProcess
|
|||||||
'order' => $this->getLatestCounter(),
|
'order' => $this->getLatestCounter(),
|
||||||
];
|
];
|
||||||
|
|
||||||
return json_encode($outputStack, flags: JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
|
return json_encode($outputStack, flags: JSON_THROW_ON_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getLatestCounter(): int
|
protected function getLatestCounter(): int
|
||||||
{
|
{
|
||||||
$description = json_decode($this->activity->description, associative: true, flags: JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
|
$description = json_decode($this->activity->description, associative: true, flags: JSON_THROW_ON_ERROR);
|
||||||
if ($description === null || count($description) === 0) {
|
if ($description === null || count($description) === 0) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return end($description)['order'] + 1;
|
return end($description)['order'] + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Database;
|
|
||||||
|
|
||||||
use App\Models\StandaloneClickhouse;
|
|
||||||
use App\Models\StandaloneDragonfly;
|
|
||||||
use App\Models\StandaloneKeydb;
|
|
||||||
use App\Models\StandaloneMariadb;
|
|
||||||
use App\Models\StandaloneMongodb;
|
|
||||||
use App\Models\StandaloneMysql;
|
|
||||||
use App\Models\StandalonePostgresql;
|
|
||||||
use App\Models\StandaloneRedis;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
|
|
||||||
class RestartDatabase
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $database)
|
|
||||||
{
|
|
||||||
$server = $database->destination->server;
|
|
||||||
if (! $server->isFunctional()) {
|
|
||||||
return 'Server is not functional';
|
|
||||||
}
|
|
||||||
StopDatabase::run($database);
|
|
||||||
|
|
||||||
return StartDatabase::run($database);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,167 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Database;
|
|
||||||
|
|
||||||
use App\Models\StandaloneClickhouse;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
use Symfony\Component\Yaml\Yaml;
|
|
||||||
|
|
||||||
class StartClickhouse
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
public StandaloneClickhouse $database;
|
|
||||||
|
|
||||||
public array $commands = [];
|
|
||||||
|
|
||||||
public string $configuration_dir;
|
|
||||||
|
|
||||||
public function handle(StandaloneClickhouse $database)
|
|
||||||
{
|
|
||||||
$this->database = $database;
|
|
||||||
|
|
||||||
$container_name = $this->database->uuid;
|
|
||||||
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
|
||||||
|
|
||||||
$this->commands = [
|
|
||||||
"echo 'Starting {$database->name}.'",
|
|
||||||
"mkdir -p $this->configuration_dir",
|
|
||||||
];
|
|
||||||
|
|
||||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
|
||||||
$persistent_file_volumes = $this->database->fileStorages()->get();
|
|
||||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
|
||||||
$environment_variables = $this->generate_environment_variables();
|
|
||||||
|
|
||||||
$docker_compose = [
|
|
||||||
'services' => [
|
|
||||||
$container_name => [
|
|
||||||
'image' => $this->database->image,
|
|
||||||
'container_name' => $container_name,
|
|
||||||
'environment' => $environment_variables,
|
|
||||||
'restart' => RESTART_MODE,
|
|
||||||
'networks' => [
|
|
||||||
$this->database->destination->network,
|
|
||||||
],
|
|
||||||
'ulimits' => [
|
|
||||||
'nofile' => [
|
|
||||||
'soft' => 262144,
|
|
||||||
'hard' => 262144,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'labels' => [
|
|
||||||
'coolify.managed' => 'true',
|
|
||||||
],
|
|
||||||
'healthcheck' => [
|
|
||||||
'test' => "clickhouse-client --password {$this->database->clickhouse_admin_password} --query 'SELECT 1'",
|
|
||||||
'interval' => '5s',
|
|
||||||
'timeout' => '5s',
|
|
||||||
'retries' => 10,
|
|
||||||
'start_period' => '5s',
|
|
||||||
],
|
|
||||||
'mem_limit' => $this->database->limits_memory,
|
|
||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
|
||||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
|
||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
|
||||||
'cpus' => (float) $this->database->limits_cpus,
|
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'networks' => [
|
|
||||||
$this->database->destination->network => [
|
|
||||||
'external' => true,
|
|
||||||
'name' => $this->database->destination->network,
|
|
||||||
'attachable' => true,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
if (! is_null($this->database->limits_cpuset)) {
|
|
||||||
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
|
||||||
}
|
|
||||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
|
||||||
$docker_compose['services'][$container_name]['logging'] = [
|
|
||||||
'driver' => 'fluentd',
|
|
||||||
'options' => [
|
|
||||||
'fluentd-address' => 'tcp://127.0.0.1:24224',
|
|
||||||
'fluentd-async' => 'true',
|
|
||||||
'fluentd-sub-second-precision' => 'true',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
if (count($this->database->ports_mappings_array) > 0) {
|
|
||||||
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
|
||||||
}
|
|
||||||
if (count($persistent_storages) > 0) {
|
|
||||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
|
||||||
}
|
|
||||||
if (count($persistent_file_volumes) > 0) {
|
|
||||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
|
||||||
return "$item->fs_path:$item->mount_path";
|
|
||||||
})->toArray();
|
|
||||||
}
|
|
||||||
if (count($volume_names) > 0) {
|
|
||||||
$docker_compose['volumes'] = $volume_names;
|
|
||||||
}
|
|
||||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
|
||||||
$docker_compose_base64 = base64_encode($docker_compose);
|
|
||||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
|
||||||
$readme = generate_readme_file($this->database->name, now());
|
|
||||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
|
||||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
|
||||||
$this->commands[] = "echo 'Database started.'";
|
|
||||||
|
|
||||||
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function generate_local_persistent_volumes()
|
|
||||||
{
|
|
||||||
$local_persistent_volumes = [];
|
|
||||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
|
||||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
|
||||||
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
|
|
||||||
} else {
|
|
||||||
$volume_name = $persistentStorage->name;
|
|
||||||
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $local_persistent_volumes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function generate_local_persistent_volumes_only_volume_names()
|
|
||||||
{
|
|
||||||
$local_persistent_volumes_names = [];
|
|
||||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
|
||||||
if ($persistentStorage->host_path) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$name = $persistentStorage->name;
|
|
||||||
$local_persistent_volumes_names[$name] = [
|
|
||||||
'name' => $name,
|
|
||||||
'external' => false,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $local_persistent_volumes_names;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function generate_environment_variables()
|
|
||||||
{
|
|
||||||
$environment_variables = collect();
|
|
||||||
foreach ($this->database->runtime_environment_variables as $env) {
|
|
||||||
$environment_variables->push("$env->key=$env->real_value");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('CLICKHOUSE_ADMIN_USER'))->isEmpty()) {
|
|
||||||
$environment_variables->push("CLICKHOUSE_ADMIN_USER={$this->database->clickhouse_admin_user}");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('CLICKHOUSE_ADMIN_PASSWORD'))->isEmpty()) {
|
|
||||||
$environment_variables->push("CLICKHOUSE_ADMIN_PASSWORD={$this->database->clickhouse_admin_password}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return $environment_variables->all();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Database;
|
|
||||||
|
|
||||||
use App\Models\StandaloneClickhouse;
|
|
||||||
use App\Models\StandaloneDragonfly;
|
|
||||||
use App\Models\StandaloneKeydb;
|
|
||||||
use App\Models\StandaloneMariadb;
|
|
||||||
use App\Models\StandaloneMongodb;
|
|
||||||
use App\Models\StandaloneMysql;
|
|
||||||
use App\Models\StandalonePostgresql;
|
|
||||||
use App\Models\StandaloneRedis;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
|
|
||||||
class StartDatabase
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $database)
|
|
||||||
{
|
|
||||||
$server = $database->destination->server;
|
|
||||||
if (! $server->isFunctional()) {
|
|
||||||
return 'Server is not functional';
|
|
||||||
}
|
|
||||||
switch ($database->getMorphClass()) {
|
|
||||||
case 'App\Models\StandalonePostgresql':
|
|
||||||
$activity = StartPostgresql::run($database);
|
|
||||||
break;
|
|
||||||
case 'App\Models\StandaloneRedis':
|
|
||||||
$activity = StartRedis::run($database);
|
|
||||||
break;
|
|
||||||
case 'App\Models\StandaloneMongodb':
|
|
||||||
$activity = StartMongodb::run($database);
|
|
||||||
break;
|
|
||||||
case 'App\Models\StandaloneMysql':
|
|
||||||
$activity = StartMysql::run($database);
|
|
||||||
break;
|
|
||||||
case 'App\Models\StandaloneMariadb':
|
|
||||||
$activity = StartMariadb::run($database);
|
|
||||||
break;
|
|
||||||
case 'App\Models\StandaloneKeydb':
|
|
||||||
$activity = StartKeydb::run($database);
|
|
||||||
break;
|
|
||||||
case 'App\Models\StandaloneDragonfly':
|
|
||||||
$activity = StartDragonfly::run($database);
|
|
||||||
break;
|
|
||||||
case 'App\Models\StandaloneClickhouse':
|
|
||||||
$activity = StartClickhouse::run($database);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ($database->is_public && $database->public_port) {
|
|
||||||
StartDatabaseProxy::dispatch($database);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $activity;
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,9 +3,6 @@
|
|||||||
namespace App\Actions\Database;
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
use App\Models\ServiceDatabase;
|
use App\Models\ServiceDatabase;
|
||||||
use App\Models\StandaloneClickhouse;
|
|
||||||
use App\Models\StandaloneDragonfly;
|
|
||||||
use App\Models\StandaloneKeydb;
|
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
@ -18,7 +15,7 @@ class StartDatabaseProxy
|
|||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse|ServiceDatabase $database)
|
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database)
|
||||||
{
|
{
|
||||||
$internalPort = null;
|
$internalPort = null;
|
||||||
$type = $database->getMorphClass();
|
$type = $database->getMorphClass();
|
||||||
@ -28,8 +25,7 @@ class StartDatabaseProxy
|
|||||||
$proxyContainerName = "{$database->uuid}-proxy";
|
$proxyContainerName = "{$database->uuid}-proxy";
|
||||||
if ($database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
if ($database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||||
$databaseType = $database->databaseType();
|
$databaseType = $database->databaseType();
|
||||||
// $connectPredefined = data_get($database, 'service.connect_to_docker_network');
|
$network = data_get($database, 'service.destination.network');
|
||||||
$network = $database->service->uuid;
|
|
||||||
$server = data_get($database, 'service.destination.server');
|
$server = data_get($database, 'service.destination.server');
|
||||||
$proxyContainerName = "{$database->service->uuid}-proxy";
|
$proxyContainerName = "{$database->service->uuid}-proxy";
|
||||||
switch ($databaseType) {
|
switch ($databaseType) {
|
||||||
@ -53,36 +49,18 @@ class StartDatabaseProxy
|
|||||||
$type = 'App\Models\StandaloneRedis';
|
$type = 'App\Models\StandaloneRedis';
|
||||||
$containerName = "redis-{$database->service->uuid}";
|
$containerName = "redis-{$database->service->uuid}";
|
||||||
break;
|
break;
|
||||||
case 'standalone-keydb':
|
|
||||||
$type = 'App\Models\StandaloneKeydb';
|
|
||||||
$containerName = "keydb-{$database->service->uuid}";
|
|
||||||
break;
|
|
||||||
case 'standalone-dragonfly':
|
|
||||||
$type = 'App\Models\StandaloneDragonfly';
|
|
||||||
$containerName = "dragonfly-{$database->service->uuid}";
|
|
||||||
break;
|
|
||||||
case 'standalone-clickhouse':
|
|
||||||
$type = 'App\Models\StandaloneClickhouse';
|
|
||||||
$containerName = "clickhouse-{$database->service->uuid}";
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($type === 'App\Models\StandaloneRedis') {
|
if ($type === 'App\Models\StandaloneRedis') {
|
||||||
$internalPort = 6379;
|
$internalPort = 6379;
|
||||||
} elseif ($type === 'App\Models\StandalonePostgresql') {
|
} else if ($type === 'App\Models\StandalonePostgresql') {
|
||||||
$internalPort = 5432;
|
$internalPort = 5432;
|
||||||
} elseif ($type === 'App\Models\StandaloneMongodb') {
|
} else if ($type === 'App\Models\StandaloneMongodb') {
|
||||||
$internalPort = 27017;
|
$internalPort = 27017;
|
||||||
} elseif ($type === 'App\Models\StandaloneMysql') {
|
} else if ($type === 'App\Models\StandaloneMysql') {
|
||||||
$internalPort = 3306;
|
$internalPort = 3306;
|
||||||
} elseif ($type === 'App\Models\StandaloneMariadb') {
|
} else if ($type === 'App\Models\StandaloneMariadb') {
|
||||||
$internalPort = 3306;
|
$internalPort = 3306;
|
||||||
} elseif ($type === 'App\Models\StandaloneKeydb') {
|
|
||||||
$internalPort = 6379;
|
|
||||||
} elseif ($type === 'App\Models\StandaloneDragonfly') {
|
|
||||||
$internalPort = 6379;
|
|
||||||
} elseif ($type === 'App\Models\StandaloneClickhouse') {
|
|
||||||
$internalPort = 9000;
|
|
||||||
}
|
}
|
||||||
$configuration_dir = database_proxy_dir($database->uuid);
|
$configuration_dir = database_proxy_dir($database->uuid);
|
||||||
$nginxconf = <<<EOF
|
$nginxconf = <<<EOF
|
||||||
@ -101,19 +79,20 @@ class StartDatabaseProxy
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
EOF;
|
EOF;
|
||||||
$dockerfile = <<< 'EOF'
|
$dockerfile = <<< EOF
|
||||||
FROM nginx:stable-alpine
|
FROM nginx:stable-alpine
|
||||||
|
|
||||||
COPY nginx.conf /etc/nginx/nginx.conf
|
COPY nginx.conf /etc/nginx/nginx.conf
|
||||||
EOF;
|
EOF;
|
||||||
$docker_compose = [
|
$docker_compose = [
|
||||||
|
'version' => '3.8',
|
||||||
'services' => [
|
'services' => [
|
||||||
$proxyContainerName => [
|
$proxyContainerName => [
|
||||||
'build' => [
|
'build' => [
|
||||||
'context' => $configuration_dir,
|
'context' => $configuration_dir,
|
||||||
'dockerfile' => 'Dockerfile',
|
'dockerfile' => 'Dockerfile',
|
||||||
],
|
],
|
||||||
'image' => 'nginx:stable-alpine',
|
'image' => "nginx:stable-alpine",
|
||||||
'container_name' => $proxyContainerName,
|
'container_name' => $proxyContainerName,
|
||||||
'restart' => RESTART_MODE,
|
'restart' => RESTART_MODE,
|
||||||
'ports' => [
|
'ports' => [
|
||||||
@ -130,27 +109,26 @@ class StartDatabaseProxy
|
|||||||
'interval' => '5s',
|
'interval' => '5s',
|
||||||
'timeout' => '5s',
|
'timeout' => '5s',
|
||||||
'retries' => 3,
|
'retries' => 3,
|
||||||
'start_period' => '1s',
|
'start_period' => '1s'
|
||||||
],
|
],
|
||||||
],
|
]
|
||||||
],
|
],
|
||||||
'networks' => [
|
'networks' => [
|
||||||
$network => [
|
$network => [
|
||||||
'external' => true,
|
'external' => true,
|
||||||
'name' => $network,
|
'name' => $network,
|
||||||
'attachable' => true,
|
'attachable' => true,
|
||||||
],
|
]
|
||||||
],
|
]
|
||||||
];
|
];
|
||||||
$dockercompose_base64 = base64_encode(Yaml::dump($docker_compose, 4, 2));
|
$dockercompose_base64 = base64_encode(Yaml::dump($docker_compose, 4, 2));
|
||||||
$nginxconf_base64 = base64_encode($nginxconf);
|
$nginxconf_base64 = base64_encode($nginxconf);
|
||||||
$dockerfile_base64 = base64_encode($dockerfile);
|
$dockerfile_base64 = base64_encode($dockerfile);
|
||||||
instant_remote_process(["docker rm -f $proxyContainerName"], $server, false);
|
|
||||||
instant_remote_process([
|
instant_remote_process([
|
||||||
"mkdir -p $configuration_dir",
|
"mkdir -p $configuration_dir",
|
||||||
"echo '{$dockerfile_base64}' | base64 -d | tee $configuration_dir/Dockerfile > /dev/null",
|
"echo '{$dockerfile_base64}' | base64 -d > $configuration_dir/Dockerfile",
|
||||||
"echo '{$nginxconf_base64}' | base64 -d | tee $configuration_dir/nginx.conf > /dev/null",
|
"echo '{$nginxconf_base64}' | base64 -d > $configuration_dir/nginx.conf",
|
||||||
"echo '{$dockercompose_base64}' | base64 -d | tee $configuration_dir/docker-compose.yaml > /dev/null",
|
"echo '{$dockercompose_base64}' | base64 -d > $configuration_dir/docker-compose.yaml",
|
||||||
"docker compose --project-directory {$configuration_dir} pull",
|
"docker compose --project-directory {$configuration_dir} pull",
|
||||||
"docker compose --project-directory {$configuration_dir} up --build -d",
|
"docker compose --project-directory {$configuration_dir} up --build -d",
|
||||||
], $server);
|
], $server);
|
||||||
|
@ -1,163 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Database;
|
|
||||||
|
|
||||||
use App\Models\StandaloneDragonfly;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
use Symfony\Component\Yaml\Yaml;
|
|
||||||
|
|
||||||
class StartDragonfly
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
public StandaloneDragonfly $database;
|
|
||||||
|
|
||||||
public array $commands = [];
|
|
||||||
|
|
||||||
public string $configuration_dir;
|
|
||||||
|
|
||||||
public function handle(StandaloneDragonfly $database)
|
|
||||||
{
|
|
||||||
$this->database = $database;
|
|
||||||
|
|
||||||
$startCommand = "dragonfly --requirepass {$this->database->dragonfly_password}";
|
|
||||||
|
|
||||||
$container_name = $this->database->uuid;
|
|
||||||
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
|
||||||
|
|
||||||
$this->commands = [
|
|
||||||
"echo 'Starting {$database->name}.'",
|
|
||||||
"mkdir -p $this->configuration_dir",
|
|
||||||
];
|
|
||||||
|
|
||||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
|
||||||
$persistent_file_volumes = $this->database->fileStorages()->get();
|
|
||||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
|
||||||
$environment_variables = $this->generate_environment_variables();
|
|
||||||
|
|
||||||
$docker_compose = [
|
|
||||||
'services' => [
|
|
||||||
$container_name => [
|
|
||||||
'image' => $this->database->image,
|
|
||||||
'command' => $startCommand,
|
|
||||||
'container_name' => $container_name,
|
|
||||||
'environment' => $environment_variables,
|
|
||||||
'restart' => RESTART_MODE,
|
|
||||||
'networks' => [
|
|
||||||
$this->database->destination->network,
|
|
||||||
],
|
|
||||||
'ulimits' => [
|
|
||||||
'memlock' => '-1',
|
|
||||||
],
|
|
||||||
'labels' => [
|
|
||||||
'coolify.managed' => 'true',
|
|
||||||
],
|
|
||||||
'healthcheck' => [
|
|
||||||
'test' => "redis-cli -a {$this->database->dragonfly_password} ping",
|
|
||||||
'interval' => '5s',
|
|
||||||
'timeout' => '5s',
|
|
||||||
'retries' => 10,
|
|
||||||
'start_period' => '5s',
|
|
||||||
],
|
|
||||||
'mem_limit' => $this->database->limits_memory,
|
|
||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
|
||||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
|
||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
|
||||||
'cpus' => (float) $this->database->limits_cpus,
|
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'networks' => [
|
|
||||||
$this->database->destination->network => [
|
|
||||||
'external' => true,
|
|
||||||
'name' => $this->database->destination->network,
|
|
||||||
'attachable' => true,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
if (! is_null($this->database->limits_cpuset)) {
|
|
||||||
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
|
||||||
}
|
|
||||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
|
||||||
$docker_compose['services'][$container_name]['logging'] = [
|
|
||||||
'driver' => 'fluentd',
|
|
||||||
'options' => [
|
|
||||||
'fluentd-address' => 'tcp://127.0.0.1:24224',
|
|
||||||
'fluentd-async' => 'true',
|
|
||||||
'fluentd-sub-second-precision' => 'true',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
if (count($this->database->ports_mappings_array) > 0) {
|
|
||||||
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
|
||||||
}
|
|
||||||
if (count($persistent_storages) > 0) {
|
|
||||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
|
||||||
}
|
|
||||||
if (count($persistent_file_volumes) > 0) {
|
|
||||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
|
||||||
return "$item->fs_path:$item->mount_path";
|
|
||||||
})->toArray();
|
|
||||||
}
|
|
||||||
if (count($volume_names) > 0) {
|
|
||||||
$docker_compose['volumes'] = $volume_names;
|
|
||||||
}
|
|
||||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
|
||||||
$docker_compose_base64 = base64_encode($docker_compose);
|
|
||||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
|
||||||
$readme = generate_readme_file($this->database->name, now());
|
|
||||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
|
||||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
|
||||||
$this->commands[] = "echo 'Database started.'";
|
|
||||||
|
|
||||||
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function generate_local_persistent_volumes()
|
|
||||||
{
|
|
||||||
$local_persistent_volumes = [];
|
|
||||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
|
||||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
|
||||||
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
|
|
||||||
} else {
|
|
||||||
$volume_name = $persistentStorage->name;
|
|
||||||
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $local_persistent_volumes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function generate_local_persistent_volumes_only_volume_names()
|
|
||||||
{
|
|
||||||
$local_persistent_volumes_names = [];
|
|
||||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
|
||||||
if ($persistentStorage->host_path) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$name = $persistentStorage->name;
|
|
||||||
$local_persistent_volumes_names[$name] = [
|
|
||||||
'name' => $name,
|
|
||||||
'external' => false,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $local_persistent_volumes_names;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function generate_environment_variables()
|
|
||||||
{
|
|
||||||
$environment_variables = collect();
|
|
||||||
foreach ($this->database->runtime_environment_variables as $env) {
|
|
||||||
$environment_variables->push("$env->key=$env->real_value");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
|
||||||
$environment_variables->push("REDIS_PASSWORD={$this->database->dragonfly_password}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return $environment_variables->all();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,183 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Database;
|
|
||||||
|
|
||||||
use App\Models\StandaloneKeydb;
|
|
||||||
use Illuminate\Support\Facades\Storage;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
use Symfony\Component\Yaml\Yaml;
|
|
||||||
|
|
||||||
class StartKeydb
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
public StandaloneKeydb $database;
|
|
||||||
|
|
||||||
public array $commands = [];
|
|
||||||
|
|
||||||
public string $configuration_dir;
|
|
||||||
|
|
||||||
public function handle(StandaloneKeydb $database)
|
|
||||||
{
|
|
||||||
$this->database = $database;
|
|
||||||
|
|
||||||
$startCommand = "keydb-server --requirepass {$this->database->keydb_password} --appendonly yes";
|
|
||||||
|
|
||||||
$container_name = $this->database->uuid;
|
|
||||||
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
|
||||||
|
|
||||||
$this->commands = [
|
|
||||||
"echo 'Starting {$database->name}.'",
|
|
||||||
"mkdir -p $this->configuration_dir",
|
|
||||||
];
|
|
||||||
|
|
||||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
|
||||||
$persistent_file_volumes = $this->database->fileStorages()->get();
|
|
||||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
|
||||||
$environment_variables = $this->generate_environment_variables();
|
|
||||||
$this->add_custom_keydb();
|
|
||||||
|
|
||||||
$docker_compose = [
|
|
||||||
'services' => [
|
|
||||||
$container_name => [
|
|
||||||
'image' => $this->database->image,
|
|
||||||
'command' => $startCommand,
|
|
||||||
'container_name' => $container_name,
|
|
||||||
'environment' => $environment_variables,
|
|
||||||
'restart' => RESTART_MODE,
|
|
||||||
'networks' => [
|
|
||||||
$this->database->destination->network,
|
|
||||||
],
|
|
||||||
'labels' => [
|
|
||||||
'coolify.managed' => 'true',
|
|
||||||
],
|
|
||||||
'healthcheck' => [
|
|
||||||
'test' => "keydb-cli --pass {$this->database->keydb_password} ping",
|
|
||||||
'interval' => '5s',
|
|
||||||
'timeout' => '5s',
|
|
||||||
'retries' => 10,
|
|
||||||
'start_period' => '5s',
|
|
||||||
],
|
|
||||||
'mem_limit' => $this->database->limits_memory,
|
|
||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
|
||||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
|
||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
|
||||||
'cpus' => (float) $this->database->limits_cpus,
|
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
'networks' => [
|
|
||||||
$this->database->destination->network => [
|
|
||||||
'external' => true,
|
|
||||||
'name' => $this->database->destination->network,
|
|
||||||
'attachable' => true,
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
if (! is_null($this->database->limits_cpuset)) {
|
|
||||||
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
|
||||||
}
|
|
||||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
|
||||||
$docker_compose['services'][$container_name]['logging'] = [
|
|
||||||
'driver' => 'fluentd',
|
|
||||||
'options' => [
|
|
||||||
'fluentd-address' => 'tcp://127.0.0.1:24224',
|
|
||||||
'fluentd-async' => 'true',
|
|
||||||
'fluentd-sub-second-precision' => 'true',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
if (count($this->database->ports_mappings_array) > 0) {
|
|
||||||
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
|
||||||
}
|
|
||||||
if (count($persistent_storages) > 0) {
|
|
||||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
|
||||||
}
|
|
||||||
if (count($persistent_file_volumes) > 0) {
|
|
||||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
|
||||||
return "$item->fs_path:$item->mount_path";
|
|
||||||
})->toArray();
|
|
||||||
}
|
|
||||||
if (count($volume_names) > 0) {
|
|
||||||
$docker_compose['volumes'] = $volume_names;
|
|
||||||
}
|
|
||||||
if (! is_null($this->database->keydb_conf) || ! empty($this->database->keydb_conf)) {
|
|
||||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
|
||||||
'type' => 'bind',
|
|
||||||
'source' => $this->configuration_dir.'/keydb.conf',
|
|
||||||
'target' => '/etc/keydb/keydb.conf',
|
|
||||||
'read_only' => true,
|
|
||||||
];
|
|
||||||
$docker_compose['services'][$container_name]['command'] = "keydb-server /etc/keydb/keydb.conf --requirepass {$this->database->keydb_password} --appendonly yes";
|
|
||||||
}
|
|
||||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
|
||||||
$docker_compose_base64 = base64_encode($docker_compose);
|
|
||||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
|
||||||
$readme = generate_readme_file($this->database->name, now());
|
|
||||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
|
||||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
|
||||||
$this->commands[] = "echo 'Database started.'";
|
|
||||||
|
|
||||||
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function generate_local_persistent_volumes()
|
|
||||||
{
|
|
||||||
$local_persistent_volumes = [];
|
|
||||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
|
||||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
|
||||||
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
|
|
||||||
} else {
|
|
||||||
$volume_name = $persistentStorage->name;
|
|
||||||
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $local_persistent_volumes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function generate_local_persistent_volumes_only_volume_names()
|
|
||||||
{
|
|
||||||
$local_persistent_volumes_names = [];
|
|
||||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
|
||||||
if ($persistentStorage->host_path) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$name = $persistentStorage->name;
|
|
||||||
$local_persistent_volumes_names[$name] = [
|
|
||||||
'name' => $name,
|
|
||||||
'external' => false,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $local_persistent_volumes_names;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function generate_environment_variables()
|
|
||||||
{
|
|
||||||
$environment_variables = collect();
|
|
||||||
foreach ($this->database->runtime_environment_variables as $env) {
|
|
||||||
$environment_variables->push("$env->key=$env->real_value");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
|
||||||
$environment_variables->push("REDIS_PASSWORD={$this->database->keydb_password}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return $environment_variables->all();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function add_custom_keydb()
|
|
||||||
{
|
|
||||||
if (is_null($this->database->keydb_conf) || empty($this->database->keydb_conf)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$filename = 'keydb.conf';
|
|
||||||
Storage::disk('local')->put("tmp/keydb.conf_{$this->database->uuid}", $this->database->keydb_conf);
|
|
||||||
$path = Storage::path("tmp/keydb.conf_{$this->database->uuid}");
|
|
||||||
instant_scp($path, "{$this->configuration_dir}/{$filename}", $this->database->destination->server);
|
|
||||||
Storage::disk('local')->delete("tmp/keydb.conf_{$this->database->uuid}");
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,17 +3,16 @@
|
|||||||
namespace App\Actions\Database;
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Illuminate\Support\Str;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class StartMariadb
|
class StartMariadb
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public StandaloneMariadb $database;
|
public StandaloneMariadb $database;
|
||||||
|
|
||||||
public array $commands = [];
|
public array $commands = [];
|
||||||
|
|
||||||
public string $configuration_dir;
|
public string $configuration_dir;
|
||||||
|
|
||||||
public function handle(StandaloneMariadb $database)
|
public function handle(StandaloneMariadb $database)
|
||||||
@ -21,7 +20,7 @@ class StartMariadb
|
|||||||
$this->database = $database;
|
$this->database = $database;
|
||||||
|
|
||||||
$container_name = $this->database->uuid;
|
$container_name = $this->database->uuid;
|
||||||
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||||
|
|
||||||
$this->commands = [
|
$this->commands = [
|
||||||
"echo 'Starting {$database->name}.'",
|
"echo 'Starting {$database->name}.'",
|
||||||
@ -29,11 +28,11 @@ class StartMariadb
|
|||||||
];
|
];
|
||||||
|
|
||||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||||
$persistent_file_volumes = $this->database->fileStorages()->get();
|
|
||||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
$environment_variables = $this->generate_environment_variables();
|
$environment_variables = $this->generate_environment_variables();
|
||||||
$this->add_custom_mysql();
|
$this->add_custom_mysql();
|
||||||
$docker_compose = [
|
$docker_compose = [
|
||||||
|
'version' => '3.8',
|
||||||
'services' => [
|
'services' => [
|
||||||
$container_name => [
|
$container_name => [
|
||||||
'image' => $this->database->image,
|
'image' => $this->database->image,
|
||||||
@ -47,11 +46,11 @@ class StartMariadb
|
|||||||
'coolify.managed' => 'true',
|
'coolify.managed' => 'true',
|
||||||
],
|
],
|
||||||
'healthcheck' => [
|
'healthcheck' => [
|
||||||
'test' => ['CMD', 'healthcheck.sh', '--connect', '--innodb_initialized'],
|
'test' => ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"],
|
||||||
'interval' => '5s',
|
'interval' => '5s',
|
||||||
'timeout' => '5s',
|
'timeout' => '5s',
|
||||||
'retries' => 10,
|
'retries' => 10,
|
||||||
'start_period' => '5s',
|
'start_period' => '5s'
|
||||||
],
|
],
|
||||||
'mem_limit' => $this->database->limits_memory,
|
'mem_limit' => $this->database->limits_memory,
|
||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
@ -59,27 +58,27 @@ class StartMariadb
|
|||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => (float) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
],
|
]
|
||||||
],
|
],
|
||||||
'networks' => [
|
'networks' => [
|
||||||
$this->database->destination->network => [
|
$this->database->destination->network => [
|
||||||
'external' => true,
|
'external' => true,
|
||||||
'name' => $this->database->destination->network,
|
'name' => $this->database->destination->network,
|
||||||
'attachable' => true,
|
'attachable' => true,
|
||||||
],
|
]
|
||||||
],
|
]
|
||||||
];
|
];
|
||||||
if (! is_null($this->database->limits_cpuset)) {
|
if (!is_null($this->database->limits_cpuset)) {
|
||||||
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||||
}
|
}
|
||||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
$docker_compose['services'][$container_name]['logging'] = [
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
'driver' => 'fluentd',
|
'driver' => 'fluentd',
|
||||||
'options' => [
|
'options' => [
|
||||||
'fluentd-address' => 'tcp://127.0.0.1:24224',
|
'fluentd-address' => "tcp://127.0.0.1:24224",
|
||||||
'fluentd-async' => 'true',
|
'fluentd-async' => "true",
|
||||||
'fluentd-sub-second-precision' => 'true',
|
'fluentd-sub-second-precision' => "true",
|
||||||
],
|
]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if (count($this->database->ports_mappings_array) > 0) {
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
@ -88,32 +87,26 @@ class StartMariadb
|
|||||||
if (count($persistent_storages) > 0) {
|
if (count($persistent_storages) > 0) {
|
||||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||||
}
|
}
|
||||||
if (count($persistent_file_volumes) > 0) {
|
|
||||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
|
||||||
return "$item->fs_path:$item->mount_path";
|
|
||||||
})->toArray();
|
|
||||||
}
|
|
||||||
if (count($volume_names) > 0) {
|
if (count($volume_names) > 0) {
|
||||||
$docker_compose['volumes'] = $volume_names;
|
$docker_compose['volumes'] = $volume_names;
|
||||||
}
|
}
|
||||||
if (! is_null($this->database->mariadb_conf) || ! empty($this->database->mariadb_conf)) {
|
if (!is_null($this->database->mariadb_conf)) {
|
||||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||||
'type' => 'bind',
|
'type' => 'bind',
|
||||||
'source' => $this->configuration_dir.'/custom-config.cnf',
|
'source' => $this->configuration_dir . '/custom-config.cnf',
|
||||||
'target' => '/etc/mysql/conf.d/custom-config.cnf',
|
'target' => '/etc/mysql/conf.d/custom-config.cnf',
|
||||||
'read_only' => true,
|
'read_only' => true,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
$docker_compose_base64 = base64_encode($docker_compose);
|
$docker_compose_base64 = base64_encode($docker_compose);
|
||||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||||
$readme = generate_readme_file($this->database->name, now());
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
$this->commands[] = "echo 'Database started.'";
|
$this->commands[] = "echo 'Database started.'";
|
||||||
|
|
||||||
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,14 +114,9 @@ class StartMariadb
|
|||||||
{
|
{
|
||||||
$local_persistent_volumes = [];
|
$local_persistent_volumes = [];
|
||||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
||||||
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
|
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||||
} else {
|
|
||||||
$volume_name = $persistentStorage->name;
|
|
||||||
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $local_persistent_volumes;
|
return $local_persistent_volumes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +133,6 @@ class StartMariadb
|
|||||||
'external' => false,
|
'external' => false,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $local_persistent_volumes_names;
|
return $local_persistent_volumes_names;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,32 +143,30 @@ class StartMariadb
|
|||||||
$environment_variables->push("$env->key=$env->real_value");
|
$environment_variables->push("$env->key=$env->real_value");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('MARIADB_ROOT_PASSWORD'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_ROOT_PASSWORD'))->isEmpty()) {
|
||||||
$environment_variables->push("MARIADB_ROOT_PASSWORD={$this->database->mariadb_root_password}");
|
$environment_variables->push("MARIADB_ROOT_PASSWORD={$this->database->mariadb_root_password}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('MARIADB_DATABASE'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_DATABASE'))->isEmpty()) {
|
||||||
$environment_variables->push("MARIADB_DATABASE={$this->database->mariadb_database}");
|
$environment_variables->push("MARIADB_DATABASE={$this->database->mariadb_database}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('MARIADB_USER'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_USER'))->isEmpty()) {
|
||||||
$environment_variables->push("MARIADB_USER={$this->database->mariadb_user}");
|
$environment_variables->push("MARIADB_USER={$this->database->mariadb_user}");
|
||||||
}
|
}
|
||||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('MARIADB_PASSWORD'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_PASSWORD'))->isEmpty()) {
|
||||||
$environment_variables->push("MARIADB_PASSWORD={$this->database->mariadb_password}");
|
$environment_variables->push("MARIADB_PASSWORD={$this->database->mariadb_password}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return $environment_variables->all();
|
return $environment_variables->all();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function add_custom_mysql()
|
private function add_custom_mysql()
|
||||||
{
|
{
|
||||||
if (is_null($this->database->mariadb_conf) || empty($this->database->mariadb_conf)) {
|
if (is_null($this->database->mariadb_conf)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$filename = 'custom-config.cnf';
|
$filename = 'custom-config.cnf';
|
||||||
$content = $this->database->mariadb_conf;
|
$content = $this->database->mariadb_conf;
|
||||||
$content_base64 = base64_encode($content);
|
$content_base64 = base64_encode($content);
|
||||||
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
|
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,27 +3,26 @@
|
|||||||
namespace App\Actions\Database;
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Illuminate\Support\Str;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class StartMongodb
|
class StartMongodb
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public StandaloneMongodb $database;
|
public StandaloneMongodb $database;
|
||||||
|
|
||||||
public array $commands = [];
|
public array $commands = [];
|
||||||
|
|
||||||
public string $configuration_dir;
|
public string $configuration_dir;
|
||||||
|
|
||||||
public function handle(StandaloneMongodb $database)
|
public function handle(StandaloneMongodb $database)
|
||||||
{
|
{
|
||||||
$this->database = $database;
|
$this->database = $database;
|
||||||
|
|
||||||
$startCommand = 'mongod';
|
$startCommand = "mongod";
|
||||||
|
|
||||||
$container_name = $this->database->uuid;
|
$container_name = $this->database->uuid;
|
||||||
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||||
|
|
||||||
$this->commands = [
|
$this->commands = [
|
||||||
"echo 'Starting {$database->name}.'",
|
"echo 'Starting {$database->name}.'",
|
||||||
@ -31,12 +30,12 @@ class StartMongodb
|
|||||||
];
|
];
|
||||||
|
|
||||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||||
$persistent_file_volumes = $this->database->fileStorages()->get();
|
|
||||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
$environment_variables = $this->generate_environment_variables();
|
$environment_variables = $this->generate_environment_variables();
|
||||||
$this->add_custom_mongo_conf();
|
$this->add_custom_mongo_conf();
|
||||||
|
|
||||||
$docker_compose = [
|
$docker_compose = [
|
||||||
|
'version' => '3.8',
|
||||||
'services' => [
|
'services' => [
|
||||||
$container_name => [
|
$container_name => [
|
||||||
'image' => $this->database->image,
|
'image' => $this->database->image,
|
||||||
@ -52,14 +51,13 @@ class StartMongodb
|
|||||||
],
|
],
|
||||||
'healthcheck' => [
|
'healthcheck' => [
|
||||||
'test' => [
|
'test' => [
|
||||||
'CMD',
|
'CMD-SHELL',
|
||||||
'echo',
|
'mongosh --eval "printjson(db.runCommand(\"ping\"))"'
|
||||||
'ok',
|
|
||||||
],
|
],
|
||||||
'interval' => '5s',
|
'interval' => '5s',
|
||||||
'timeout' => '5s',
|
'timeout' => '5s',
|
||||||
'retries' => 10,
|
'retries' => 10,
|
||||||
'start_period' => '5s',
|
'start_period' => '5s'
|
||||||
],
|
],
|
||||||
'mem_limit' => $this->database->limits_memory,
|
'mem_limit' => $this->database->limits_memory,
|
||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
@ -67,27 +65,27 @@ class StartMongodb
|
|||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => (float) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
],
|
]
|
||||||
],
|
],
|
||||||
'networks' => [
|
'networks' => [
|
||||||
$this->database->destination->network => [
|
$this->database->destination->network => [
|
||||||
'external' => true,
|
'external' => true,
|
||||||
'name' => $this->database->destination->network,
|
'name' => $this->database->destination->network,
|
||||||
'attachable' => true,
|
'attachable' => true,
|
||||||
],
|
]
|
||||||
],
|
]
|
||||||
];
|
];
|
||||||
if (! is_null($this->database->limits_cpuset)) {
|
if (!is_null($this->database->limits_cpuset)) {
|
||||||
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||||
}
|
}
|
||||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
$docker_compose['services'][$container_name]['logging'] = [
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
'driver' => 'fluentd',
|
'driver' => 'fluentd',
|
||||||
'options' => [
|
'options' => [
|
||||||
'fluentd-address' => 'tcp://127.0.0.1:24224',
|
'fluentd-address' => "tcp://127.0.0.1:24224",
|
||||||
'fluentd-async' => 'true',
|
'fluentd-async' => "true",
|
||||||
'fluentd-sub-second-precision' => 'true',
|
'fluentd-sub-second-precision' => "true",
|
||||||
],
|
]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if (count($this->database->ports_mappings_array) > 0) {
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
@ -96,41 +94,35 @@ class StartMongodb
|
|||||||
if (count($persistent_storages) > 0) {
|
if (count($persistent_storages) > 0) {
|
||||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||||
}
|
}
|
||||||
if (count($persistent_file_volumes) > 0) {
|
|
||||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
|
||||||
return "$item->fs_path:$item->mount_path";
|
|
||||||
})->toArray();
|
|
||||||
}
|
|
||||||
if (count($volume_names) > 0) {
|
if (count($volume_names) > 0) {
|
||||||
$docker_compose['volumes'] = $volume_names;
|
$docker_compose['volumes'] = $volume_names;
|
||||||
}
|
}
|
||||||
if (! is_null($this->database->mongo_conf) || ! empty($this->database->mongo_conf)) {
|
if (!is_null($this->database->mongo_conf)) {
|
||||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||||
'type' => 'bind',
|
'type' => 'bind',
|
||||||
'source' => $this->configuration_dir.'/mongod.conf',
|
'source' => $this->configuration_dir . '/mongod.conf',
|
||||||
'target' => '/etc/mongo/mongod.conf',
|
'target' => '/etc/mongo/mongod.conf',
|
||||||
'read_only' => true,
|
'read_only' => true,
|
||||||
];
|
];
|
||||||
$docker_compose['services'][$container_name]['command'] = $startCommand.' --config /etc/mongo/mongod.conf';
|
$docker_compose['services'][$container_name]['command'] = $startCommand . ' --config /etc/mongo/mongod.conf';
|
||||||
}
|
}
|
||||||
$this->add_default_database();
|
$this->add_default_database();
|
||||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||||
'type' => 'bind',
|
'type' => 'bind',
|
||||||
'source' => $this->configuration_dir.'/docker-entrypoint-initdb.d',
|
'source' => $this->configuration_dir . '/docker-entrypoint-initdb.d',
|
||||||
'target' => '/docker-entrypoint-initdb.d',
|
'target' => '/docker-entrypoint-initdb.d',
|
||||||
'read_only' => true,
|
'read_only' => true,
|
||||||
];
|
];
|
||||||
|
|
||||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
$docker_compose_base64 = base64_encode($docker_compose);
|
$docker_compose_base64 = base64_encode($docker_compose);
|
||||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||||
$readme = generate_readme_file($this->database->name, now());
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
$this->commands[] = "echo 'Database started.'";
|
$this->commands[] = "echo 'Database started.'";
|
||||||
|
|
||||||
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,14 +130,9 @@ class StartMongodb
|
|||||||
{
|
{
|
||||||
$local_persistent_volumes = [];
|
$local_persistent_volumes = [];
|
||||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
||||||
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
|
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||||
} else {
|
|
||||||
$volume_name = $persistentStorage->name;
|
|
||||||
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $local_persistent_volumes;
|
return $local_persistent_volumes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +149,6 @@ class StartMongodb
|
|||||||
'external' => false,
|
'external' => false,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $local_persistent_volumes_names;
|
return $local_persistent_volumes_names;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,37 +159,34 @@ class StartMongodb
|
|||||||
$environment_variables->push("$env->key=$env->real_value");
|
$environment_variables->push("$env->key=$env->real_value");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('MONGO_INITDB_ROOT_USERNAME'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MONGO_INITDB_ROOT_USERNAME'))->isEmpty()) {
|
||||||
$environment_variables->push("MONGO_INITDB_ROOT_USERNAME={$this->database->mongo_initdb_root_username}");
|
$environment_variables->push("MONGO_INITDB_ROOT_USERNAME={$this->database->mongo_initdb_root_username}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('MONGO_INITDB_ROOT_PASSWORD'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MONGO_INITDB_ROOT_PASSWORD'))->isEmpty()) {
|
||||||
$environment_variables->push("MONGO_INITDB_ROOT_PASSWORD={$this->database->mongo_initdb_root_password}");
|
$environment_variables->push("MONGO_INITDB_ROOT_PASSWORD={$this->database->mongo_initdb_root_password}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('MONGO_INITDB_DATABASE'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MONGO_INITDB_DATABASE'))->isEmpty()) {
|
||||||
$environment_variables->push("MONGO_INITDB_DATABASE={$this->database->mongo_initdb_database}");
|
$environment_variables->push("MONGO_INITDB_DATABASE={$this->database->mongo_initdb_database}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return $environment_variables->all();
|
return $environment_variables->all();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function add_custom_mongo_conf()
|
private function add_custom_mongo_conf()
|
||||||
{
|
{
|
||||||
if (is_null($this->database->mongo_conf) || empty($this->database->mongo_conf)) {
|
if (is_null($this->database->mongo_conf)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$filename = 'mongod.conf';
|
$filename = 'mongod.conf';
|
||||||
$content = $this->database->mongo_conf;
|
$content = $this->database->mongo_conf;
|
||||||
$content_base64 = base64_encode($content);
|
$content_base64 = base64_encode($content);
|
||||||
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
|
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private function add_default_database()
|
private function add_default_database()
|
||||||
{
|
{
|
||||||
$content = "db = db.getSiblingDB(\"{$this->database->mongo_initdb_database}\");db.createCollection('init_collection');db.createUser({user: \"{$this->database->mongo_initdb_root_username}\", pwd: \"{$this->database->mongo_initdb_root_password}\",roles: [{role:\"readWrite\",db:\"{$this->database->mongo_initdb_database}\"}]});";
|
$content = "db = db.getSiblingDB(\"{$this->database->mongo_initdb_database}\");db.createCollection('init_collection');db.createUser({user: \"{$this->database->mongo_initdb_root_username}\", pwd: \"{$this->database->mongo_initdb_root_password}\",roles: [{role:\"readWrite\",db:\"{$this->database->mongo_initdb_database}\"}]});";
|
||||||
$content_base64 = base64_encode($content);
|
$content_base64 = base64_encode($content);
|
||||||
$this->commands[] = "mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d";
|
$this->commands[] = "mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d";
|
||||||
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/docker-entrypoint-initdb.d/01-default-database.js > /dev/null";
|
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/docker-entrypoint-initdb.d/01-default-database.js";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,17 +3,16 @@
|
|||||||
namespace App\Actions\Database;
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Illuminate\Support\Str;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class StartMysql
|
class StartMysql
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public StandaloneMysql $database;
|
public StandaloneMysql $database;
|
||||||
|
|
||||||
public array $commands = [];
|
public array $commands = [];
|
||||||
|
|
||||||
public string $configuration_dir;
|
public string $configuration_dir;
|
||||||
|
|
||||||
public function handle(StandaloneMysql $database)
|
public function handle(StandaloneMysql $database)
|
||||||
@ -21,7 +20,7 @@ class StartMysql
|
|||||||
$this->database = $database;
|
$this->database = $database;
|
||||||
|
|
||||||
$container_name = $this->database->uuid;
|
$container_name = $this->database->uuid;
|
||||||
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||||
|
|
||||||
$this->commands = [
|
$this->commands = [
|
||||||
"echo 'Starting {$database->name}.'",
|
"echo 'Starting {$database->name}.'",
|
||||||
@ -29,11 +28,11 @@ class StartMysql
|
|||||||
];
|
];
|
||||||
|
|
||||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||||
$persistent_file_volumes = $this->database->fileStorages()->get();
|
|
||||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
$environment_variables = $this->generate_environment_variables();
|
$environment_variables = $this->generate_environment_variables();
|
||||||
$this->add_custom_mysql();
|
$this->add_custom_mysql();
|
||||||
$docker_compose = [
|
$docker_compose = [
|
||||||
|
'version' => '3.8',
|
||||||
'services' => [
|
'services' => [
|
||||||
$container_name => [
|
$container_name => [
|
||||||
'image' => $this->database->image,
|
'image' => $this->database->image,
|
||||||
@ -47,11 +46,11 @@ class StartMysql
|
|||||||
'coolify.managed' => 'true',
|
'coolify.managed' => 'true',
|
||||||
],
|
],
|
||||||
'healthcheck' => [
|
'healthcheck' => [
|
||||||
'test' => ['CMD', 'mysqladmin', 'ping', '-h', 'localhost', '-u', 'root', "-p{$this->database->mysql_root_password}"],
|
'test' => ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p{$this->database->mysql_root_password}"],
|
||||||
'interval' => '5s',
|
'interval' => '5s',
|
||||||
'timeout' => '5s',
|
'timeout' => '5s',
|
||||||
'retries' => 10,
|
'retries' => 10,
|
||||||
'start_period' => '5s',
|
'start_period' => '5s'
|
||||||
],
|
],
|
||||||
'mem_limit' => $this->database->limits_memory,
|
'mem_limit' => $this->database->limits_memory,
|
||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
@ -59,27 +58,27 @@ class StartMysql
|
|||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => (float) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
],
|
]
|
||||||
],
|
],
|
||||||
'networks' => [
|
'networks' => [
|
||||||
$this->database->destination->network => [
|
$this->database->destination->network => [
|
||||||
'external' => true,
|
'external' => true,
|
||||||
'name' => $this->database->destination->network,
|
'name' => $this->database->destination->network,
|
||||||
'attachable' => true,
|
'attachable' => true,
|
||||||
],
|
]
|
||||||
],
|
]
|
||||||
];
|
];
|
||||||
if (! is_null($this->database->limits_cpuset)) {
|
if (!is_null($this->database->limits_cpuset)) {
|
||||||
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||||
}
|
}
|
||||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
$docker_compose['services'][$container_name]['logging'] = [
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
'driver' => 'fluentd',
|
'driver' => 'fluentd',
|
||||||
'options' => [
|
'options' => [
|
||||||
'fluentd-address' => 'tcp://127.0.0.1:24224',
|
'fluentd-address' => "tcp://127.0.0.1:24224",
|
||||||
'fluentd-async' => 'true',
|
'fluentd-async' => "true",
|
||||||
'fluentd-sub-second-precision' => 'true',
|
'fluentd-sub-second-precision' => "true",
|
||||||
],
|
]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if (count($this->database->ports_mappings_array) > 0) {
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
@ -88,47 +87,36 @@ class StartMysql
|
|||||||
if (count($persistent_storages) > 0) {
|
if (count($persistent_storages) > 0) {
|
||||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||||
}
|
}
|
||||||
if (count($persistent_file_volumes) > 0) {
|
|
||||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
|
||||||
return "$item->fs_path:$item->mount_path";
|
|
||||||
})->toArray();
|
|
||||||
}
|
|
||||||
if (count($volume_names) > 0) {
|
if (count($volume_names) > 0) {
|
||||||
$docker_compose['volumes'] = $volume_names;
|
$docker_compose['volumes'] = $volume_names;
|
||||||
}
|
}
|
||||||
if (! is_null($this->database->mysql_conf) || ! empty($this->database->mysql_conf)) {
|
if (!is_null($this->database->mysql_conf)) {
|
||||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||||
'type' => 'bind',
|
'type' => 'bind',
|
||||||
'source' => $this->configuration_dir.'/custom-config.cnf',
|
'source' => $this->configuration_dir . '/custom-config.cnf',
|
||||||
'target' => '/etc/mysql/conf.d/custom-config.cnf',
|
'target' => '/etc/mysql/conf.d/custom-config.cnf',
|
||||||
'read_only' => true,
|
'read_only' => true,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
$docker_compose_base64 = base64_encode($docker_compose);
|
$docker_compose_base64 = base64_encode($docker_compose);
|
||||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||||
$readme = generate_readme_file($this->database->name, now());
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
$this->commands[] = "echo 'Database started.'";
|
$this->commands[] = "echo 'Database started.'";
|
||||||
|
return remote_process($this->commands, $database->destination->server,callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_local_persistent_volumes()
|
private function generate_local_persistent_volumes()
|
||||||
{
|
{
|
||||||
$local_persistent_volumes = [];
|
$local_persistent_volumes = [];
|
||||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
||||||
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
|
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||||
} else {
|
|
||||||
$volume_name = $persistentStorage->name;
|
|
||||||
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $local_persistent_volumes;
|
return $local_persistent_volumes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +133,6 @@ class StartMysql
|
|||||||
'external' => false,
|
'external' => false,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $local_persistent_volumes_names;
|
return $local_persistent_volumes_names;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,32 +143,30 @@ class StartMysql
|
|||||||
$environment_variables->push("$env->key=$env->real_value");
|
$environment_variables->push("$env->key=$env->real_value");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('MYSQL_ROOT_PASSWORD'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_ROOT_PASSWORD'))->isEmpty()) {
|
||||||
$environment_variables->push("MYSQL_ROOT_PASSWORD={$this->database->mysql_root_password}");
|
$environment_variables->push("MYSQL_ROOT_PASSWORD={$this->database->mysql_root_password}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('MYSQL_DATABASE'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_DATABASE'))->isEmpty()) {
|
||||||
$environment_variables->push("MYSQL_DATABASE={$this->database->mysql_database}");
|
$environment_variables->push("MYSQL_DATABASE={$this->database->mysql_database}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('MYSQL_USER'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_USER'))->isEmpty()) {
|
||||||
$environment_variables->push("MYSQL_USER={$this->database->mysql_user}");
|
$environment_variables->push("MYSQL_USER={$this->database->mysql_user}");
|
||||||
}
|
}
|
||||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('MYSQL_PASSWORD'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_PASSWORD'))->isEmpty()) {
|
||||||
$environment_variables->push("MYSQL_PASSWORD={$this->database->mysql_password}");
|
$environment_variables->push("MYSQL_PASSWORD={$this->database->mysql_password}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return $environment_variables->all();
|
return $environment_variables->all();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function add_custom_mysql()
|
private function add_custom_mysql()
|
||||||
{
|
{
|
||||||
if (is_null($this->database->mysql_conf) || empty($this->database->mysql_conf)) {
|
if (is_null($this->database->mysql_conf)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$filename = 'custom-config.cnf';
|
$filename = 'custom-config.cnf';
|
||||||
$content = $this->database->mysql_conf;
|
$content = $this->database->mysql_conf;
|
||||||
$content_base64 = base64_encode($content);
|
$content_base64 = base64_encode($content);
|
||||||
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
|
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,41 +3,39 @@
|
|||||||
namespace App\Actions\Database;
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Illuminate\Support\Str;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class StartPostgresql
|
class StartPostgresql
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public StandalonePostgresql $database;
|
public StandalonePostgresql $database;
|
||||||
|
|
||||||
public array $commands = [];
|
public array $commands = [];
|
||||||
|
|
||||||
public array $init_scripts = [];
|
public array $init_scripts = [];
|
||||||
|
|
||||||
public string $configuration_dir;
|
public string $configuration_dir;
|
||||||
|
|
||||||
public function handle(StandalonePostgresql $database)
|
public function handle(StandalonePostgresql $database)
|
||||||
{
|
{
|
||||||
$this->database = $database;
|
$this->database = $database;
|
||||||
$container_name = $this->database->uuid;
|
$container_name = $this->database->uuid;
|
||||||
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||||
|
|
||||||
$this->commands = [
|
$this->commands = [
|
||||||
"echo 'Starting {$database->name}.'",
|
"echo 'Starting {$database->name}.'",
|
||||||
"mkdir -p $this->configuration_dir",
|
"mkdir -p $this->configuration_dir",
|
||||||
"mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d/",
|
"mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d/"
|
||||||
];
|
];
|
||||||
|
|
||||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||||
$persistent_file_volumes = $this->database->fileStorages()->get();
|
|
||||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
$environment_variables = $this->generate_environment_variables();
|
$environment_variables = $this->generate_environment_variables();
|
||||||
$this->generate_init_scripts();
|
$this->generate_init_scripts();
|
||||||
$this->add_custom_conf();
|
$this->add_custom_conf();
|
||||||
|
|
||||||
$docker_compose = [
|
$docker_compose = [
|
||||||
|
'version' => '3.8',
|
||||||
'services' => [
|
'services' => [
|
||||||
$container_name => [
|
$container_name => [
|
||||||
'image' => $this->database->image,
|
'image' => $this->database->image,
|
||||||
@ -52,13 +50,13 @@ class StartPostgresql
|
|||||||
],
|
],
|
||||||
'healthcheck' => [
|
'healthcheck' => [
|
||||||
'test' => [
|
'test' => [
|
||||||
'CMD-SHELL',
|
"CMD-SHELL",
|
||||||
"psql -U {$this->database->postgres_user} -d {$this->database->postgres_db} -c 'SELECT 1' || exit 1",
|
"psql -U {$this->database->postgres_user} -d {$this->database->postgres_db} -c 'SELECT 1' || exit 1"
|
||||||
],
|
],
|
||||||
'interval' => '5s',
|
'interval' => '5s',
|
||||||
'timeout' => '5s',
|
'timeout' => '5s',
|
||||||
'retries' => 10,
|
'retries' => 10,
|
||||||
'start_period' => '5s',
|
'start_period' => '5s'
|
||||||
],
|
],
|
||||||
'mem_limit' => $this->database->limits_memory,
|
'mem_limit' => $this->database->limits_memory,
|
||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
@ -66,27 +64,28 @@ class StartPostgresql
|
|||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => (float) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
],
|
]
|
||||||
],
|
],
|
||||||
'networks' => [
|
'networks' => [
|
||||||
$this->database->destination->network => [
|
$this->database->destination->network => [
|
||||||
'external' => true,
|
'external' => true,
|
||||||
'name' => $this->database->destination->network,
|
'name' => $this->database->destination->network,
|
||||||
'attachable' => true,
|
'attachable' => true,
|
||||||
],
|
]
|
||||||
],
|
]
|
||||||
];
|
];
|
||||||
if (! is_null($this->database->limits_cpuset)) {
|
if (!is_null($this->database->limits_cpuset)) {
|
||||||
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||||
}
|
}
|
||||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
|
ray('Log Drain Enabled');
|
||||||
$docker_compose['services'][$container_name]['logging'] = [
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
'driver' => 'fluentd',
|
'driver' => 'fluentd',
|
||||||
'options' => [
|
'options' => [
|
||||||
'fluentd-address' => 'tcp://127.0.0.1:24224',
|
'fluentd-address' => "tcp://127.0.0.1:24224",
|
||||||
'fluentd-async' => 'true',
|
'fluentd-async' => "true",
|
||||||
'fluentd-sub-second-precision' => 'true',
|
'fluentd-sub-second-precision' => "true",
|
||||||
],
|
]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if (count($this->database->ports_mappings_array) > 0) {
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
@ -95,11 +94,6 @@ class StartPostgresql
|
|||||||
if (count($persistent_storages) > 0) {
|
if (count($persistent_storages) > 0) {
|
||||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||||
}
|
}
|
||||||
if (count($persistent_file_volumes) > 0) {
|
|
||||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
|
||||||
return "$item->fs_path:$item->mount_path";
|
|
||||||
})->toArray();
|
|
||||||
}
|
|
||||||
if (count($volume_names) > 0) {
|
if (count($volume_names) > 0) {
|
||||||
$docker_compose['volumes'] = $volume_names;
|
$docker_compose['volumes'] = $volume_names;
|
||||||
}
|
}
|
||||||
@ -108,15 +102,15 @@ class StartPostgresql
|
|||||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||||
'type' => 'bind',
|
'type' => 'bind',
|
||||||
'source' => $init_script,
|
'source' => $init_script,
|
||||||
'target' => '/docker-entrypoint-initdb.d/'.basename($init_script),
|
'target' => '/docker-entrypoint-initdb.d/' . basename($init_script),
|
||||||
'read_only' => true,
|
'read_only' => true,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (! is_null($this->database->postgres_conf) && ! empty($this->database->postgres_conf)) {
|
if (!is_null($this->database->postgres_conf)) {
|
||||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||||
'type' => 'bind',
|
'type' => 'bind',
|
||||||
'source' => $this->configuration_dir.'/custom-postgres.conf',
|
'source' => $this->configuration_dir . '/custom-postgres.conf',
|
||||||
'target' => '/etc/postgresql/postgresql.conf',
|
'target' => '/etc/postgresql/postgresql.conf',
|
||||||
'read_only' => true,
|
'read_only' => true,
|
||||||
];
|
];
|
||||||
@ -128,14 +122,13 @@ class StartPostgresql
|
|||||||
}
|
}
|
||||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
$docker_compose_base64 = base64_encode($docker_compose);
|
$docker_compose_base64 = base64_encode($docker_compose);
|
||||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||||
$readme = generate_readme_file($this->database->name, now());
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
$this->commands[] = "echo 'Database started.'";
|
$this->commands[] = "echo 'Database started.'";
|
||||||
|
|
||||||
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,14 +136,9 @@ class StartPostgresql
|
|||||||
{
|
{
|
||||||
$local_persistent_volumes = [];
|
$local_persistent_volumes = [];
|
||||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
||||||
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
|
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||||
} else {
|
|
||||||
$volume_name = $persistentStorage->name;
|
|
||||||
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $local_persistent_volumes;
|
return $local_persistent_volumes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,32 +155,32 @@ class StartPostgresql
|
|||||||
'external' => false,
|
'external' => false,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $local_persistent_volumes_names;
|
return $local_persistent_volumes_names;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generate_environment_variables()
|
private function generate_environment_variables()
|
||||||
{
|
{
|
||||||
$environment_variables = collect();
|
$environment_variables = collect();
|
||||||
|
ray('Generate Environment Variables')->green();
|
||||||
|
ray($this->database->runtime_environment_variables)->green();
|
||||||
foreach ($this->database->runtime_environment_variables as $env) {
|
foreach ($this->database->runtime_environment_variables as $env) {
|
||||||
$environment_variables->push("$env->key=$env->real_value");
|
$environment_variables->push("$env->key=$env->real_value");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('POSTGRES_USER'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('POSTGRES_USER'))->isEmpty()) {
|
||||||
$environment_variables->push("POSTGRES_USER={$this->database->postgres_user}");
|
$environment_variables->push("POSTGRES_USER={$this->database->postgres_user}");
|
||||||
}
|
}
|
||||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('PGUSER'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('PGUSER'))->isEmpty()) {
|
||||||
$environment_variables->push("PGUSER={$this->database->postgres_user}");
|
$environment_variables->push("PGUSER={$this->database->postgres_user}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('POSTGRES_PASSWORD'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('POSTGRES_PASSWORD'))->isEmpty()) {
|
||||||
$environment_variables->push("POSTGRES_PASSWORD={$this->database->postgres_password}");
|
$environment_variables->push("POSTGRES_PASSWORD={$this->database->postgres_password}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('POSTGRES_DB'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('POSTGRES_DB'))->isEmpty()) {
|
||||||
$environment_variables->push("POSTGRES_DB={$this->database->postgres_db}");
|
$environment_variables->push("POSTGRES_DB={$this->database->postgres_db}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return $environment_variables->all();
|
return $environment_variables->all();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,24 +193,18 @@ class StartPostgresql
|
|||||||
$filename = data_get($init_script, 'filename');
|
$filename = data_get($init_script, 'filename');
|
||||||
$content = data_get($init_script, 'content');
|
$content = data_get($init_script, 'content');
|
||||||
$content_base64 = base64_encode($content);
|
$content_base64 = base64_encode($content);
|
||||||
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/docker-entrypoint-initdb.d/{$filename} > /dev/null";
|
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/docker-entrypoint-initdb.d/{$filename}";
|
||||||
$this->init_scripts[] = "$this->configuration_dir/docker-entrypoint-initdb.d/{$filename}";
|
$this->init_scripts[] = "$this->configuration_dir/docker-entrypoint-initdb.d/{$filename}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function add_custom_conf()
|
private function add_custom_conf()
|
||||||
{
|
{
|
||||||
if (is_null($this->database->postgres_conf) || empty($this->database->postgres_conf)) {
|
if (is_null($this->database->postgres_conf)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$filename = 'custom-postgres.conf';
|
$filename = 'custom-postgres.conf';
|
||||||
$content = $this->database->postgres_conf;
|
$content = $this->database->postgres_conf;
|
||||||
if (! str($content)->contains('listen_addresses')) {
|
|
||||||
$content .= "\nlisten_addresses = '*'";
|
|
||||||
$this->database->postgres_conf = $content;
|
|
||||||
$this->database->save();
|
|
||||||
}
|
|
||||||
$content_base64 = base64_encode($content);
|
$content_base64 = base64_encode($content);
|
||||||
$this->commands[] = "echo '{$content_base64}' | base64 -d | tee $this->configuration_dir/{$filename} > /dev/null";
|
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,19 +4,19 @@ namespace App\Actions\Database;
|
|||||||
|
|
||||||
use App\Models\StandaloneRedis;
|
use App\Models\StandaloneRedis;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Illuminate\Support\Str;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class StartRedis
|
class StartRedis
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public StandaloneRedis $database;
|
public StandaloneRedis $database;
|
||||||
|
|
||||||
public array $commands = [];
|
public array $commands = [];
|
||||||
|
|
||||||
public string $configuration_dir;
|
public string $configuration_dir;
|
||||||
|
|
||||||
|
|
||||||
public function handle(StandaloneRedis $database)
|
public function handle(StandaloneRedis $database)
|
||||||
{
|
{
|
||||||
$this->database = $database;
|
$this->database = $database;
|
||||||
@ -24,7 +24,7 @@ class StartRedis
|
|||||||
$startCommand = "redis-server --requirepass {$this->database->redis_password} --appendonly yes";
|
$startCommand = "redis-server --requirepass {$this->database->redis_password} --appendonly yes";
|
||||||
|
|
||||||
$container_name = $this->database->uuid;
|
$container_name = $this->database->uuid;
|
||||||
$this->configuration_dir = database_configuration_dir().'/'.$container_name;
|
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||||
|
|
||||||
$this->commands = [
|
$this->commands = [
|
||||||
"echo 'Starting {$database->name}.'",
|
"echo 'Starting {$database->name}.'",
|
||||||
@ -32,12 +32,12 @@ class StartRedis
|
|||||||
];
|
];
|
||||||
|
|
||||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||||
$persistent_file_volumes = $this->database->fileStorages()->get();
|
|
||||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
$environment_variables = $this->generate_environment_variables();
|
$environment_variables = $this->generate_environment_variables();
|
||||||
$this->add_custom_redis();
|
$this->add_custom_redis();
|
||||||
|
|
||||||
$docker_compose = [
|
$docker_compose = [
|
||||||
|
'version' => '3.8',
|
||||||
'services' => [
|
'services' => [
|
||||||
$container_name => [
|
$container_name => [
|
||||||
'image' => $this->database->image,
|
'image' => $this->database->image,
|
||||||
@ -55,12 +55,12 @@ class StartRedis
|
|||||||
'test' => [
|
'test' => [
|
||||||
'CMD-SHELL',
|
'CMD-SHELL',
|
||||||
'redis-cli',
|
'redis-cli',
|
||||||
'ping',
|
'ping'
|
||||||
],
|
],
|
||||||
'interval' => '5s',
|
'interval' => '5s',
|
||||||
'timeout' => '5s',
|
'timeout' => '5s',
|
||||||
'retries' => 10,
|
'retries' => 10,
|
||||||
'start_period' => '5s',
|
'start_period' => '5s'
|
||||||
],
|
],
|
||||||
'mem_limit' => $this->database->limits_memory,
|
'mem_limit' => $this->database->limits_memory,
|
||||||
'memswap_limit' => $this->database->limits_memory_swap,
|
'memswap_limit' => $this->database->limits_memory_swap,
|
||||||
@ -68,27 +68,27 @@ class StartRedis
|
|||||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||||
'cpus' => (float) $this->database->limits_cpus,
|
'cpus' => (float) $this->database->limits_cpus,
|
||||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||||
],
|
]
|
||||||
],
|
],
|
||||||
'networks' => [
|
'networks' => [
|
||||||
$this->database->destination->network => [
|
$this->database->destination->network => [
|
||||||
'external' => true,
|
'external' => true,
|
||||||
'name' => $this->database->destination->network,
|
'name' => $this->database->destination->network,
|
||||||
'attachable' => true,
|
'attachable' => true,
|
||||||
],
|
]
|
||||||
],
|
]
|
||||||
];
|
];
|
||||||
if (! is_null($this->database->limits_cpuset)) {
|
if (!is_null($this->database->limits_cpuset)) {
|
||||||
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
|
||||||
}
|
}
|
||||||
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
|
||||||
$docker_compose['services'][$container_name]['logging'] = [
|
$docker_compose['services'][$container_name]['logging'] = [
|
||||||
'driver' => 'fluentd',
|
'driver' => 'fluentd',
|
||||||
'options' => [
|
'options' => [
|
||||||
'fluentd-address' => 'tcp://127.0.0.1:24224',
|
'fluentd-address' => "tcp://127.0.0.1:24224",
|
||||||
'fluentd-async' => 'true',
|
'fluentd-async' => "true",
|
||||||
'fluentd-sub-second-precision' => 'true',
|
'fluentd-sub-second-precision' => "true",
|
||||||
],
|
]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if (count($this->database->ports_mappings_array) > 0) {
|
if (count($this->database->ports_mappings_array) > 0) {
|
||||||
@ -97,18 +97,13 @@ class StartRedis
|
|||||||
if (count($persistent_storages) > 0) {
|
if (count($persistent_storages) > 0) {
|
||||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||||
}
|
}
|
||||||
if (count($persistent_file_volumes) > 0) {
|
|
||||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
|
|
||||||
return "$item->fs_path:$item->mount_path";
|
|
||||||
})->toArray();
|
|
||||||
}
|
|
||||||
if (count($volume_names) > 0) {
|
if (count($volume_names) > 0) {
|
||||||
$docker_compose['volumes'] = $volume_names;
|
$docker_compose['volumes'] = $volume_names;
|
||||||
}
|
}
|
||||||
if (! is_null($this->database->redis_conf) || ! empty($this->database->redis_conf)) {
|
if (!is_null($this->database->redis_conf)) {
|
||||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||||
'type' => 'bind',
|
'type' => 'bind',
|
||||||
'source' => $this->configuration_dir.'/redis.conf',
|
'source' => $this->configuration_dir . '/redis.conf',
|
||||||
'target' => '/usr/local/etc/redis/redis.conf',
|
'target' => '/usr/local/etc/redis/redis.conf',
|
||||||
'read_only' => true,
|
'read_only' => true,
|
||||||
];
|
];
|
||||||
@ -116,14 +111,13 @@ class StartRedis
|
|||||||
}
|
}
|
||||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||||
$docker_compose_base64 = base64_encode($docker_compose);
|
$docker_compose_base64 = base64_encode($docker_compose);
|
||||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null";
|
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||||
$readme = generate_readme_file($this->database->name, now());
|
$readme = generate_readme_file($this->database->name, now());
|
||||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||||
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
$this->commands[] = "echo 'Pulling {$database->image} image.'";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
|
||||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||||
$this->commands[] = "echo 'Database started.'";
|
$this->commands[] = "echo 'Database started.'";
|
||||||
|
|
||||||
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,14 +125,9 @@ class StartRedis
|
|||||||
{
|
{
|
||||||
$local_persistent_volumes = [];
|
$local_persistent_volumes = [];
|
||||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||||
if ($persistentStorage->host_path !== '' && $persistentStorage->host_path !== null) {
|
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
||||||
$local_persistent_volumes[] = $persistentStorage->host_path.':'.$persistentStorage->mount_path;
|
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||||
} else {
|
|
||||||
$volume_name = $persistentStorage->name;
|
|
||||||
$local_persistent_volumes[] = $volume_name.':'.$persistentStorage->mount_path;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $local_persistent_volumes;
|
return $local_persistent_volumes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,7 +144,6 @@ class StartRedis
|
|||||||
'external' => false,
|
'external' => false,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $local_persistent_volumes_names;
|
return $local_persistent_volumes_names;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,16 +154,15 @@ class StartRedis
|
|||||||
$environment_variables->push("$env->key=$env->real_value");
|
$environment_variables->push("$env->key=$env->real_value");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($environment_variables->filter(fn ($env) => str($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('REDIS_PASSWORD'))->isEmpty()) {
|
||||||
$environment_variables->push("REDIS_PASSWORD={$this->database->redis_password}");
|
$environment_variables->push("REDIS_PASSWORD={$this->database->redis_password}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return $environment_variables->all();
|
return $environment_variables->all();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function add_custom_redis()
|
private function add_custom_redis()
|
||||||
{
|
{
|
||||||
if (is_null($this->database->redis_conf) || empty($this->database->redis_conf)) {
|
if (is_null($this->database->redis_conf)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$filename = 'redis.conf';
|
$filename = 'redis.conf';
|
||||||
|
@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Actions\Database;
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
use App\Models\StandaloneClickhouse;
|
use App\Events\DatabaseStatusChanged;
|
||||||
use App\Models\StandaloneDragonfly;
|
|
||||||
use App\Models\StandaloneKeydb;
|
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
@ -16,10 +14,10 @@ class StopDatabase
|
|||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $database)
|
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $database)
|
||||||
{
|
{
|
||||||
$server = $database->destination->server;
|
$server = $database->destination->server;
|
||||||
if (! $server->isFunctional()) {
|
if (!$server->isFunctional()) {
|
||||||
return 'Server is not functional';
|
return 'Server is not functional';
|
||||||
}
|
}
|
||||||
instant_remote_process(
|
instant_remote_process(
|
||||||
@ -29,5 +27,7 @@ class StopDatabase
|
|||||||
if ($database->is_public) {
|
if ($database->is_public) {
|
||||||
StopDatabaseProxy::run($database);
|
StopDatabaseProxy::run($database);
|
||||||
}
|
}
|
||||||
|
// TODO: make notification for services
|
||||||
|
// $database->environment->project->team->notify(new StatusChanged($database));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Actions\Database;
|
namespace App\Actions\Database;
|
||||||
|
|
||||||
use App\Events\DatabaseStatusChanged;
|
|
||||||
use App\Models\ServiceDatabase;
|
use App\Models\ServiceDatabase;
|
||||||
use App\Models\StandaloneClickhouse;
|
|
||||||
use App\Models\StandaloneDragonfly;
|
|
||||||
use App\Models\StandaloneKeydb;
|
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
@ -18,16 +14,14 @@ class StopDatabaseProxy
|
|||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|ServiceDatabase|StandaloneDragonfly|StandaloneClickhouse $database)
|
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|ServiceDatabase $database)
|
||||||
{
|
{
|
||||||
$server = data_get($database, 'destination.server');
|
$server = data_get($database, 'destination.server');
|
||||||
$uuid = $database->uuid;
|
|
||||||
if ($database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
if ($database->getMorphClass() === 'App\Models\ServiceDatabase') {
|
||||||
$uuid = $database->service->uuid;
|
|
||||||
$server = data_get($database, 'service.server');
|
$server = data_get($database, 'service.server');
|
||||||
}
|
}
|
||||||
instant_remote_process(["docker rm -f {$uuid}-proxy"], $server);
|
instant_remote_process(["docker rm -f {$database->uuid}-proxy"], $server);
|
||||||
|
$database->is_public = false;
|
||||||
$database->save();
|
$database->save();
|
||||||
DatabaseStatusChanged::dispatch();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,686 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Docker;
|
|
||||||
|
|
||||||
use App\Actions\Database\StartDatabaseProxy;
|
|
||||||
use App\Actions\Proxy\CheckProxy;
|
|
||||||
use App\Actions\Proxy\StartProxy;
|
|
||||||
use App\Actions\Shared\ComplexStatusCheck;
|
|
||||||
use App\Models\ApplicationPreview;
|
|
||||||
use App\Models\Server;
|
|
||||||
use App\Models\ServiceDatabase;
|
|
||||||
use App\Notifications\Container\ContainerRestarted;
|
|
||||||
use App\Notifications\Container\ContainerStopped;
|
|
||||||
use Illuminate\Support\Arr;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
|
|
||||||
class GetContainersStatus
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
public $applications;
|
|
||||||
|
|
||||||
public $server;
|
|
||||||
|
|
||||||
public function handle(Server $server)
|
|
||||||
{
|
|
||||||
// if (isDev()) {
|
|
||||||
// $server = Server::find(0);
|
|
||||||
// }
|
|
||||||
$this->server = $server;
|
|
||||||
if (! $this->server->isFunctional()) {
|
|
||||||
return 'Server is not ready.';
|
|
||||||
}
|
|
||||||
$this->applications = $this->server->applications();
|
|
||||||
$skip_these_applications = collect([]);
|
|
||||||
foreach ($this->applications as $application) {
|
|
||||||
if ($application->additional_servers->count() > 0) {
|
|
||||||
$skip_these_applications->push($application);
|
|
||||||
ComplexStatusCheck::run($application);
|
|
||||||
$this->applications = $this->applications->filter(function ($value, $key) use ($application) {
|
|
||||||
return $value->id !== $application->id;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->applications = $this->applications->filter(function ($value, $key) use ($skip_these_applications) {
|
|
||||||
return ! $skip_these_applications->pluck('id')->contains($value->id);
|
|
||||||
});
|
|
||||||
$this->old_way();
|
|
||||||
// if ($this->server->isSwarm()) {
|
|
||||||
// $this->old_way();
|
|
||||||
// } else {
|
|
||||||
// if (!$this->server->is_metrics_enabled) {
|
|
||||||
// $this->old_way();
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// $sentinel_found = instant_remote_process(["docker inspect coolify-sentinel"], $this->server, false);
|
|
||||||
// $sentinel_found = json_decode($sentinel_found, true);
|
|
||||||
// $status = data_get($sentinel_found, '0.State.Status', 'exited');
|
|
||||||
// if ($status === 'running') {
|
|
||||||
// ray('Checking with Sentinel');
|
|
||||||
// $this->sentinel();
|
|
||||||
// } else {
|
|
||||||
// ray('Checking the Old way');
|
|
||||||
// $this->old_way();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
private function sentinel()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$containers = $this->server->getContainers();
|
|
||||||
if ($containers->count() === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$databases = $this->server->databases();
|
|
||||||
$services = $this->server->services()->get();
|
|
||||||
$previews = $this->server->previews();
|
|
||||||
$foundApplications = [];
|
|
||||||
$foundApplicationPreviews = [];
|
|
||||||
$foundDatabases = [];
|
|
||||||
$foundServices = [];
|
|
||||||
|
|
||||||
foreach ($containers as $container) {
|
|
||||||
$labels = Arr::undot(data_get($container, 'labels'));
|
|
||||||
$containerStatus = data_get($container, 'state');
|
|
||||||
$containerHealth = data_get($container, 'health_status', 'unhealthy');
|
|
||||||
$containerStatus = "$containerStatus ($containerHealth)";
|
|
||||||
$applicationId = data_get($labels, 'coolify.applicationId');
|
|
||||||
if ($applicationId) {
|
|
||||||
$pullRequestId = data_get($labels, 'coolify.pullRequestId');
|
|
||||||
if ($pullRequestId) {
|
|
||||||
if (str($applicationId)->contains('-')) {
|
|
||||||
$applicationId = str($applicationId)->before('-');
|
|
||||||
}
|
|
||||||
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
|
|
||||||
if ($preview) {
|
|
||||||
$foundApplicationPreviews[] = $preview->id;
|
|
||||||
$statusFromDb = $preview->status;
|
|
||||||
if ($statusFromDb !== $containerStatus) {
|
|
||||||
$preview->update(['status' => $containerStatus]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//Notify user that this container should not be there.
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$application = $this->applications->where('id', $applicationId)->first();
|
|
||||||
if ($application) {
|
|
||||||
$foundApplications[] = $application->id;
|
|
||||||
$statusFromDb = $application->status;
|
|
||||||
if ($statusFromDb !== $containerStatus) {
|
|
||||||
$application->update(['status' => $containerStatus]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//Notify user that this container should not be there.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$uuid = data_get($labels, 'com.docker.compose.service');
|
|
||||||
$type = data_get($labels, 'coolify.type');
|
|
||||||
if ($uuid) {
|
|
||||||
if ($type === 'service') {
|
|
||||||
$database_id = data_get($labels, 'coolify.service.subId');
|
|
||||||
if ($database_id) {
|
|
||||||
$service_db = ServiceDatabase::where('id', $database_id)->first();
|
|
||||||
if ($service_db) {
|
|
||||||
$uuid = $service_db->service->uuid;
|
|
||||||
$isPublic = data_get($service_db, 'is_public');
|
|
||||||
if ($isPublic) {
|
|
||||||
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
|
|
||||||
if ($this->server->isSwarm()) {
|
|
||||||
// TODO: fix this with sentinel
|
|
||||||
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
|
||||||
} else {
|
|
||||||
return data_get($value, 'name') === "$uuid-proxy";
|
|
||||||
}
|
|
||||||
})->first();
|
|
||||||
if (! $foundTcpProxy) {
|
|
||||||
StartDatabaseProxy::run($service_db);
|
|
||||||
// $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$database = $databases->where('uuid', $uuid)->first();
|
|
||||||
if ($database) {
|
|
||||||
$isPublic = data_get($database, 'is_public');
|
|
||||||
$foundDatabases[] = $database->id;
|
|
||||||
$statusFromDb = $database->status;
|
|
||||||
if ($statusFromDb !== $containerStatus) {
|
|
||||||
$database->update(['status' => $containerStatus]);
|
|
||||||
}
|
|
||||||
if ($isPublic) {
|
|
||||||
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
|
|
||||||
if ($this->server->isSwarm()) {
|
|
||||||
// TODO: fix this with sentinel
|
|
||||||
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
|
||||||
} else {
|
|
||||||
return data_get($value, 'name') === "$uuid-proxy";
|
|
||||||
}
|
|
||||||
})->first();
|
|
||||||
if (! $foundTcpProxy) {
|
|
||||||
StartDatabaseProxy::run($database);
|
|
||||||
$this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Notify user that this container should not be there.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (data_get($container, 'name') === 'coolify-db') {
|
|
||||||
$foundDatabases[] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$serviceLabelId = data_get($labels, 'coolify.serviceId');
|
|
||||||
if ($serviceLabelId) {
|
|
||||||
$subType = data_get($labels, 'coolify.service.subType');
|
|
||||||
$subId = data_get($labels, 'coolify.service.subId');
|
|
||||||
$service = $services->where('id', $serviceLabelId)->first();
|
|
||||||
if (! $service) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ($subType === 'application') {
|
|
||||||
$service = $service->applications()->where('id', $subId)->first();
|
|
||||||
} else {
|
|
||||||
$service = $service->databases()->where('id', $subId)->first();
|
|
||||||
}
|
|
||||||
if ($service) {
|
|
||||||
$foundServices[] = "$service->id-$service->name";
|
|
||||||
$statusFromDb = $service->status;
|
|
||||||
if ($statusFromDb !== $containerStatus) {
|
|
||||||
// ray('Updating status: ' . $containerStatus);
|
|
||||||
$service->update(['status' => $containerStatus]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$exitedServices = collect([]);
|
|
||||||
foreach ($services as $service) {
|
|
||||||
$apps = $service->applications()->get();
|
|
||||||
$dbs = $service->databases()->get();
|
|
||||||
foreach ($apps as $app) {
|
|
||||||
if (in_array("$app->id-$app->name", $foundServices)) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
$exitedServices->push($app);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach ($dbs as $db) {
|
|
||||||
if (in_array("$db->id-$db->name", $foundServices)) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
$exitedServices->push($db);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$exitedServices = $exitedServices->unique('id');
|
|
||||||
foreach ($exitedServices as $exitedService) {
|
|
||||||
if (str($exitedService->status)->startsWith('exited')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$name = data_get($exitedService, 'name');
|
|
||||||
$fqdn = data_get($exitedService, 'fqdn');
|
|
||||||
if ($name) {
|
|
||||||
if ($fqdn) {
|
|
||||||
$containerName = "$name, available at $fqdn";
|
|
||||||
} else {
|
|
||||||
$containerName = $name;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ($fqdn) {
|
|
||||||
$containerName = $fqdn;
|
|
||||||
} else {
|
|
||||||
$containerName = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$projectUuid = data_get($service, 'environment.project.uuid');
|
|
||||||
$serviceUuid = data_get($service, 'uuid');
|
|
||||||
$environmentName = data_get($service, 'environment.name');
|
|
||||||
|
|
||||||
if ($projectUuid && $serviceUuid && $environmentName) {
|
|
||||||
$url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/service/'.$serviceUuid;
|
|
||||||
} else {
|
|
||||||
$url = null;
|
|
||||||
}
|
|
||||||
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
|
||||||
$exitedService->update(['status' => 'exited']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$notRunningApplications = $this->applications->pluck('id')->diff($foundApplications);
|
|
||||||
foreach ($notRunningApplications as $applicationId) {
|
|
||||||
$application = $this->applications->where('id', $applicationId)->first();
|
|
||||||
if (str($application->status)->startsWith('exited')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$application->update(['status' => 'exited']);
|
|
||||||
|
|
||||||
$name = data_get($application, 'name');
|
|
||||||
$fqdn = data_get($application, 'fqdn');
|
|
||||||
|
|
||||||
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
|
||||||
|
|
||||||
$projectUuid = data_get($application, 'environment.project.uuid');
|
|
||||||
$applicationUuid = data_get($application, 'uuid');
|
|
||||||
$environment = data_get($application, 'environment.name');
|
|
||||||
|
|
||||||
if ($projectUuid && $applicationUuid && $environment) {
|
|
||||||
$url = base_url().'/project/'.$projectUuid.'/'.$environment.'/application/'.$applicationUuid;
|
|
||||||
} else {
|
|
||||||
$url = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
|
||||||
}
|
|
||||||
$notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews);
|
|
||||||
foreach ($notRunningApplicationPreviews as $previewId) {
|
|
||||||
$preview = $previews->where('id', $previewId)->first();
|
|
||||||
if (str($preview->status)->startsWith('exited')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$preview->update(['status' => 'exited']);
|
|
||||||
|
|
||||||
$name = data_get($preview, 'name');
|
|
||||||
$fqdn = data_get($preview, 'fqdn');
|
|
||||||
|
|
||||||
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
|
||||||
|
|
||||||
$projectUuid = data_get($preview, 'application.environment.project.uuid');
|
|
||||||
$environmentName = data_get($preview, 'application.environment.name');
|
|
||||||
$applicationUuid = data_get($preview, 'application.uuid');
|
|
||||||
|
|
||||||
if ($projectUuid && $applicationUuid && $environmentName) {
|
|
||||||
$url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/application/'.$applicationUuid;
|
|
||||||
} else {
|
|
||||||
$url = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
|
||||||
}
|
|
||||||
$notRunningDatabases = $databases->pluck('id')->diff($foundDatabases);
|
|
||||||
foreach ($notRunningDatabases as $database) {
|
|
||||||
$database = $databases->where('id', $database)->first();
|
|
||||||
if (str($database->status)->startsWith('exited')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$database->update(['status' => 'exited']);
|
|
||||||
|
|
||||||
$name = data_get($database, 'name');
|
|
||||||
$fqdn = data_get($database, 'fqdn');
|
|
||||||
|
|
||||||
$containerName = $name;
|
|
||||||
|
|
||||||
$projectUuid = data_get($database, 'environment.project.uuid');
|
|
||||||
$environmentName = data_get($database, 'environment.name');
|
|
||||||
$databaseUuid = data_get($database, 'uuid');
|
|
||||||
|
|
||||||
if ($projectUuid && $databaseUuid && $environmentName) {
|
|
||||||
$url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/database/'.$databaseUuid;
|
|
||||||
} else {
|
|
||||||
$url = null;
|
|
||||||
}
|
|
||||||
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if proxy is running
|
|
||||||
$this->server->proxyType();
|
|
||||||
$foundProxyContainer = $containers->filter(function ($value, $key) {
|
|
||||||
if ($this->server->isSwarm()) {
|
|
||||||
// TODO: fix this with sentinel
|
|
||||||
return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
|
|
||||||
} else {
|
|
||||||
return data_get($value, 'name') === 'coolify-proxy';
|
|
||||||
}
|
|
||||||
})->first();
|
|
||||||
if (! $foundProxyContainer) {
|
|
||||||
try {
|
|
||||||
$shouldStart = CheckProxy::run($this->server);
|
|
||||||
if ($shouldStart) {
|
|
||||||
StartProxy::run($this->server, false);
|
|
||||||
$this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
ray($e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$this->server->proxy->status = data_get($foundProxyContainer, 'state');
|
|
||||||
$this->server->save();
|
|
||||||
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
|
||||||
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
|
||||||
}
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
// send_internal_notification("ContainerStatusJob failed on ({$this->server->id}) with: " . $e->getMessage());
|
|
||||||
ray($e->getMessage());
|
|
||||||
|
|
||||||
return handleError($e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function old_way()
|
|
||||||
{
|
|
||||||
if ($this->server->isSwarm()) {
|
|
||||||
$containers = instant_remote_process(["docker service inspect $(docker service ls -q) --format '{{json .}}'"], $this->server, false);
|
|
||||||
$containerReplicates = instant_remote_process(["docker service ls --format '{{json .}}'"], $this->server, false);
|
|
||||||
} else {
|
|
||||||
// Precheck for containers
|
|
||||||
$containers = instant_remote_process(['docker container ls -q'], $this->server, false);
|
|
||||||
if (! $containers) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$containers = instant_remote_process(["docker container inspect $(docker container ls -q) --format '{{json .}}'"], $this->server, false);
|
|
||||||
$containerReplicates = null;
|
|
||||||
}
|
|
||||||
if (is_null($containers)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$containers = format_docker_command_output_to_json($containers);
|
|
||||||
if ($containerReplicates) {
|
|
||||||
$containerReplicates = format_docker_command_output_to_json($containerReplicates);
|
|
||||||
foreach ($containerReplicates as $containerReplica) {
|
|
||||||
$name = data_get($containerReplica, 'Name');
|
|
||||||
$containers = $containers->map(function ($container) use ($name, $containerReplica) {
|
|
||||||
if (data_get($container, 'Spec.Name') === $name) {
|
|
||||||
$replicas = data_get($containerReplica, 'Replicas');
|
|
||||||
$running = str($replicas)->explode('/')[0];
|
|
||||||
$total = str($replicas)->explode('/')[1];
|
|
||||||
if ($running === $total) {
|
|
||||||
data_set($container, 'State.Status', 'running');
|
|
||||||
data_set($container, 'State.Health.Status', 'healthy');
|
|
||||||
} else {
|
|
||||||
data_set($container, 'State.Status', 'starting');
|
|
||||||
data_set($container, 'State.Health.Status', 'unhealthy');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $container;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$databases = $this->server->databases();
|
|
||||||
$services = $this->server->services()->get();
|
|
||||||
$previews = $this->server->previews();
|
|
||||||
$foundApplications = [];
|
|
||||||
$foundApplicationPreviews = [];
|
|
||||||
$foundDatabases = [];
|
|
||||||
$foundServices = [];
|
|
||||||
|
|
||||||
foreach ($containers as $container) {
|
|
||||||
if ($this->server->isSwarm()) {
|
|
||||||
$labels = data_get($container, 'Spec.Labels');
|
|
||||||
$uuid = data_get($labels, 'coolify.name');
|
|
||||||
} else {
|
|
||||||
$labels = data_get($container, 'Config.Labels');
|
|
||||||
}
|
|
||||||
$containerStatus = data_get($container, 'State.Status');
|
|
||||||
$containerHealth = data_get($container, 'State.Health.Status', 'unhealthy');
|
|
||||||
$containerStatus = "$containerStatus ($containerHealth)";
|
|
||||||
$labels = Arr::undot(format_docker_labels_to_json($labels));
|
|
||||||
$applicationId = data_get($labels, 'coolify.applicationId');
|
|
||||||
if ($applicationId) {
|
|
||||||
$pullRequestId = data_get($labels, 'coolify.pullRequestId');
|
|
||||||
if ($pullRequestId) {
|
|
||||||
if (str($applicationId)->contains('-')) {
|
|
||||||
$applicationId = str($applicationId)->before('-');
|
|
||||||
}
|
|
||||||
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
|
|
||||||
if ($preview) {
|
|
||||||
$foundApplicationPreviews[] = $preview->id;
|
|
||||||
$statusFromDb = $preview->status;
|
|
||||||
if ($statusFromDb !== $containerStatus) {
|
|
||||||
$preview->update(['status' => $containerStatus]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//Notify user that this container should not be there.
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$application = $this->applications->where('id', $applicationId)->first();
|
|
||||||
if ($application) {
|
|
||||||
$foundApplications[] = $application->id;
|
|
||||||
$statusFromDb = $application->status;
|
|
||||||
if ($statusFromDb !== $containerStatus) {
|
|
||||||
$application->update(['status' => $containerStatus]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//Notify user that this container should not be there.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$uuid = data_get($labels, 'com.docker.compose.service');
|
|
||||||
$type = data_get($labels, 'coolify.type');
|
|
||||||
|
|
||||||
if ($uuid) {
|
|
||||||
if ($type === 'service') {
|
|
||||||
$database_id = data_get($labels, 'coolify.service.subId');
|
|
||||||
if ($database_id) {
|
|
||||||
$service_db = ServiceDatabase::where('id', $database_id)->first();
|
|
||||||
if ($service_db) {
|
|
||||||
$uuid = data_get($service_db, 'service.uuid');
|
|
||||||
if ($uuid) {
|
|
||||||
$isPublic = data_get($service_db, 'is_public');
|
|
||||||
if ($isPublic) {
|
|
||||||
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
|
|
||||||
if ($this->server->isSwarm()) {
|
|
||||||
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
|
||||||
} else {
|
|
||||||
return data_get($value, 'Name') === "/$uuid-proxy";
|
|
||||||
}
|
|
||||||
})->first();
|
|
||||||
if (! $foundTcpProxy) {
|
|
||||||
StartDatabaseProxy::run($service_db);
|
|
||||||
// $this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$service_db->service->name}", $this->server));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$database = $databases->where('uuid', $uuid)->first();
|
|
||||||
if ($database) {
|
|
||||||
$isPublic = data_get($database, 'is_public');
|
|
||||||
$foundDatabases[] = $database->id;
|
|
||||||
$statusFromDb = $database->status;
|
|
||||||
if ($statusFromDb !== $containerStatus) {
|
|
||||||
$database->update(['status' => $containerStatus]);
|
|
||||||
}
|
|
||||||
if ($isPublic) {
|
|
||||||
$foundTcpProxy = $containers->filter(function ($value, $key) use ($uuid) {
|
|
||||||
if ($this->server->isSwarm()) {
|
|
||||||
return data_get($value, 'Spec.Name') === "coolify-proxy_$uuid";
|
|
||||||
} else {
|
|
||||||
return data_get($value, 'Name') === "/$uuid-proxy";
|
|
||||||
}
|
|
||||||
})->first();
|
|
||||||
if (! $foundTcpProxy) {
|
|
||||||
StartDatabaseProxy::run($database);
|
|
||||||
$this->server->team?->notify(new ContainerRestarted("TCP Proxy for {$database->name}", $this->server));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Notify user that this container should not be there.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (data_get($container, 'Name') === '/coolify-db') {
|
|
||||||
$foundDatabases[] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$serviceLabelId = data_get($labels, 'coolify.serviceId');
|
|
||||||
if ($serviceLabelId) {
|
|
||||||
$subType = data_get($labels, 'coolify.service.subType');
|
|
||||||
$subId = data_get($labels, 'coolify.service.subId');
|
|
||||||
$service = $services->where('id', $serviceLabelId)->first();
|
|
||||||
if (! $service) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ($subType === 'application') {
|
|
||||||
$service = $service->applications()->where('id', $subId)->first();
|
|
||||||
} else {
|
|
||||||
$service = $service->databases()->where('id', $subId)->first();
|
|
||||||
}
|
|
||||||
if ($service) {
|
|
||||||
$foundServices[] = "$service->id-$service->name";
|
|
||||||
$statusFromDb = $service->status;
|
|
||||||
if ($statusFromDb !== $containerStatus) {
|
|
||||||
// ray('Updating status: ' . $containerStatus);
|
|
||||||
$service->update(['status' => $containerStatus]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$exitedServices = collect([]);
|
|
||||||
foreach ($services as $service) {
|
|
||||||
$apps = $service->applications()->get();
|
|
||||||
$dbs = $service->databases()->get();
|
|
||||||
foreach ($apps as $app) {
|
|
||||||
if (in_array("$app->id-$app->name", $foundServices)) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
$exitedServices->push($app);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach ($dbs as $db) {
|
|
||||||
if (in_array("$db->id-$db->name", $foundServices)) {
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
$exitedServices->push($db);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$exitedServices = $exitedServices->unique('id');
|
|
||||||
foreach ($exitedServices as $exitedService) {
|
|
||||||
if (str($exitedService->status)->startsWith('exited')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$name = data_get($exitedService, 'name');
|
|
||||||
$fqdn = data_get($exitedService, 'fqdn');
|
|
||||||
if ($name) {
|
|
||||||
if ($fqdn) {
|
|
||||||
$containerName = "$name, available at $fqdn";
|
|
||||||
} else {
|
|
||||||
$containerName = $name;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ($fqdn) {
|
|
||||||
$containerName = $fqdn;
|
|
||||||
} else {
|
|
||||||
$containerName = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$projectUuid = data_get($service, 'environment.project.uuid');
|
|
||||||
$serviceUuid = data_get($service, 'uuid');
|
|
||||||
$environmentName = data_get($service, 'environment.name');
|
|
||||||
|
|
||||||
if ($projectUuid && $serviceUuid && $environmentName) {
|
|
||||||
$url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/service/'.$serviceUuid;
|
|
||||||
} else {
|
|
||||||
$url = null;
|
|
||||||
}
|
|
||||||
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
|
||||||
$exitedService->update(['status' => 'exited']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$notRunningApplications = $this->applications->pluck('id')->diff($foundApplications);
|
|
||||||
foreach ($notRunningApplications as $applicationId) {
|
|
||||||
$application = $this->applications->where('id', $applicationId)->first();
|
|
||||||
if (str($application->status)->startsWith('exited')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$application->update(['status' => 'exited']);
|
|
||||||
|
|
||||||
$name = data_get($application, 'name');
|
|
||||||
$fqdn = data_get($application, 'fqdn');
|
|
||||||
|
|
||||||
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
|
||||||
|
|
||||||
$projectUuid = data_get($application, 'environment.project.uuid');
|
|
||||||
$applicationUuid = data_get($application, 'uuid');
|
|
||||||
$environment = data_get($application, 'environment.name');
|
|
||||||
|
|
||||||
if ($projectUuid && $applicationUuid && $environment) {
|
|
||||||
$url = base_url().'/project/'.$projectUuid.'/'.$environment.'/application/'.$applicationUuid;
|
|
||||||
} else {
|
|
||||||
$url = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
|
||||||
}
|
|
||||||
$notRunningApplicationPreviews = $previews->pluck('id')->diff($foundApplicationPreviews);
|
|
||||||
foreach ($notRunningApplicationPreviews as $previewId) {
|
|
||||||
$preview = $previews->where('id', $previewId)->first();
|
|
||||||
if (str($preview->status)->startsWith('exited')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$preview->update(['status' => 'exited']);
|
|
||||||
|
|
||||||
$name = data_get($preview, 'name');
|
|
||||||
$fqdn = data_get($preview, 'fqdn');
|
|
||||||
|
|
||||||
$containerName = $name ? "$name ($fqdn)" : $fqdn;
|
|
||||||
|
|
||||||
$projectUuid = data_get($preview, 'application.environment.project.uuid');
|
|
||||||
$environmentName = data_get($preview, 'application.environment.name');
|
|
||||||
$applicationUuid = data_get($preview, 'application.uuid');
|
|
||||||
|
|
||||||
if ($projectUuid && $applicationUuid && $environmentName) {
|
|
||||||
$url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/application/'.$applicationUuid;
|
|
||||||
} else {
|
|
||||||
$url = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
|
||||||
}
|
|
||||||
$notRunningDatabases = $databases->pluck('id')->diff($foundDatabases);
|
|
||||||
foreach ($notRunningDatabases as $database) {
|
|
||||||
$database = $databases->where('id', $database)->first();
|
|
||||||
if (str($database->status)->startsWith('exited')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$database->update(['status' => 'exited']);
|
|
||||||
|
|
||||||
$name = data_get($database, 'name');
|
|
||||||
$fqdn = data_get($database, 'fqdn');
|
|
||||||
|
|
||||||
$containerName = $name;
|
|
||||||
|
|
||||||
$projectUuid = data_get($database, 'environment.project.uuid');
|
|
||||||
$environmentName = data_get($database, 'environment.name');
|
|
||||||
$databaseUuid = data_get($database, 'uuid');
|
|
||||||
|
|
||||||
if ($projectUuid && $databaseUuid && $environmentName) {
|
|
||||||
$url = base_url().'/project/'.$projectUuid.'/'.$environmentName.'/database/'.$databaseUuid;
|
|
||||||
} else {
|
|
||||||
$url = null;
|
|
||||||
}
|
|
||||||
// $this->server->team?->notify(new ContainerStopped($containerName, $this->server, $url));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if proxy is running
|
|
||||||
$this->server->proxyType();
|
|
||||||
$foundProxyContainer = $containers->filter(function ($value, $key) {
|
|
||||||
if ($this->server->isSwarm()) {
|
|
||||||
return data_get($value, 'Spec.Name') === 'coolify-proxy_traefik';
|
|
||||||
} else {
|
|
||||||
return data_get($value, 'Name') === '/coolify-proxy';
|
|
||||||
}
|
|
||||||
})->first();
|
|
||||||
if (! $foundProxyContainer) {
|
|
||||||
try {
|
|
||||||
$shouldStart = CheckProxy::run($this->server);
|
|
||||||
if ($shouldStart) {
|
|
||||||
StartProxy::run($this->server, false);
|
|
||||||
$this->server->team?->notify(new ContainerRestarted('coolify-proxy', $this->server));
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
ray($e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$this->server->proxy->status = data_get($foundProxyContainer, 'State.Status');
|
|
||||||
$this->server->save();
|
|
||||||
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
|
||||||
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,12 +16,12 @@ class CreateNewUser implements CreatesNewUsers
|
|||||||
/**
|
/**
|
||||||
* Validate and create a newly registered user.
|
* Validate and create a newly registered user.
|
||||||
*
|
*
|
||||||
* @param array<string, string> $input
|
* @param array<string, string> $input
|
||||||
*/
|
*/
|
||||||
public function create(array $input): User
|
public function create(array $input): User
|
||||||
{
|
{
|
||||||
$settings = InstanceSettings::get();
|
$settings = InstanceSettings::get();
|
||||||
if (! $settings->is_registration_enabled) {
|
if (!$settings->is_registration_enabled) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
Validator::make($input, [
|
Validator::make($input, [
|
||||||
@ -66,7 +66,6 @@ class CreateNewUser implements CreatesNewUsers
|
|||||||
}
|
}
|
||||||
// Set session variable
|
// Set session variable
|
||||||
session(['currentTeam' => $user->currentTeam = $team]);
|
session(['currentTeam' => $user->currentTeam = $team]);
|
||||||
|
|
||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ class ResetUserPassword implements ResetsUserPasswords
|
|||||||
/**
|
/**
|
||||||
* Validate and reset the user's forgotten password.
|
* Validate and reset the user's forgotten password.
|
||||||
*
|
*
|
||||||
* @param array<string, string> $input
|
* @param array<string, string> $input
|
||||||
*/
|
*/
|
||||||
public function reset(User $user, array $input): void
|
public function reset(User $user, array $input): void
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@ class UpdateUserPassword implements UpdatesUserPasswords
|
|||||||
/**
|
/**
|
||||||
* Validate and update the user's password.
|
* Validate and update the user's password.
|
||||||
*
|
*
|
||||||
* @param array<string, string> $input
|
* @param array<string, string> $input
|
||||||
*/
|
*/
|
||||||
public function update(User $user, array $input): void
|
public function update(User $user, array $input): void
|
||||||
{
|
{
|
||||||
|
@ -13,7 +13,7 @@ class UpdateUserProfileInformation implements UpdatesUserProfileInformation
|
|||||||
/**
|
/**
|
||||||
* Validate and update the given user's profile information.
|
* Validate and update the given user's profile information.
|
||||||
*
|
*
|
||||||
* @param array<string, string> $input
|
* @param array<string, string> $input
|
||||||
*/
|
*/
|
||||||
public function update(User $user, array $input): void
|
public function update(User $user, array $input): void
|
||||||
{
|
{
|
||||||
@ -45,7 +45,7 @@ class UpdateUserProfileInformation implements UpdatesUserProfileInformation
|
|||||||
/**
|
/**
|
||||||
* Update the given verified user's profile information.
|
* Update the given verified user's profile information.
|
||||||
*
|
*
|
||||||
* @param array<string, string> $input
|
* @param array<string, string> $input
|
||||||
*/
|
*/
|
||||||
protected function updateVerifiedUser(User $user, array $input): void
|
protected function updateVerifiedUser(User $user, array $input): void
|
||||||
{
|
{
|
||||||
|
@ -6,10 +6,10 @@ use App\Models\InstanceSettings;
|
|||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
|
||||||
class CheckResaleLicense
|
class CheckResaleLicense
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@ -18,7 +18,6 @@ class CheckResaleLicense
|
|||||||
$settings->update([
|
$settings->update([
|
||||||
'is_resale_license_active' => true,
|
'is_resale_license_active' => true,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// if (!$settings->resale_license) {
|
// if (!$settings->resale_license) {
|
||||||
@ -39,7 +38,6 @@ class CheckResaleLicense
|
|||||||
$settings->update([
|
$settings->update([
|
||||||
'is_resale_license_active' => true,
|
'is_resale_license_active' => true,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$data = Http::withHeaders([
|
$data = Http::withHeaders([
|
||||||
@ -53,7 +51,6 @@ class CheckResaleLicense
|
|||||||
$settings->update([
|
$settings->update([
|
||||||
'is_resale_license_active' => true,
|
'is_resale_license_active' => true,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data_get($data, 'license_key.status') === 'active') {
|
if (data_get($data, 'license_key.status') === 'active') {
|
||||||
|
@ -2,32 +2,27 @@
|
|||||||
|
|
||||||
namespace App\Actions\Proxy;
|
namespace App\Actions\Proxy;
|
||||||
|
|
||||||
use App\Models\Server;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use App\Models\Server;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class CheckConfiguration
|
class CheckConfiguration
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Server $server, bool $reset = false)
|
public function handle(Server $server, bool $reset = false)
|
||||||
{
|
{
|
||||||
$proxyType = $server->proxyType();
|
$proxy_path = get_proxy_path();
|
||||||
if ($proxyType === 'NONE') {
|
$proxy_configuration = instant_remote_process([
|
||||||
return 'OK';
|
|
||||||
}
|
|
||||||
$proxy_path = $server->proxyPath();
|
|
||||||
$payload = [
|
|
||||||
"mkdir -p $proxy_path",
|
"mkdir -p $proxy_path",
|
||||||
"cat $proxy_path/docker-compose.yml",
|
"cat $proxy_path/docker-compose.yml",
|
||||||
];
|
], $server, false);
|
||||||
$proxy_configuration = instant_remote_process($payload, $server, false);
|
|
||||||
if ($reset || ! $proxy_configuration || is_null($proxy_configuration)) {
|
|
||||||
$proxy_configuration = str(generate_default_proxy_configuration($server))->trim()->value;
|
|
||||||
}
|
|
||||||
if (! $proxy_configuration || is_null($proxy_configuration)) {
|
|
||||||
throw new \Exception('Could not generate proxy configuration');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if ($reset || !$proxy_configuration || is_null($proxy_configuration)) {
|
||||||
|
$proxy_configuration = Str::of(generate_default_proxy_configuration($server))->trim()->value;
|
||||||
|
}
|
||||||
|
if (!$proxy_configuration || is_null($proxy_configuration)) {
|
||||||
|
throw new \Exception("Could not generate proxy configuration");
|
||||||
|
}
|
||||||
return $proxy_configuration;
|
return $proxy_configuration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,31 +8,11 @@ use Lorisleiva\Actions\Concerns\AsAction;
|
|||||||
class CheckProxy
|
class CheckProxy
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Server $server, $fromUI = false)
|
public function handle(Server $server, $fromUI = false)
|
||||||
{
|
{
|
||||||
if (! $server->isFunctional()) {
|
if (!$server->isProxyShouldRun()) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ($server->isBuildServer()) {
|
|
||||||
if ($server->proxy) {
|
|
||||||
$server->proxy = null;
|
|
||||||
$server->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$proxyType = $server->proxyType();
|
|
||||||
if (is_null($proxyType) || $proxyType === 'NONE' || $server->proxy->force_stop) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
['uptime' => $uptime, 'error' => $error] = $server->validateConnection();
|
|
||||||
if (! $uptime) {
|
|
||||||
throw new \Exception($error);
|
|
||||||
}
|
|
||||||
if (! $server->isProxyShouldRun()) {
|
|
||||||
if ($fromUI) {
|
if ($fromUI) {
|
||||||
throw new \Exception('Proxy should not run. You selected the Custom Proxy.');
|
throw new \Exception("Proxy should not run. You selected the Custom Proxy.");
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -44,17 +24,12 @@ class CheckProxy
|
|||||||
if ($status === 'running') {
|
if ($status === 'running') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
$status = getContainerStatus($server, 'coolify-proxy');
|
$status = getContainerStatus($server, 'coolify-proxy');
|
||||||
if ($status === 'running') {
|
if ($status === 'running') {
|
||||||
$server->proxy->set('status', 'running');
|
$server->proxy->set('status', 'running');
|
||||||
$server->save();
|
$server->save();
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ($server->settings->is_cloudflare_tunnel) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$ip = $server->ip;
|
$ip = $server->ip;
|
||||||
@ -68,19 +43,18 @@ class CheckProxy
|
|||||||
$port443 = is_resource($connection443) && fclose($connection443);
|
$port443 = is_resource($connection443) && fclose($connection443);
|
||||||
if ($port80) {
|
if ($port80) {
|
||||||
if ($fromUI) {
|
if ($fromUI) {
|
||||||
throw new \Exception("Port 80 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a><br>Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
throw new \Exception("Port 80 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a> <br> Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($port443) {
|
if ($port443) {
|
||||||
if ($fromUI) {
|
if ($fromUI) {
|
||||||
throw new \Exception("Port 443 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a><br>Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
throw new \Exception("Port 443 is in use.<br>You must stop the process using this port.<br>Docs: <a target='_blank' href='https://coolify.io/docs'>https://coolify.io/docs</a> <br> Discord: <a target='_blank' href='https://coollabs.io/discord'>https://coollabs.io/discord</a>");
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\Actions\Proxy;
|
namespace App\Actions\Proxy;
|
||||||
|
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
class SaveConfiguration
|
class SaveConfiguration
|
||||||
@ -14,15 +15,15 @@ class SaveConfiguration
|
|||||||
if (is_null($proxy_settings)) {
|
if (is_null($proxy_settings)) {
|
||||||
$proxy_settings = CheckConfiguration::run($server, true);
|
$proxy_settings = CheckConfiguration::run($server, true);
|
||||||
}
|
}
|
||||||
$proxy_path = $server->proxyPath();
|
$proxy_path = get_proxy_path();
|
||||||
$docker_compose_yml_base64 = base64_encode($proxy_settings);
|
$docker_compose_yml_base64 = base64_encode($proxy_settings);
|
||||||
|
|
||||||
$server->proxy->last_saved_settings = str($docker_compose_yml_base64)->pipe('md5')->value;
|
$server->proxy->last_saved_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
||||||
$server->save();
|
$server->save();
|
||||||
|
|
||||||
return instant_remote_process([
|
return instant_remote_process([
|
||||||
"mkdir -p $proxy_path",
|
"mkdir -p $proxy_path",
|
||||||
"echo '$docker_compose_yml_base64' | base64 -d | tee $proxy_path/docker-compose.yml > /dev/null",
|
"echo '$docker_compose_yml_base64' | base64 -d > $proxy_path/docker-compose.yml",
|
||||||
], $server);
|
], $server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,70 +2,60 @@
|
|||||||
|
|
||||||
namespace App\Actions\Proxy;
|
namespace App\Actions\Proxy;
|
||||||
|
|
||||||
use App\Events\ProxyStarted;
|
use App\Events\ProxyStatusChanged;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
use Spatie\Activitylog\Models\Activity;
|
use Spatie\Activitylog\Models\Activity;
|
||||||
|
|
||||||
class StartProxy
|
class StartProxy
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
public function handle(Server $server, bool $async = true): string|Activity
|
||||||
public function handle(Server $server, bool $async = true, bool $force = false): string|Activity
|
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$proxyType = $server->proxyType();
|
$proxyType = $server->proxyType();
|
||||||
if ((is_null($proxyType) || $proxyType === 'NONE' || $server->proxy->force_stop || $server->isBuildServer()) && $force === false) {
|
|
||||||
return 'OK';
|
|
||||||
}
|
|
||||||
$commands = collect([]);
|
$commands = collect([]);
|
||||||
$proxy_path = $server->proxyPath();
|
$proxy_path = get_proxy_path();
|
||||||
$configuration = CheckConfiguration::run($server);
|
$configuration = CheckConfiguration::run($server);
|
||||||
if (! $configuration) {
|
if (!$configuration) {
|
||||||
throw new \Exception('Configuration is not synced');
|
throw new \Exception("Configuration is not synced");
|
||||||
}
|
}
|
||||||
SaveConfiguration::run($server, $configuration);
|
SaveConfiguration::run($server, $configuration);
|
||||||
$docker_compose_yml_base64 = base64_encode($configuration);
|
$docker_compose_yml_base64 = base64_encode($configuration);
|
||||||
$server->proxy->last_applied_settings = str($docker_compose_yml_base64)->pipe('md5')->value;
|
$server->proxy->last_applied_settings = Str::of($docker_compose_yml_base64)->pipe('md5')->value;
|
||||||
$server->save();
|
$server->save();
|
||||||
if ($server->isSwarm()) {
|
if ($server->isSwarm()) {
|
||||||
$commands = $commands->merge([
|
$commands = $commands->merge([
|
||||||
"mkdir -p $proxy_path/dynamic",
|
"mkdir -p $proxy_path/dynamic && cd $proxy_path",
|
||||||
"cd $proxy_path",
|
|
||||||
"echo 'Creating required Docker Compose file.'",
|
"echo 'Creating required Docker Compose file.'",
|
||||||
"echo 'Starting coolify-proxy.'",
|
"echo 'Starting coolify-proxy.'",
|
||||||
'docker stack deploy -c docker-compose.yml coolify-proxy',
|
"cd $proxy_path && docker stack deploy -c docker-compose.yml coolify-proxy",
|
||||||
"echo 'Proxy started successfully.'",
|
"echo 'Proxy started successfully.'"
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
$caddfile = 'import /dynamic/*.caddy';
|
|
||||||
$commands = $commands->merge([
|
$commands = $commands->merge([
|
||||||
"mkdir -p $proxy_path/dynamic",
|
"mkdir -p $proxy_path/dynamic && cd $proxy_path",
|
||||||
"cd $proxy_path",
|
|
||||||
"echo '$caddfile' > $proxy_path/dynamic/Caddyfile",
|
|
||||||
"echo 'Creating required Docker Compose file.'",
|
"echo 'Creating required Docker Compose file.'",
|
||||||
"echo 'Pulling docker image.'",
|
"echo 'Pulling docker image.'",
|
||||||
'docker compose pull',
|
'docker compose pull',
|
||||||
"echo 'Stopping existing coolify-proxy.'",
|
"echo 'Stopping existing coolify-proxy.'",
|
||||||
'docker compose down -v --remove-orphans > /dev/null 2>&1',
|
"docker compose down -v --remove-orphans > /dev/null 2>&1",
|
||||||
"echo 'Starting coolify-proxy.'",
|
"echo 'Starting coolify-proxy.'",
|
||||||
'docker compose up -d --remove-orphans',
|
'docker compose up -d --remove-orphans',
|
||||||
"echo 'Proxy started successfully.'",
|
"echo 'Proxy started successfully.'"
|
||||||
]);
|
]);
|
||||||
$commands = $commands->merge(connectProxyToNetworks($server));
|
$commands = $commands->merge(connectProxyToNetworks($server));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($async) {
|
if ($async) {
|
||||||
$activity = remote_process($commands, $server, callEventOnFinish: 'ProxyStarted', callEventData: $server);
|
$activity = remote_process($commands, $server);
|
||||||
|
|
||||||
return $activity;
|
return $activity;
|
||||||
} else {
|
} else {
|
||||||
instant_remote_process($commands, $server);
|
instant_remote_process($commands, $server);
|
||||||
$server->proxy->set('status', 'running');
|
$server->proxy->set('status', 'running');
|
||||||
$server->proxy->set('type', $proxyType);
|
$server->proxy->set('type', $proxyType);
|
||||||
$server->save();
|
$server->save();
|
||||||
ProxyStarted::dispatch($server);
|
|
||||||
|
|
||||||
return 'OK';
|
return 'OK';
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
@ -2,17 +2,14 @@
|
|||||||
|
|
||||||
namespace App\Actions\Server;
|
namespace App\Actions\Server;
|
||||||
|
|
||||||
use App\Models\Server;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use App\Models\Server;
|
||||||
|
|
||||||
class CleanupDocker
|
class CleanupDocker
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Server $server, bool $force = true)
|
public function handle(Server $server, bool $force = true)
|
||||||
{
|
{
|
||||||
|
|
||||||
// cleanup docker images, containers, and builder caches
|
|
||||||
if ($force) {
|
if ($force) {
|
||||||
instant_remote_process(['docker image prune -af'], $server, false);
|
instant_remote_process(['docker image prune -af'], $server, false);
|
||||||
instant_remote_process(['docker container prune -f --filter "label=coolify.managed=true"'], $server, false);
|
instant_remote_process(['docker container prune -f --filter "label=coolify.managed=true"'], $server, false);
|
||||||
@ -22,15 +19,5 @@ class CleanupDocker
|
|||||||
instant_remote_process(['docker container prune -f --filter "label=coolify.managed=true"'], $server, false);
|
instant_remote_process(['docker container prune -f --filter "label=coolify.managed=true"'], $server, false);
|
||||||
instant_remote_process(['docker builder prune -f'], $server, false);
|
instant_remote_process(['docker builder prune -f'], $server, false);
|
||||||
}
|
}
|
||||||
// cleanup networks
|
|
||||||
// $networks = collectDockerNetworksByServer($server);
|
|
||||||
// $proxyNetworks = collectProxyDockerNetworksByServer($server);
|
|
||||||
// $diff = $proxyNetworks->diff($networks);
|
|
||||||
// if ($diff->count() > 0) {
|
|
||||||
// $diff->map(function ($network) use ($server) {
|
|
||||||
// instant_remote_process(["docker network disconnect $network coolify-proxy"], $server);
|
|
||||||
// instant_remote_process(["docker network rm $network"], $server);
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Server;
|
|
||||||
|
|
||||||
use App\Models\Server;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
use Symfony\Component\Yaml\Yaml;
|
|
||||||
|
|
||||||
class ConfigureCloudflared
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
public function handle(Server $server, string $cloudflare_token)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$config = [
|
|
||||||
'services' => [
|
|
||||||
'coolify-cloudflared' => [
|
|
||||||
'container_name' => 'coolify-cloudflared',
|
|
||||||
'image' => 'cloudflare/cloudflared:latest',
|
|
||||||
'restart' => RESTART_MODE,
|
|
||||||
'network_mode' => 'host',
|
|
||||||
'command' => 'tunnel run',
|
|
||||||
'environment' => [
|
|
||||||
"TUNNEL_TOKEN={$cloudflare_token}",
|
|
||||||
],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
$config = Yaml::dump($config, 12, 2);
|
|
||||||
$docker_compose_yml_base64 = base64_encode($config);
|
|
||||||
$commands = collect([
|
|
||||||
'mkdir -p /tmp/cloudflared',
|
|
||||||
'cd /tmp/cloudflared',
|
|
||||||
"echo '$docker_compose_yml_base64' | base64 -d | tee docker-compose.yml > /dev/null",
|
|
||||||
'docker compose pull',
|
|
||||||
'docker compose down -v --remove-orphans > /dev/null 2>&1',
|
|
||||||
'docker compose up -d --remove-orphans',
|
|
||||||
]);
|
|
||||||
instant_remote_process($commands, $server);
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
ray($e);
|
|
||||||
throw $e;
|
|
||||||
} finally {
|
|
||||||
$commands = collect([
|
|
||||||
'rm -fr /tmp/cloudflared',
|
|
||||||
]);
|
|
||||||
instant_remote_process($commands, $server);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,21 +2,20 @@
|
|||||||
|
|
||||||
namespace App\Actions\Server;
|
namespace App\Actions\Server;
|
||||||
|
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use App\Models\StandaloneDocker;
|
use App\Models\StandaloneDocker;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
|
|
||||||
class InstallDocker
|
class InstallDocker
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Server $server)
|
public function handle(Server $server)
|
||||||
{
|
{
|
||||||
$supported_os_type = $server->validateOS();
|
$supported_os_type = $server->validateOS();
|
||||||
if (! $supported_os_type) {
|
if (!$supported_os_type) {
|
||||||
throw new \Exception('Server OS type is not supported for automated installation. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://coolify.io/docs/installation#manually">documentation</a>.');
|
throw new \Exception('Server OS type is not supported for automated installation. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://coolify.io/docs/servers#install-docker-engine-manually">documentation</a>.');
|
||||||
}
|
}
|
||||||
ray('Installing Docker on server: '.$server->name.' ('.$server->ip.')'.' with OS type: '.$supported_os_type);
|
ray('Installing Docker on server: ' . $server->name . ' (' . $server->ip . ')' . ' with OS type: ' . $supported_os_type);
|
||||||
$dockerVersion = '24.0';
|
$dockerVersion = '24.0';
|
||||||
$config = base64_encode('{
|
$config = base64_encode('{
|
||||||
"log-driver": "json-file",
|
"log-driver": "json-file",
|
||||||
@ -37,41 +36,32 @@ class InstallDocker
|
|||||||
if (isDev() && $server->id === 0) {
|
if (isDev() && $server->id === 0) {
|
||||||
$command = $command->merge([
|
$command = $command->merge([
|
||||||
"echo 'Installing Prerequisites...'",
|
"echo 'Installing Prerequisites...'",
|
||||||
'sleep 1',
|
"sleep 1",
|
||||||
"echo 'Installing Docker Engine...'",
|
"echo 'Installing Docker Engine...'",
|
||||||
"echo 'Configuring Docker Engine (merging existing configuration with the required)...'",
|
"echo 'Configuring Docker Engine (merging existing configuration with the required)...'",
|
||||||
'sleep 4',
|
"sleep 4",
|
||||||
"echo 'Restarting Docker Engine...'",
|
"echo 'Restarting Docker Engine...'",
|
||||||
'ls -l /tmp',
|
"ls -l /tmp"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return remote_process($command, $server);
|
return remote_process($command, $server);
|
||||||
} else {
|
} else {
|
||||||
if ($supported_os_type->contains('debian')) {
|
if ($supported_os_type->contains('debian')) {
|
||||||
$command = $command->merge([
|
$command = $command->merge([
|
||||||
"echo 'Installing Prerequisites...'",
|
"echo 'Installing Prerequisites...'",
|
||||||
'apt-get update -y',
|
"command -v jq >/dev/null || apt-get update -y",
|
||||||
'command -v curl >/dev/null || apt install -y curl',
|
"command -v jq >/dev/null || apt install -y curl wget git jq",
|
||||||
'command -v wget >/dev/null || apt install -y wget',
|
|
||||||
'command -v git >/dev/null || apt install -y git',
|
|
||||||
'command -v jq >/dev/null || apt install -y jq',
|
|
||||||
]);
|
]);
|
||||||
} elseif ($supported_os_type->contains('rhel')) {
|
} else if ($supported_os_type->contains('rhel')) {
|
||||||
$command = $command->merge([
|
$command = $command->merge([
|
||||||
"echo 'Installing Prerequisites...'",
|
"echo 'Installing Prerequisites...'",
|
||||||
'command -v curl >/dev/null || dnf install -y curl',
|
"command -v jq >/dev/null || dnf install -y curl wget git jq",
|
||||||
'command -v wget >/dev/null || dnf install -y wget',
|
|
||||||
'command -v git >/dev/null || dnf install -y git',
|
|
||||||
'command -v jq >/dev/null || dnf install -y jq',
|
|
||||||
]);
|
]);
|
||||||
} elseif ($supported_os_type->contains('sles')) {
|
} else if ($supported_os_type->contains('sles')) {
|
||||||
$command = $command->merge([
|
$command = $command->merge([
|
||||||
"echo 'Installing Prerequisites...'",
|
"echo 'Installing Prerequisites...'",
|
||||||
'zypper update -y',
|
"command -v jq >/dev/null || zypper update -y",
|
||||||
'command -v curl >/dev/null || zypper install -y curl',
|
"command -v jq >/dev/null || zypper install -y curl wget git jq",
|
||||||
'command -v wget >/dev/null || zypper install -y wget',
|
|
||||||
'command -v git >/dev/null || zypper install -y git',
|
|
||||||
'command -v jq >/dev/null || zypper install -y jq',
|
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
throw new \Exception('Unsupported OS');
|
throw new \Exception('Unsupported OS');
|
||||||
@ -80,30 +70,26 @@ class InstallDocker
|
|||||||
"echo 'Installing Docker Engine...'",
|
"echo 'Installing Docker Engine...'",
|
||||||
"curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh || curl https://get.docker.com | sh -s -- --version {$dockerVersion}",
|
"curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh || curl https://get.docker.com | sh -s -- --version {$dockerVersion}",
|
||||||
"echo 'Configuring Docker Engine (merging existing configuration with the required)...'",
|
"echo 'Configuring Docker Engine (merging existing configuration with the required)...'",
|
||||||
'test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json "/etc/docker/daemon.json.original-$(date +"%Y%m%d-%H%M%S")"',
|
"test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json \"/etc/docker/daemon.json.original-`date +\"%Y%m%d-%H%M%S\"`\" || echo '{$config}' | base64 -d > /etc/docker/daemon.json",
|
||||||
"test ! -s /etc/docker/daemon.json && echo '{$config}' | base64 -d | tee /etc/docker/daemon.json > /dev/null",
|
"echo '{$config}' | base64 -d > /etc/docker/daemon.json.coolify",
|
||||||
"echo '{$config}' | base64 -d | tee /etc/docker/daemon.json.coolify > /dev/null",
|
"cat <<< $(jq . /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json.coolify",
|
||||||
'jq . /etc/docker/daemon.json.coolify | tee /etc/docker/daemon.json.coolify.pretty > /dev/null',
|
"cat <<< $(jq -s '.[0] * .[1]' /etc/docker/daemon.json /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json",
|
||||||
'mv /etc/docker/daemon.json.coolify.pretty /etc/docker/daemon.json.coolify',
|
|
||||||
"jq -s '.[0] * .[1]' /etc/docker/daemon.json.coolify /etc/docker/daemon.json | tee /etc/docker/daemon.json.appended > /dev/null",
|
|
||||||
'mv /etc/docker/daemon.json.appended /etc/docker/daemon.json',
|
|
||||||
"echo 'Restarting Docker Engine...'",
|
"echo 'Restarting Docker Engine...'",
|
||||||
'systemctl enable docker >/dev/null 2>&1 || true',
|
"systemctl enable docker >/dev/null 2>&1 || true",
|
||||||
'systemctl restart docker',
|
"systemctl restart docker",
|
||||||
]);
|
]);
|
||||||
if ($server->isSwarm()) {
|
if ($server->isSwarm()) {
|
||||||
$command = $command->merge([
|
$command = $command->merge([
|
||||||
'docker network create --attachable --driver overlay coolify-overlay >/dev/null 2>&1 || true',
|
"docker network create --attachable --driver overlay coolify-overlay >/dev/null 2>&1 || true",
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
$command = $command->merge([
|
$command = $command->merge([
|
||||||
'docker network create --attachable coolify >/dev/null 2>&1 || true',
|
"docker network create --attachable coolify >/dev/null 2>&1 || true",
|
||||||
]);
|
]);
|
||||||
$command = $command->merge([
|
$command = $command->merge([
|
||||||
"echo 'Done!'",
|
"echo 'Done!'",
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return remote_process($command, $server);
|
return remote_process($command, $server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,22 +2,21 @@
|
|||||||
|
|
||||||
namespace App\Actions\Server;
|
namespace App\Actions\Server;
|
||||||
|
|
||||||
use App\Models\Server;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use App\Models\Server;
|
||||||
|
|
||||||
class InstallLogDrain
|
class InstallLogDrain
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Server $server)
|
public function handle(Server $server)
|
||||||
{
|
{
|
||||||
if ($server->settings->is_logdrain_newrelic_enabled) {
|
if ($server->settings->is_logdrain_newrelic_enabled) {
|
||||||
$type = 'newrelic';
|
$type = 'newrelic';
|
||||||
} elseif ($server->settings->is_logdrain_highlight_enabled) {
|
} else if ($server->settings->is_logdrain_highlight_enabled) {
|
||||||
$type = 'highlight';
|
$type = 'highlight';
|
||||||
} elseif ($server->settings->is_logdrain_axiom_enabled) {
|
} else if ($server->settings->is_logdrain_axiom_enabled) {
|
||||||
$type = 'axiom';
|
$type = 'axiom';
|
||||||
} elseif ($server->settings->is_logdrain_custom_enabled) {
|
} else if ($server->settings->is_logdrain_custom_enabled) {
|
||||||
$type = 'custom';
|
$type = 'custom';
|
||||||
} else {
|
} else {
|
||||||
$type = 'none';
|
$type = 'none';
|
||||||
@ -26,12 +25,11 @@ class InstallLogDrain
|
|||||||
if ($type === 'none') {
|
if ($type === 'none') {
|
||||||
$command = [
|
$command = [
|
||||||
"echo 'Stopping old Fluent Bit'",
|
"echo 'Stopping old Fluent Bit'",
|
||||||
'docker rm -f coolify-log-drain || true',
|
"docker rm -f coolify-log-drain || true",
|
||||||
];
|
];
|
||||||
|
|
||||||
return instant_remote_process($command, $server);
|
return instant_remote_process($command, $server);
|
||||||
} elseif ($type === 'newrelic') {
|
} else if ($type === 'newrelic') {
|
||||||
if (! $server->settings->is_logdrain_newrelic_enabled) {
|
if (!$server->settings->is_logdrain_newrelic_enabled) {
|
||||||
throw new \Exception('New Relic log drain is not enabled.');
|
throw new \Exception('New Relic log drain is not enabled.');
|
||||||
}
|
}
|
||||||
$config = base64_encode("
|
$config = base64_encode("
|
||||||
@ -61,11 +59,11 @@ class InstallLogDrain
|
|||||||
# https://log-api.newrelic.com/log/v1 - US
|
# https://log-api.newrelic.com/log/v1 - US
|
||||||
base_uri \${BASE_URI}
|
base_uri \${BASE_URI}
|
||||||
");
|
");
|
||||||
} elseif ($type === 'highlight') {
|
} else if ($type === 'highlight') {
|
||||||
if (! $server->settings->is_logdrain_highlight_enabled) {
|
if (!$server->settings->is_logdrain_highlight_enabled) {
|
||||||
throw new \Exception('Highlight log drain is not enabled.');
|
throw new \Exception('Highlight log drain is not enabled.');
|
||||||
}
|
}
|
||||||
$config = base64_encode('
|
$config = base64_encode("
|
||||||
[SERVICE]
|
[SERVICE]
|
||||||
Flush 5
|
Flush 5
|
||||||
Daemon off
|
Daemon off
|
||||||
@ -73,7 +71,7 @@ class InstallLogDrain
|
|||||||
Parsers_File parsers.conf
|
Parsers_File parsers.conf
|
||||||
[INPUT]
|
[INPUT]
|
||||||
Name forward
|
Name forward
|
||||||
tag ${HIGHLIGHT_PROJECT_ID}
|
tag \${HIGHLIGHT_PROJECT_ID}
|
||||||
Buffer_Chunk_Size 1M
|
Buffer_Chunk_Size 1M
|
||||||
Buffer_Max_Size 6M
|
Buffer_Max_Size 6M
|
||||||
[OUTPUT]
|
[OUTPUT]
|
||||||
@ -81,9 +79,9 @@ class InstallLogDrain
|
|||||||
Match *
|
Match *
|
||||||
Host otel.highlight.io
|
Host otel.highlight.io
|
||||||
Port 24224
|
Port 24224
|
||||||
');
|
");
|
||||||
} elseif ($type === 'axiom') {
|
} else if ($type === 'axiom') {
|
||||||
if (! $server->settings->is_logdrain_axiom_enabled) {
|
if (!$server->settings->is_logdrain_axiom_enabled) {
|
||||||
throw new \Exception('Axiom log drain is not enabled.');
|
throw new \Exception('Axiom log drain is not enabled.');
|
||||||
}
|
}
|
||||||
$config = base64_encode("
|
$config = base64_encode("
|
||||||
@ -118,8 +116,8 @@ class InstallLogDrain
|
|||||||
json_date_format iso8601
|
json_date_format iso8601
|
||||||
tls On
|
tls On
|
||||||
");
|
");
|
||||||
} elseif ($type === 'custom') {
|
} else if ($type === 'custom') {
|
||||||
if (! $server->settings->is_logdrain_custom_enabled) {
|
if (!$server->settings->is_logdrain_custom_enabled) {
|
||||||
throw new \Exception('Custom log drain is not enabled.');
|
throw new \Exception('Custom log drain is not enabled.');
|
||||||
}
|
}
|
||||||
$config = base64_encode($server->settings->logdrain_custom_config);
|
$config = base64_encode($server->settings->logdrain_custom_config);
|
||||||
@ -135,7 +133,7 @@ class InstallLogDrain
|
|||||||
Regex /^(?!\s*$).+/
|
Regex /^(?!\s*$).+/
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
$compose = base64_encode('
|
$compose = base64_encode("
|
||||||
services:
|
services:
|
||||||
coolify-log-drain:
|
coolify-log-drain:
|
||||||
image: cr.fluentbit.io/fluent/fluent-bit:2.0
|
image: cr.fluentbit.io/fluent/fluent-bit:2.0
|
||||||
@ -149,7 +147,7 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 127.0.0.1:24224:24224
|
- 127.0.0.1:24224:24224
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
');
|
");
|
||||||
$readme = base64_encode('# New Relic Log Drain
|
$readme = base64_encode('# New Relic Log Drain
|
||||||
This log drain is based on [Fluent Bit](https://fluentbit.io/) and New Relic Log Forwarder.
|
This log drain is based on [Fluent Bit](https://fluentbit.io/) and New Relic Log Forwarder.
|
||||||
|
|
||||||
@ -162,18 +160,18 @@ Files:
|
|||||||
$base_uri = $server->settings->logdrain_newrelic_base_uri;
|
$base_uri = $server->settings->logdrain_newrelic_base_uri;
|
||||||
$base_path = config('coolify.base_config_path');
|
$base_path = config('coolify.base_config_path');
|
||||||
|
|
||||||
$config_path = $base_path.'/log-drains';
|
$config_path = $base_path . '/log-drains';
|
||||||
$fluent_bit_config = $config_path.'/fluent-bit.conf';
|
$fluent_bit_config = $config_path . '/fluent-bit.conf';
|
||||||
$parsers_config = $config_path.'/parsers.conf';
|
$parsers_config = $config_path . '/parsers.conf';
|
||||||
$compose_path = $config_path.'/docker-compose.yml';
|
$compose_path = $config_path . '/docker-compose.yml';
|
||||||
$readme_path = $config_path.'/README.md';
|
$readme_path = $config_path . '/README.md';
|
||||||
$command = [
|
$command = [
|
||||||
"echo 'Saving configuration'",
|
"echo 'Saving configuration'",
|
||||||
"mkdir -p $config_path",
|
"mkdir -p $config_path",
|
||||||
"echo '{$parsers}' | base64 -d | tee $parsers_config > /dev/null",
|
"echo '{$parsers}' | base64 -d > $parsers_config",
|
||||||
"echo '{$config}' | base64 -d | tee $fluent_bit_config > /dev/null",
|
"echo '{$config}' | base64 -d > $fluent_bit_config",
|
||||||
"echo '{$compose}' | base64 -d | tee $compose_path > /dev/null",
|
"echo '{$compose}' | base64 -d > $compose_path",
|
||||||
"echo '{$readme}' | base64 -d | tee $readme_path > /dev/null",
|
"echo '{$readme}' | base64 -d > $readme_path",
|
||||||
"test -f $config_path/.env && rm $config_path/.env",
|
"test -f $config_path/.env && rm $config_path/.env",
|
||||||
|
|
||||||
];
|
];
|
||||||
@ -182,18 +180,18 @@ Files:
|
|||||||
"echo LICENSE_KEY=$license_key >> $config_path/.env",
|
"echo LICENSE_KEY=$license_key >> $config_path/.env",
|
||||||
"echo BASE_URI=$base_uri >> $config_path/.env",
|
"echo BASE_URI=$base_uri >> $config_path/.env",
|
||||||
];
|
];
|
||||||
} elseif ($type === 'highlight') {
|
} else if ($type === 'highlight') {
|
||||||
$add_envs_command = [
|
$add_envs_command = [
|
||||||
"echo HIGHLIGHT_PROJECT_ID={$server->settings->logdrain_highlight_project_id} >> $config_path/.env",
|
"echo HIGHLIGHT_PROJECT_ID={$server->settings->logdrain_highlight_project_id} >> $config_path/.env",
|
||||||
];
|
];
|
||||||
} elseif ($type === 'axiom') {
|
} else if ($type === 'axiom') {
|
||||||
$add_envs_command = [
|
$add_envs_command = [
|
||||||
"echo AXIOM_DATASET_NAME={$server->settings->logdrain_axiom_dataset_name} >> $config_path/.env",
|
"echo AXIOM_DATASET_NAME={$server->settings->logdrain_axiom_dataset_name} >> $config_path/.env",
|
||||||
"echo AXIOM_API_KEY={$server->settings->logdrain_axiom_api_key} >> $config_path/.env",
|
"echo AXIOM_API_KEY={$server->settings->logdrain_axiom_api_key} >> $config_path/.env",
|
||||||
];
|
];
|
||||||
} elseif ($type === 'custom') {
|
} else if ($type === 'custom') {
|
||||||
$add_envs_command = [
|
$add_envs_command = [
|
||||||
"touch $config_path/.env",
|
"touch $config_path/.env"
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
throw new \Exception('Unknown log drain type.');
|
throw new \Exception('Unknown log drain type.');
|
||||||
@ -205,7 +203,6 @@ Files:
|
|||||||
"cd $config_path && docker compose up -d --remove-orphans",
|
"cd $config_path && docker compose up -d --remove-orphans",
|
||||||
];
|
];
|
||||||
$command = array_merge($command, $add_envs_command, $restart_command);
|
$command = array_merge($command, $add_envs_command, $restart_command);
|
||||||
|
|
||||||
return instant_remote_process($command, $server);
|
return instant_remote_process($command, $server);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e);
|
return handleError($e);
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Server;
|
|
||||||
|
|
||||||
use App\Enums\ActivityTypes;
|
|
||||||
use App\Models\Server;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
|
|
||||||
class RunCommand
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
public function handle(Server $server, $command)
|
|
||||||
{
|
|
||||||
$activity = remote_process(command: [$command], server: $server, ignore_errors: true, type: ActivityTypes::COMMAND->value);
|
|
||||||
|
|
||||||
return $activity;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Server;
|
|
||||||
|
|
||||||
use App\Models\Server;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
|
|
||||||
class StartSentinel
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
public function handle(Server $server, $version = 'latest', bool $restart = false)
|
|
||||||
{
|
|
||||||
if ($restart) {
|
|
||||||
StopSentinel::run($server);
|
|
||||||
}
|
|
||||||
$metrics_history = $server->settings->metrics_history_days;
|
|
||||||
$refresh_rate = $server->settings->metrics_refresh_rate_seconds;
|
|
||||||
$token = $server->settings->metrics_token;
|
|
||||||
instant_remote_process([
|
|
||||||
"docker run --rm --pull always -d -e \"TOKEN={$token}\" -e \"SCHEDULER=true\" -e \"METRICS_HISTORY={$metrics_history}\" -e \"REFRESH_RATE={$refresh_rate}\" --name coolify-sentinel -v /var/run/docker.sock:/var/run/docker.sock -v /data/coolify/metrics:/app/metrics -v /data/coolify/logs:/app/logs --pid host --health-cmd \"curl --fail http://127.0.0.1:8888/api/health || exit 1\" --health-interval 10s --health-retries 3 ghcr.io/coollabsio/sentinel:$version",
|
|
||||||
'chown -R 9999:root /data/coolify/metrics /data/coolify/logs',
|
|
||||||
'chmod -R 700 /data/coolify/metrics /data/coolify/logs',
|
|
||||||
], $server, true);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Server;
|
|
||||||
|
|
||||||
use App\Models\Server;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
|
|
||||||
class StopSentinel
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
public function handle(Server $server)
|
|
||||||
{
|
|
||||||
instant_remote_process(['docker rm -f coolify-sentinel'], $server, false);
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,21 +2,18 @@
|
|||||||
|
|
||||||
namespace App\Actions\Server;
|
namespace App\Actions\Server;
|
||||||
|
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
|
|
||||||
class UpdateCoolify
|
class UpdateCoolify
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public ?Server $server = null;
|
public ?Server $server = null;
|
||||||
|
|
||||||
public ?string $latestVersion = null;
|
public ?string $latestVersion = null;
|
||||||
|
|
||||||
public ?string $currentVersion = null;
|
public ?string $currentVersion = null;
|
||||||
|
|
||||||
public function handle($manual_update = false)
|
public function handle(bool $force)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$settings = InstanceSettings::get();
|
$settings = InstanceSettings::get();
|
||||||
@ -25,22 +22,32 @@ class UpdateCoolify
|
|||||||
if (!$this->server) {
|
if (!$this->server) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CleanupDocker::dispatch($this->server, false)->onQueue('high');
|
CleanupDocker::run($this->server, false);
|
||||||
$this->latestVersion = get_latest_version_of_coolify();
|
$this->latestVersion = get_latest_version_of_coolify();
|
||||||
$this->currentVersion = config('version');
|
$this->currentVersion = config('version');
|
||||||
if (!$manual_update) {
|
if ($settings->next_channel) {
|
||||||
|
ray('next channel enabled');
|
||||||
|
$this->latestVersion = 'next';
|
||||||
|
}
|
||||||
|
if ($force) {
|
||||||
|
$this->update();
|
||||||
|
} else {
|
||||||
if (!$settings->is_auto_update_enabled) {
|
if (!$settings->is_auto_update_enabled) {
|
||||||
return;
|
return 'Auto update is disabled';
|
||||||
}
|
}
|
||||||
if ($this->latestVersion === $this->currentVersion) {
|
if ($this->latestVersion === $this->currentVersion) {
|
||||||
return;
|
return 'Already on latest version';
|
||||||
}
|
}
|
||||||
if (version_compare($this->latestVersion, $this->currentVersion, '<')) {
|
if (version_compare($this->latestVersion, $this->currentVersion, '<')) {
|
||||||
return;
|
return 'Latest version is lower than current version?!';
|
||||||
}
|
}
|
||||||
|
$this->update();
|
||||||
}
|
}
|
||||||
$this->update();
|
send_internal_notification("Instance updated from {$this->currentVersion} -> {$this->latestVersion}");
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
ray('InstanceAutoUpdateJob failed');
|
||||||
|
ray($e->getMessage());
|
||||||
|
send_internal_notification('InstanceAutoUpdateJob failed: ' . $e->getMessage());
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -48,7 +55,7 @@ class UpdateCoolify
|
|||||||
private function update()
|
private function update()
|
||||||
{
|
{
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
ray('Running in dev mode');
|
ray("Running update on local docker container. Updating to $this->latestVersion");
|
||||||
remote_process([
|
remote_process([
|
||||||
"sleep 10"
|
"sleep 10"
|
||||||
], $this->server);
|
], $this->server);
|
||||||
@ -60,12 +67,7 @@ class UpdateCoolify
|
|||||||
"curl -fsSL https://cdn.lasthourhosting.org/lasthourcloud/scripts/upgrade.sh -o /data/coolify/source/upgrade.sh",
|
"curl -fsSL https://cdn.lasthourhosting.org/lasthourcloud/scripts/upgrade.sh -o /data/coolify/source/upgrade.sh",
|
||||||
"bash /data/coolify/source/upgrade.sh $this->latestVersion"
|
"bash /data/coolify/source/upgrade.sh $this->latestVersion"
|
||||||
], $this->server);
|
], $this->server);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
remote_process([
|
|
||||||
'curl -fsSL https://cdn.coollabs.io/coolify/upgrade.sh -o /data/coolify/source/upgrade.sh',
|
|
||||||
"bash /data/coolify/source/upgrade.sh $this->latestVersion",
|
|
||||||
], $this->server);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Server;
|
|
||||||
|
|
||||||
use App\Models\Server;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
|
|
||||||
class ValidateServer
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
public ?string $uptime = null;
|
|
||||||
|
|
||||||
public ?string $error = null;
|
|
||||||
|
|
||||||
public ?string $supported_os_type = null;
|
|
||||||
|
|
||||||
public ?string $docker_installed = null;
|
|
||||||
|
|
||||||
public ?string $docker_compose_installed = null;
|
|
||||||
|
|
||||||
public ?string $docker_version = null;
|
|
||||||
|
|
||||||
public function handle(Server $server)
|
|
||||||
{
|
|
||||||
$server->update([
|
|
||||||
'validation_logs' => null,
|
|
||||||
]);
|
|
||||||
['uptime' => $this->uptime, 'error' => $error] = $server->validateConnection();
|
|
||||||
if (! $this->uptime) {
|
|
||||||
$this->error = 'Server is not reachable. Please validate your configuration and connection.<br>Check this <a target="_blank" class="text-black underline dark:text-white" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help. <br><br><div class="text-error">Error: '.$error.'</div>';
|
|
||||||
$server->update([
|
|
||||||
'validation_logs' => $this->error,
|
|
||||||
]);
|
|
||||||
throw new \Exception($this->error);
|
|
||||||
}
|
|
||||||
$this->supported_os_type = $server->validateOS();
|
|
||||||
if (! $this->supported_os_type) {
|
|
||||||
$this->error = 'Server OS type is not supported. Please install Docker manually before continuing: <a target="_blank" class="text-black underline dark:text-white" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
|
||||||
$server->update([
|
|
||||||
'validation_logs' => $this->error,
|
|
||||||
]);
|
|
||||||
throw new \Exception($this->error);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->docker_installed = $server->validateDockerEngine();
|
|
||||||
$this->docker_compose_installed = $server->validateDockerCompose();
|
|
||||||
if (! $this->docker_installed || ! $this->docker_compose_installed) {
|
|
||||||
$this->error = 'Docker Engine is not installed. Please install Docker manually before continuing: <a target="_blank" class="text-black underline dark:text-white" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
|
||||||
$server->update([
|
|
||||||
'validation_logs' => $this->error,
|
|
||||||
]);
|
|
||||||
throw new \Exception($this->error);
|
|
||||||
}
|
|
||||||
$this->docker_version = $server->validateDockerEngineVersion();
|
|
||||||
|
|
||||||
if ($this->docker_version) {
|
|
||||||
return 'OK';
|
|
||||||
} else {
|
|
||||||
$this->error = 'Docker Engine is not installed. Please install Docker manually before continuing: <a target="_blank" class="text-black underline dark:text-white" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
|
||||||
$server->update([
|
|
||||||
'validation_logs' => $this->error,
|
|
||||||
]);
|
|
||||||
throw new \Exception($this->error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,13 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Actions\Service;
|
namespace App\Actions\Service;
|
||||||
|
|
||||||
use App\Models\Service;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use App\Models\Service;
|
||||||
|
|
||||||
class DeleteService
|
class DeleteService
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Service $service)
|
public function handle(Service $service)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Actions\Service;
|
|
||||||
|
|
||||||
use App\Models\Service;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
|
|
||||||
class RestartService
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
public function handle(Service $service)
|
|
||||||
{
|
|
||||||
StopService::run($service);
|
|
||||||
|
|
||||||
return StartService::run($service);
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,27 +2,26 @@
|
|||||||
|
|
||||||
namespace App\Actions\Service;
|
namespace App\Actions\Service;
|
||||||
|
|
||||||
use App\Models\Service;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use App\Models\Service;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
|
||||||
class StartService
|
class StartService
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Service $service)
|
public function handle(Service $service)
|
||||||
{
|
{
|
||||||
ray('Starting service: '.$service->name);
|
ray('Starting service: ' . $service->name);
|
||||||
$service->saveComposeConfigs();
|
$service->saveComposeConfigs();
|
||||||
$commands[] = 'cd '.$service->workdir();
|
$commands[] = "cd " . $service->workdir();
|
||||||
$commands[] = "echo 'Saved configuration files to {$service->workdir()}.'";
|
$commands[] = "echo 'Saved configuration files to {$service->workdir()}.'";
|
||||||
$commands[] = "echo 'Creating Docker network.'";
|
$commands[] = "echo 'Creating Docker network.'";
|
||||||
$commands[] = "docker network inspect $service->uuid >/dev/null 2>&1 || docker network create --attachable $service->uuid";
|
$commands[] = "docker network inspect $service->uuid >/dev/null 2>&1 || docker network create --attachable $service->uuid >/dev/null 2>&1 || true";
|
||||||
$commands[] = 'echo Starting service.';
|
$commands[] = "echo Starting service.";
|
||||||
$commands[] = "echo 'Pulling images.'";
|
$commands[] = "echo 'Pulling images.'";
|
||||||
$commands[] = 'docker compose pull';
|
$commands[] = "docker compose pull";
|
||||||
$commands[] = "echo 'Starting containers.'";
|
$commands[] = "echo 'Starting containers.'";
|
||||||
$commands[] = 'docker compose up -d --remove-orphans --force-recreate --build';
|
$commands[] = "docker compose up -d --remove-orphans --force-recreate --build";
|
||||||
$commands[] = "docker network connect $service->uuid coolify-proxy >/dev/null 2>&1 || true";
|
$commands[] = "docker network connect $service->uuid coolify-proxy >/dev/null 2>&1 || true";
|
||||||
if (data_get($service, 'connect_to_docker_network')) {
|
if (data_get($service, 'connect_to_docker_network')) {
|
||||||
$compose = data_get($service, 'docker_compose', []);
|
$compose = data_get($service, 'docker_compose', []);
|
||||||
@ -33,7 +32,6 @@ class StartService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$activity = remote_process($commands, $service->server, type_uuid: $service->uuid, callEventOnFinish: 'ServiceStatusChanged');
|
$activity = remote_process($commands, $service->server, type_uuid: $service->uuid, callEventOnFinish: 'ServiceStatusChanged');
|
||||||
|
|
||||||
return $activity;
|
return $activity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,37 +2,37 @@
|
|||||||
|
|
||||||
namespace App\Actions\Service;
|
namespace App\Actions\Service;
|
||||||
|
|
||||||
use App\Models\Service;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use App\Models\Service;
|
||||||
|
|
||||||
class StopService
|
class StopService
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Service $service)
|
public function handle(Service $service)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$server = $service->destination->server;
|
$server = $service->destination->server;
|
||||||
if (! $server->isFunctional()) {
|
if (!$server->isFunctional()) {
|
||||||
return 'Server is not functional';
|
return 'Server is not functional';
|
||||||
}
|
}
|
||||||
ray('Stopping service: '.$service->name);
|
ray('Stopping service: ' . $service->name);
|
||||||
$applications = $service->applications()->get();
|
$applications = $service->applications()->get();
|
||||||
foreach ($applications as $application) {
|
foreach ($applications as $application) {
|
||||||
instant_remote_process(["docker rm -f {$application->name}-{$service->uuid}"], $service->server, false);
|
instant_remote_process(["docker rm -f {$application->name}-{$service->uuid}"], $service->server);
|
||||||
$application->update(['status' => 'exited']);
|
$application->update(['status' => 'exited']);
|
||||||
}
|
}
|
||||||
$dbs = $service->databases()->get();
|
$dbs = $service->databases()->get();
|
||||||
foreach ($dbs as $db) {
|
foreach ($dbs as $db) {
|
||||||
instant_remote_process(["docker rm -f {$db->name}-{$service->uuid}"], $service->server, false);
|
instant_remote_process(["docker rm -f {$db->name}-{$service->uuid}"], $service->server);
|
||||||
$db->update(['status' => 'exited']);
|
$db->update(['status' => 'exited']);
|
||||||
}
|
}
|
||||||
instant_remote_process(["docker network disconnect {$service->uuid} coolify-proxy"], $service->server);
|
instant_remote_process(["docker network disconnect {$service->uuid} coolify-proxy 2>/dev/null"], $service->server, false);
|
||||||
instant_remote_process(["docker network rm {$service->uuid}"], $service->server);
|
instant_remote_process(["docker network rm {$service->uuid} 2>/dev/null"], $service->server, false);
|
||||||
|
// TODO: make notification for databases
|
||||||
|
// $service->environment->project->team->notify(new StatusChanged($service));
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
echo $e->getMessage();
|
echo $e->getMessage();
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
|
|
||||||
return $e->getMessage();
|
return $e->getMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,21 +8,18 @@ use Lorisleiva\Actions\Concerns\AsAction;
|
|||||||
class ComplexStatusCheck
|
class ComplexStatusCheck
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Application $application)
|
public function handle(Application $application)
|
||||||
{
|
{
|
||||||
$servers = $application->additional_servers;
|
$servers = $application->additional_servers;
|
||||||
$servers->push($application->destination->server);
|
$servers->push($application->destination->server);
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
$is_main_server = $application->destination->server->id === $server->id;
|
$is_main_server = $application->destination->server->id === $server->id;
|
||||||
if (! $server->isFunctional()) {
|
if (!$server->isFunctional()) {
|
||||||
if ($is_main_server) {
|
if ($is_main_server) {
|
||||||
$application->update(['status' => 'exited:unhealthy']);
|
$application->update(['status' => 'exited:unhealthy']);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
$application->additional_servers()->updateExistingPivot($server->id, ['status' => 'exited:unhealthy']);
|
$application->additional_servers()->updateExistingPivot($server->id, ['status' => 'exited:unhealthy']);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,11 +44,9 @@ class ComplexStatusCheck
|
|||||||
} else {
|
} else {
|
||||||
if ($is_main_server) {
|
if ($is_main_server) {
|
||||||
$application->update(['status' => 'exited:unhealthy']);
|
$application->update(['status' => 'exited:unhealthy']);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
$application->additional_servers()->updateExistingPivot($server->id, ['status' => 'exited:unhealthy']);
|
$application->additional_servers()->updateExistingPivot($server->id, ['status' => 'exited:unhealthy']);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,20 +8,17 @@ use Lorisleiva\Actions\Concerns\AsAction;
|
|||||||
class PullImage
|
class PullImage
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
public function handle(Service $resource)
|
public function handle(Service $resource)
|
||||||
{
|
{
|
||||||
$resource->saveComposeConfigs();
|
$resource->saveComposeConfigs();
|
||||||
|
|
||||||
$commands[] = 'cd '.$resource->workdir();
|
$commands[] = "cd " . $resource->workdir();
|
||||||
$commands[] = "echo 'Saved configuration files to {$resource->workdir()}.'";
|
$commands[] = "echo 'Saved configuration files to {$resource->workdir()}.'";
|
||||||
$commands[] = 'docker compose pull';
|
$commands[] = "docker compose pull";
|
||||||
|
|
||||||
$server = data_get($resource, 'server');
|
$server = data_get($resource, 'server');
|
||||||
|
|
||||||
if (! $server) {
|
if (!$server) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
instant_remote_process($commands, $resource->server);
|
instant_remote_process($commands, $resource->server);
|
||||||
}
|
}
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
|
|
||||||
class AdminRemoveUser extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The name and signature of the console command.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $signature = 'admin:remove-user {email}';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The console command description.
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $description = 'Remove User from database';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the console command.
|
|
||||||
*/
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$email = $this->argument('email');
|
|
||||||
$confirm = $this->confirm('Are you sure you want to remove user with email: '.$email.'?');
|
|
||||||
if (! $confirm) {
|
|
||||||
$this->info('User removal cancelled.');
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$this->info("Removing user with email: $email");
|
|
||||||
$user = User::whereEmail($email)->firstOrFail();
|
|
||||||
$teams = $user->teams;
|
|
||||||
foreach ($teams as $team) {
|
|
||||||
if ($team->members->count() > 1) {
|
|
||||||
$this->error('User is a member of a team with more than one member. Please remove user from team first.');
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$team->delete();
|
|
||||||
}
|
|
||||||
$user->delete();
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$this->error('Failed to remove user.');
|
|
||||||
$this->error($e->getMessage());
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
|
|
||||||
class CleanupApplicationDeploymentQueue extends Command
|
|
||||||
{
|
|
||||||
protected $signature = 'cleanup:application-deployment-queue {--team-id=}';
|
|
||||||
|
|
||||||
protected $description = 'CleanupApplicationDeploymentQueue';
|
|
||||||
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
$team_id = $this->option('team-id');
|
|
||||||
$servers = \App\Models\Server::where('team_id', $team_id)->get();
|
|
||||||
foreach ($servers as $server) {
|
|
||||||
$deployments = ApplicationDeploymentQueue::whereIn('status', ['in_progress', 'queued'])->where('server_id', $server->id)->get();
|
|
||||||
foreach ($deployments as $deployment) {
|
|
||||||
$deployment->update(['status' => 'failed']);
|
|
||||||
instant_remote_process(['docker rm -f '.$deployment->deployment_uuid], $server, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
|
|
||||||
class CleanupDatabase extends Command
|
|
||||||
{
|
|
||||||
protected $signature = 'cleanup:database {--yes}';
|
|
||||||
|
|
||||||
protected $description = 'Cleanup database';
|
|
||||||
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
if ($this->option('yes')) {
|
|
||||||
echo "Running database cleanup...\n";
|
|
||||||
} else {
|
|
||||||
echo "Running database cleanup in dry-run mode...\n";
|
|
||||||
}
|
|
||||||
$keep_days = 60;
|
|
||||||
echo "Keep days: $keep_days\n";
|
|
||||||
// Cleanup failed jobs table
|
|
||||||
$failed_jobs = DB::table('failed_jobs')->where('failed_at', '<', now()->subDays(1));
|
|
||||||
$count = $failed_jobs->count();
|
|
||||||
echo "Delete $count entries from failed_jobs.\n";
|
|
||||||
if ($this->option('yes')) {
|
|
||||||
$failed_jobs->delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup sessions table
|
|
||||||
$sessions = DB::table('sessions')->where('last_activity', '<', now()->subDays($keep_days)->timestamp);
|
|
||||||
$count = $sessions->count();
|
|
||||||
echo "Delete $count entries from sessions.\n";
|
|
||||||
if ($this->option('yes')) {
|
|
||||||
$sessions->delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup activity_log table
|
|
||||||
$activity_log = DB::table('activity_log')->where('created_at', '<', now()->subDays($keep_days))->orderBy('created_at', 'desc')->skip(10);
|
|
||||||
$count = $activity_log->count();
|
|
||||||
echo "Delete $count entries from activity_log.\n";
|
|
||||||
if ($this->option('yes')) {
|
|
||||||
$activity_log->delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup application_deployment_queues table
|
|
||||||
$application_deployment_queues = DB::table('application_deployment_queues')->where('created_at', '<', now()->subDays($keep_days))->orderBy('created_at', 'desc')->skip(10);
|
|
||||||
$count = $application_deployment_queues->count();
|
|
||||||
echo "Delete $count entries from application_deployment_queues.\n";
|
|
||||||
if ($this->option('yes')) {
|
|
||||||
$application_deployment_queues->delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup webhooks table
|
|
||||||
$webhooks = DB::table('webhooks')->where('created_at', '<', now()->subDays($keep_days));
|
|
||||||
$count = $webhooks->count();
|
|
||||||
echo "Delete $count entries from webhooks.\n";
|
|
||||||
if ($this->option('yes')) {
|
|
||||||
$webhooks->delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,7 +8,6 @@ use Illuminate\Support\Facades\Redis;
|
|||||||
class CleanupQueue extends Command
|
class CleanupQueue extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'cleanup:queue';
|
protected $signature = 'cleanup:queue';
|
||||||
|
|
||||||
protected $description = 'Cleanup Queue';
|
protected $description = 'Cleanup Queue';
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
|
@ -7,9 +7,6 @@ use App\Models\ScheduledTask;
|
|||||||
use App\Models\Service;
|
use App\Models\Service;
|
||||||
use App\Models\ServiceApplication;
|
use App\Models\ServiceApplication;
|
||||||
use App\Models\ServiceDatabase;
|
use App\Models\ServiceDatabase;
|
||||||
use App\Models\StandaloneClickhouse;
|
|
||||||
use App\Models\StandaloneDragonfly;
|
|
||||||
use App\Models\StandaloneKeydb;
|
|
||||||
use App\Models\StandaloneMariadb;
|
use App\Models\StandaloneMariadb;
|
||||||
use App\Models\StandaloneMongodb;
|
use App\Models\StandaloneMongodb;
|
||||||
use App\Models\StandaloneMysql;
|
use App\Models\StandaloneMysql;
|
||||||
@ -20,7 +17,6 @@ use Illuminate\Console\Command;
|
|||||||
class CleanupStuckedResources extends Command
|
class CleanupStuckedResources extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'cleanup:stucked-resources';
|
protected $signature = 'cleanup:stucked-resources';
|
||||||
|
|
||||||
protected $description = 'Cleanup Stucked Resources';
|
protected $description = 'Cleanup Stucked Resources';
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
@ -29,7 +25,6 @@ class CleanupStuckedResources extends Command
|
|||||||
echo "Running cleanup stucked resources.\n";
|
echo "Running cleanup stucked resources.\n";
|
||||||
$this->cleanup_stucked_resources();
|
$this->cleanup_stucked_resources();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function cleanup_stucked_resources()
|
private function cleanup_stucked_resources()
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -60,33 +55,6 @@ class CleanupStuckedResources extends Command
|
|||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
echo "Error in cleaning stuck redis: {$e->getMessage()}\n";
|
echo "Error in cleaning stuck redis: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
$keydbs = StandaloneKeydb::withTrashed()->whereNotNull('deleted_at')->get();
|
|
||||||
foreach ($keydbs as $keydb) {
|
|
||||||
echo "Deleting stuck keydb: {$keydb->name}\n";
|
|
||||||
$keydb->forceDelete();
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
echo "Error in cleaning stuck keydb: {$e->getMessage()}\n";
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
$dragonflies = StandaloneDragonfly::withTrashed()->whereNotNull('deleted_at')->get();
|
|
||||||
foreach ($dragonflies as $dragonfly) {
|
|
||||||
echo "Deleting stuck dragonfly: {$dragonfly->name}\n";
|
|
||||||
$dragonfly->forceDelete();
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
echo "Error in cleaning stuck dragonfly: {$e->getMessage()}\n";
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
$clickhouses = StandaloneClickhouse::withTrashed()->whereNotNull('deleted_at')->get();
|
|
||||||
foreach ($clickhouses as $clickhouse) {
|
|
||||||
echo "Deleting stuck clickhouse: {$clickhouse->name}\n";
|
|
||||||
$clickhouse->forceDelete();
|
|
||||||
}
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
echo "Error in cleaning stuck clickhouse: {$e->getMessage()}\n";
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
$mongodbs = StandaloneMongodb::withTrashed()->whereNotNull('deleted_at')->get();
|
$mongodbs = StandaloneMongodb::withTrashed()->whereNotNull('deleted_at')->get();
|
||||||
foreach ($mongodbs as $mongodb) {
|
foreach ($mongodbs as $mongodb) {
|
||||||
@ -144,7 +112,7 @@ class CleanupStuckedResources extends Command
|
|||||||
try {
|
try {
|
||||||
$scheduled_tasks = ScheduledTask::all();
|
$scheduled_tasks = ScheduledTask::all();
|
||||||
foreach ($scheduled_tasks as $scheduled_task) {
|
foreach ($scheduled_tasks as $scheduled_task) {
|
||||||
if (! $scheduled_task->service && ! $scheduled_task->application) {
|
if (!$scheduled_task->service && !$scheduled_task->application) {
|
||||||
echo "Deleting stuck scheduledtask: {$scheduled_task->name}\n";
|
echo "Deleting stuck scheduledtask: {$scheduled_task->name}\n";
|
||||||
$scheduled_task->delete();
|
$scheduled_task->delete();
|
||||||
}
|
}
|
||||||
@ -157,22 +125,19 @@ class CleanupStuckedResources extends Command
|
|||||||
try {
|
try {
|
||||||
$applications = Application::all();
|
$applications = Application::all();
|
||||||
foreach ($applications as $application) {
|
foreach ($applications as $application) {
|
||||||
if (! data_get($application, 'environment')) {
|
if (!data_get($application, 'environment')) {
|
||||||
echo 'Application without environment: '.$application->name.'\n';
|
echo 'Application without environment: ' . $application->name . '\n';
|
||||||
$application->forceDelete();
|
$application->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! $application->destination()) {
|
if (!$application->destination()) {
|
||||||
echo 'Application without destination: '.$application->name.'\n';
|
echo 'Application without destination: ' . $application->name . '\n';
|
||||||
$application->forceDelete();
|
$application->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! data_get($application, 'destination.server')) {
|
if (!data_get($application, 'destination.server')) {
|
||||||
echo 'Application without server: '.$application->name.'\n';
|
echo 'Application without server: ' . $application->name . '\n';
|
||||||
$application->forceDelete();
|
$application->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,22 +147,19 @@ class CleanupStuckedResources extends Command
|
|||||||
try {
|
try {
|
||||||
$postgresqls = StandalonePostgresql::all()->where('id', '!=', 0);
|
$postgresqls = StandalonePostgresql::all()->where('id', '!=', 0);
|
||||||
foreach ($postgresqls as $postgresql) {
|
foreach ($postgresqls as $postgresql) {
|
||||||
if (! data_get($postgresql, 'environment')) {
|
if (!data_get($postgresql, 'environment')) {
|
||||||
echo 'Postgresql without environment: '.$postgresql->name.'\n';
|
echo 'Postgresql without environment: ' . $postgresql->name . '\n';
|
||||||
$postgresql->forceDelete();
|
$postgresql->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! $postgresql->destination()) {
|
if (!$postgresql->destination()) {
|
||||||
echo 'Postgresql without destination: '.$postgresql->name.'\n';
|
echo 'Postgresql without destination: ' . $postgresql->name . '\n';
|
||||||
$postgresql->forceDelete();
|
$postgresql->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! data_get($postgresql, 'destination.server')) {
|
if (!data_get($postgresql, 'destination.server')) {
|
||||||
echo 'Postgresql without server: '.$postgresql->name.'\n';
|
echo 'Postgresql without server: ' . $postgresql->name . '\n';
|
||||||
$postgresql->forceDelete();
|
$postgresql->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -207,22 +169,19 @@ class CleanupStuckedResources extends Command
|
|||||||
try {
|
try {
|
||||||
$redis = StandaloneRedis::all();
|
$redis = StandaloneRedis::all();
|
||||||
foreach ($redis as $redis) {
|
foreach ($redis as $redis) {
|
||||||
if (! data_get($redis, 'environment')) {
|
if (!data_get($redis, 'environment')) {
|
||||||
echo 'Redis without environment: '.$redis->name.'\n';
|
echo 'Redis without environment: ' . $redis->name . '\n';
|
||||||
$redis->forceDelete();
|
$redis->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! $redis->destination()) {
|
if (!$redis->destination()) {
|
||||||
echo 'Redis without destination: '.$redis->name.'\n';
|
echo 'Redis without destination: ' . $redis->name . '\n';
|
||||||
$redis->forceDelete();
|
$redis->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! data_get($redis, 'destination.server')) {
|
if (!data_get($redis, 'destination.server')) {
|
||||||
echo 'Redis without server: '.$redis->name.'\n';
|
echo 'Redis without server: ' . $redis->name . '\n';
|
||||||
$redis->forceDelete();
|
$redis->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -233,22 +192,19 @@ class CleanupStuckedResources extends Command
|
|||||||
try {
|
try {
|
||||||
$mongodbs = StandaloneMongodb::all();
|
$mongodbs = StandaloneMongodb::all();
|
||||||
foreach ($mongodbs as $mongodb) {
|
foreach ($mongodbs as $mongodb) {
|
||||||
if (! data_get($mongodb, 'environment')) {
|
if (!data_get($mongodb, 'environment')) {
|
||||||
echo 'Mongodb without environment: '.$mongodb->name.'\n';
|
echo 'Mongodb without environment: ' . $mongodb->name . '\n';
|
||||||
$mongodb->forceDelete();
|
$mongodb->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! $mongodb->destination()) {
|
if (!$mongodb->destination()) {
|
||||||
echo 'Mongodb without destination: '.$mongodb->name.'\n';
|
echo 'Mongodb without destination: ' . $mongodb->name . '\n';
|
||||||
$mongodb->forceDelete();
|
$mongodb->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! data_get($mongodb, 'destination.server')) {
|
if (!data_get($mongodb, 'destination.server')) {
|
||||||
echo 'Mongodb without server: '.$mongodb->name.'\n';
|
echo 'Mongodb without server: ' . $mongodb->name . '\n';
|
||||||
$mongodb->forceDelete();
|
$mongodb->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -259,22 +215,19 @@ class CleanupStuckedResources extends Command
|
|||||||
try {
|
try {
|
||||||
$mysqls = StandaloneMysql::all();
|
$mysqls = StandaloneMysql::all();
|
||||||
foreach ($mysqls as $mysql) {
|
foreach ($mysqls as $mysql) {
|
||||||
if (! data_get($mysql, 'environment')) {
|
if (!data_get($mysql, 'environment')) {
|
||||||
echo 'Mysql without environment: '.$mysql->name.'\n';
|
echo 'Mysql without environment: ' . $mysql->name . '\n';
|
||||||
$mysql->forceDelete();
|
$mysql->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! $mysql->destination()) {
|
if (!$mysql->destination()) {
|
||||||
echo 'Mysql without destination: '.$mysql->name.'\n';
|
echo 'Mysql without destination: ' . $mysql->name . '\n';
|
||||||
$mysql->forceDelete();
|
$mysql->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! data_get($mysql, 'destination.server')) {
|
if (!data_get($mysql, 'destination.server')) {
|
||||||
echo 'Mysql without server: '.$mysql->name.'\n';
|
echo 'Mysql without server: ' . $mysql->name . '\n';
|
||||||
$mysql->forceDelete();
|
$mysql->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -285,22 +238,19 @@ class CleanupStuckedResources extends Command
|
|||||||
try {
|
try {
|
||||||
$mariadbs = StandaloneMariadb::all();
|
$mariadbs = StandaloneMariadb::all();
|
||||||
foreach ($mariadbs as $mariadb) {
|
foreach ($mariadbs as $mariadb) {
|
||||||
if (! data_get($mariadb, 'environment')) {
|
if (!data_get($mariadb, 'environment')) {
|
||||||
echo 'Mariadb without environment: '.$mariadb->name.'\n';
|
echo 'Mariadb without environment: ' . $mariadb->name . '\n';
|
||||||
$mariadb->forceDelete();
|
$mariadb->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! $mariadb->destination()) {
|
if (!$mariadb->destination()) {
|
||||||
echo 'Mariadb without destination: '.$mariadb->name.'\n';
|
echo 'Mariadb without destination: ' . $mariadb->name . '\n';
|
||||||
$mariadb->forceDelete();
|
$mariadb->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! data_get($mariadb, 'destination.server')) {
|
if (!data_get($mariadb, 'destination.server')) {
|
||||||
echo 'Mariadb without server: '.$mariadb->name.'\n';
|
echo 'Mariadb without server: ' . $mariadb->name . '\n';
|
||||||
$mariadb->forceDelete();
|
$mariadb->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -311,22 +261,19 @@ class CleanupStuckedResources extends Command
|
|||||||
try {
|
try {
|
||||||
$services = Service::all();
|
$services = Service::all();
|
||||||
foreach ($services as $service) {
|
foreach ($services as $service) {
|
||||||
if (! data_get($service, 'environment')) {
|
if (!data_get($service, 'environment')) {
|
||||||
echo 'Service without environment: '.$service->name.'\n';
|
echo 'Service without environment: ' . $service->name . '\n';
|
||||||
$service->forceDelete();
|
$service->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! $service->destination()) {
|
if (!$service->destination()) {
|
||||||
echo 'Service without destination: '.$service->name.'\n';
|
echo 'Service without destination: ' . $service->name . '\n';
|
||||||
$service->forceDelete();
|
$service->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (! data_get($service, 'server')) {
|
if (!data_get($service, 'server')) {
|
||||||
echo 'Service without server: '.$service->name.'\n';
|
echo 'Service without server: ' . $service->name . '\n';
|
||||||
$service->forceDelete();
|
$service->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -336,10 +283,9 @@ class CleanupStuckedResources extends Command
|
|||||||
try {
|
try {
|
||||||
$serviceApplications = ServiceApplication::all();
|
$serviceApplications = ServiceApplication::all();
|
||||||
foreach ($serviceApplications as $service) {
|
foreach ($serviceApplications as $service) {
|
||||||
if (! data_get($service, 'service')) {
|
if (!data_get($service, 'service')) {
|
||||||
echo 'ServiceApplication without service: '.$service->name.'\n';
|
echo 'ServiceApplication without service: ' . $service->name . '\n';
|
||||||
$service->forceDelete();
|
$service->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -349,10 +295,9 @@ class CleanupStuckedResources extends Command
|
|||||||
try {
|
try {
|
||||||
$serviceDatabases = ServiceDatabase::all();
|
$serviceDatabases = ServiceDatabase::all();
|
||||||
foreach ($serviceDatabases as $service) {
|
foreach ($serviceDatabases as $service) {
|
||||||
if (! data_get($service, 'service')) {
|
if (!data_get($service, 'service')) {
|
||||||
echo 'ServiceDatabase without service: '.$service->name.'\n';
|
echo 'ServiceDatabase without service: ' . $service->name . '\n';
|
||||||
$service->forceDelete();
|
$service->forceDelete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,19 +8,17 @@ use Illuminate\Console\Command;
|
|||||||
class CleanupUnreachableServers extends Command
|
class CleanupUnreachableServers extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'cleanup:unreachable-servers';
|
protected $signature = 'cleanup:unreachable-servers';
|
||||||
|
protected $description = 'Cleanup Unreachable Servers (3 days)';
|
||||||
protected $description = 'Cleanup Unreachable Servers (7 days)';
|
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
echo "Running unreachable server cleanup...\n";
|
echo "Running unreachable server cleanup...\n";
|
||||||
$servers = Server::where('unreachable_count', 3)->where('unreachable_notification_sent', true)->where('updated_at', '<', now()->subDays(7))->get();
|
$servers = Server::where('unreachable_count', 3)->where('unreachable_notification_sent', true)->where('updated_at', '<', now()->subDays(3))->get();
|
||||||
if ($servers->count() > 0) {
|
if ($servers->count() > 0) {
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
echo "Cleanup unreachable server ($server->id) with name $server->name";
|
echo "Cleanup unreachable server ($server->id) with name $server->name";
|
||||||
// send_internal_notification("Server $server->name is unreachable for 7 days. Cleaning up...");
|
|
||||||
$server->update([
|
$server->update([
|
||||||
'ip' => '1.2.3.4',
|
'ip' => '1.2.3.4'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
33
app/Console/Commands/Cloud.php
Normal file
33
app/Console/Commands/Cloud.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use App\Models\Server;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
class Cloud extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'cloud:unused-servers';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Get Unused Servers from Cloud';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended',true)->each(function($server){
|
||||||
|
$this->info($server->name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,101 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use App\Models\Team;
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
|
|
||||||
class CloudCleanupSubscriptions extends Command
|
|
||||||
{
|
|
||||||
protected $signature = 'cloud:cleanup-subs';
|
|
||||||
|
|
||||||
protected $description = 'Cleanup subcriptions teams';
|
|
||||||
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
if (! isCloud()) {
|
|
||||||
$this->error('This command can only be run on cloud');
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ray()->clearAll();
|
|
||||||
$this->info('Cleaning up subcriptions teams');
|
|
||||||
$stripe = new \Stripe\StripeClient(config('subscription.stripe_api_key'));
|
|
||||||
|
|
||||||
$teams = Team::all()->filter(function ($team) {
|
|
||||||
return $team->id !== 0;
|
|
||||||
})->sortBy('id');
|
|
||||||
foreach ($teams as $team) {
|
|
||||||
if ($team) {
|
|
||||||
$this->info("Checking team {$team->id}");
|
|
||||||
}
|
|
||||||
if (! data_get($team, 'subscription')) {
|
|
||||||
$this->disableServers($team);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// If the team has no subscription id and the invoice is paid, we need to reset the invoice paid status
|
|
||||||
if (! (data_get($team, 'subscription.stripe_subscription_id'))) {
|
|
||||||
$this->info("Resetting invoice paid status for team {$team->id} {$team->name}");
|
|
||||||
|
|
||||||
$team->subscription->update([
|
|
||||||
'stripe_invoice_paid' => false,
|
|
||||||
'stripe_trial_already_ended' => false,
|
|
||||||
'stripe_subscription_id' => null,
|
|
||||||
]);
|
|
||||||
$this->disableServers($team);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
$subscription = $stripe->subscriptions->retrieve(data_get($team, 'subscription.stripe_subscription_id'), []);
|
|
||||||
$status = data_get($subscription, 'status');
|
|
||||||
if ($status === 'active' || $status === 'past_due') {
|
|
||||||
$team->subscription->update([
|
|
||||||
'stripe_invoice_paid' => true,
|
|
||||||
'stripe_trial_already_ended' => false,
|
|
||||||
]);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$this->info('Subscription status: '.$status);
|
|
||||||
$this->info('Subscription id: '.data_get($team, 'subscription.stripe_subscription_id'));
|
|
||||||
$confirm = $this->confirm('Do you want to cancel the subscription?', true);
|
|
||||||
if (! $confirm) {
|
|
||||||
$this->info("Skipping team {$team->id} {$team->name}");
|
|
||||||
} else {
|
|
||||||
$this->info("Cancelling subscription for team {$team->id} {$team->name}");
|
|
||||||
$team->subscription->update([
|
|
||||||
'stripe_invoice_paid' => false,
|
|
||||||
'stripe_trial_already_ended' => false,
|
|
||||||
'stripe_subscription_id' => null,
|
|
||||||
]);
|
|
||||||
$this->disableServers($team);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
$this->error($e->getMessage());
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function disableServers(Team $team)
|
|
||||||
{
|
|
||||||
foreach ($team->servers as $server) {
|
|
||||||
if ($server->settings->is_usable === true || $server->settings->is_reachable === true || $server->ip !== '1.2.3.4') {
|
|
||||||
$this->info("Disabling server {$server->id} {$server->name}");
|
|
||||||
$server->settings()->update([
|
|
||||||
'is_usable' => false,
|
|
||||||
'is_reachable' => false,
|
|
||||||
]);
|
|
||||||
$server->update([
|
|
||||||
'ip' => '1.2.3.4',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,48 +9,19 @@ use Illuminate\Support\Facades\Process;
|
|||||||
|
|
||||||
class Dev extends Command
|
class Dev extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'dev {--init} {--generate-openapi}';
|
protected $signature = 'dev:init';
|
||||||
|
protected $description = 'Init the app in dev mode';
|
||||||
protected $description = 'Helper commands for development.';
|
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
|
||||||
if ($this->option('init')) {
|
|
||||||
$this->init();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ($this->option('generate-openapi')) {
|
|
||||||
$this->generateOpenApi();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function generateOpenApi()
|
|
||||||
{
|
|
||||||
// Generate OpenAPI documentation
|
|
||||||
echo "Generating OpenAPI documentation.\n";
|
|
||||||
$process = Process::run(['/var/www/html/vendor/bin/openapi', 'app', '-o', 'openapi.yaml']);
|
|
||||||
$error = $process->errorOutput();
|
|
||||||
$error = preg_replace('/^.*an object literal,.*$/m', '', $error);
|
|
||||||
$error = preg_replace('/^\h*\v+/m', '', $error);
|
|
||||||
echo $error;
|
|
||||||
echo $process->output();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function init()
|
|
||||||
{
|
{
|
||||||
// Generate APP_KEY if not exists
|
// Generate APP_KEY if not exists
|
||||||
|
|
||||||
if (empty(env('APP_KEY'))) {
|
if (empty(env('APP_KEY'))) {
|
||||||
echo "Generating APP_KEY.\n";
|
echo "Generating APP_KEY.\n";
|
||||||
Artisan::call('key:generate');
|
Artisan::call('key:generate');
|
||||||
}
|
}
|
||||||
// Seed database if it's empty
|
// Seed database if it's empty
|
||||||
$settings = InstanceSettings::find(0);
|
$settings = InstanceSettings::find(0);
|
||||||
if (! $settings) {
|
if (!$settings) {
|
||||||
echo "Initializing instance, seeding database.\n";
|
echo "Initializing instance, seeding database.\n";
|
||||||
Artisan::call('migrate --seed');
|
Artisan::call('migrate --seed');
|
||||||
} else {
|
} else {
|
||||||
|
@ -15,7 +15,6 @@ use App\Notifications\Application\DeploymentSuccess;
|
|||||||
use App\Notifications\Application\StatusChanged;
|
use App\Notifications\Application\StatusChanged;
|
||||||
use App\Notifications\Database\BackupFailed;
|
use App\Notifications\Database\BackupFailed;
|
||||||
use App\Notifications\Database\BackupSuccess;
|
use App\Notifications\Database\BackupSuccess;
|
||||||
use App\Notifications\Database\DailyBackup;
|
|
||||||
use App\Notifications\Test;
|
use App\Notifications\Test;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
@ -47,9 +46,7 @@ class Emails extends Command
|
|||||||
* Execute the console command.
|
* Execute the console command.
|
||||||
*/
|
*/
|
||||||
private ?MailMessage $mail = null;
|
private ?MailMessage $mail = null;
|
||||||
|
|
||||||
private ?string $email = null;
|
private ?string $email = null;
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$type = select(
|
$type = select(
|
||||||
@ -57,8 +54,6 @@ class Emails extends Command
|
|||||||
options: [
|
options: [
|
||||||
'updates' => 'Send Update Email to all users',
|
'updates' => 'Send Update Email to all users',
|
||||||
'emails-test' => 'Test',
|
'emails-test' => 'Test',
|
||||||
'database-backup-statuses-daily' => 'Database - Backup Statuses (Daily)',
|
|
||||||
'application-deployment-success-daily' => 'Application - Deployment Success (Daily)',
|
|
||||||
'application-deployment-success' => 'Application - Deployment Success',
|
'application-deployment-success' => 'Application - Deployment Success',
|
||||||
'application-deployment-failed' => 'Application - Deployment Failed',
|
'application-deployment-failed' => 'Application - Deployment Failed',
|
||||||
'application-status-changed' => 'Application - Status Changed',
|
'application-status-changed' => 'Application - Status Changed',
|
||||||
@ -72,23 +67,18 @@ class Emails extends Command
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
$emailsGathered = ['realusers-before-trial', 'realusers-server-lost-connection'];
|
$emailsGathered = ['realusers-before-trial', 'realusers-server-lost-connection'];
|
||||||
if (isDev()) {
|
if (!in_array($type, $emailsGathered)) {
|
||||||
$this->email = 'test@example.com';
|
$this->email = text('Email Address to send to');
|
||||||
} else {
|
|
||||||
if (! in_array($type, $emailsGathered)) {
|
|
||||||
$this->email = text('Email Address to send to:');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
set_transanctional_email_settings();
|
set_transanctional_email_settings();
|
||||||
|
|
||||||
$this->mail = new MailMessage;
|
$this->mail = new MailMessage();
|
||||||
$this->mail->subject('Test Email');
|
$this->mail->subject("Test Email");
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
case 'updates':
|
case 'updates':
|
||||||
$teams = Team::all();
|
$teams = Team::all();
|
||||||
if (! $teams || $teams->isEmpty()) {
|
if (!$teams || $teams->isEmpty()) {
|
||||||
echo 'No teams found.'.PHP_EOL;
|
echo 'No teams found.' . PHP_EOL;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$emails = [];
|
$emails = [];
|
||||||
@ -100,56 +90,27 @@ class Emails extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$emails = array_unique($emails);
|
$emails = array_unique($emails);
|
||||||
$this->info('Sending to '.count($emails).' emails.');
|
$this->info("Sending to " . count($emails) . " emails.");
|
||||||
foreach ($emails as $email) {
|
foreach ($emails as $email) {
|
||||||
$this->info($email);
|
$this->info($email);
|
||||||
}
|
}
|
||||||
$confirmed = confirm('Are you sure?');
|
$confirmed = confirm('Are you sure?');
|
||||||
if ($confirmed) {
|
if ($confirmed) {
|
||||||
foreach ($emails as $email) {
|
foreach ($emails as $email) {
|
||||||
$this->mail = new MailMessage;
|
$this->mail = new MailMessage();
|
||||||
$this->mail->subject('One-click Services, Docker Compose support');
|
$this->mail->subject('One-click Services, Docker Compose support');
|
||||||
$unsubscribeUrl = route('unsubscribe.marketing.emails', [
|
$unsubscribeUrl = route('unsubscribe.marketing.emails', [
|
||||||
'token' => encrypt($email),
|
'token' => encrypt($email),
|
||||||
]);
|
]);
|
||||||
$this->mail->view('emails.updates', ['unsubscribeUrl' => $unsubscribeUrl]);
|
$this->mail->view('emails.updates',["unsubscribeUrl" => $unsubscribeUrl]);
|
||||||
$this->sendEmail($email);
|
$this->sendEmail($email);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'emails-test':
|
case 'emails-test':
|
||||||
$this->mail = (new Test)->toMail();
|
$this->mail = (new Test())->toMail();
|
||||||
$this->sendEmail();
|
$this->sendEmail();
|
||||||
break;
|
break;
|
||||||
case 'database-backup-statuses-daily':
|
|
||||||
$scheduled_backups = ScheduledDatabaseBackup::all();
|
|
||||||
$databases = collect();
|
|
||||||
foreach ($scheduled_backups as $scheduled_backup) {
|
|
||||||
$last_days_backups = $scheduled_backup->get_last_days_backup_status();
|
|
||||||
if ($last_days_backups->isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$failed = $last_days_backups->where('status', 'failed');
|
|
||||||
$database = $scheduled_backup->database;
|
|
||||||
$databases->put($database->name, [
|
|
||||||
'failed_count' => $failed->count(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
$this->mail = (new DailyBackup($databases))->toMail();
|
|
||||||
$this->sendEmail();
|
|
||||||
break;
|
|
||||||
case 'application-deployment-success-daily':
|
|
||||||
$applications = Application::all();
|
|
||||||
foreach ($applications as $application) {
|
|
||||||
$deployments = $application->get_last_days_deployments();
|
|
||||||
ray($deployments);
|
|
||||||
if ($deployments->isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$this->mail = (new DeploymentSuccess($application, 'test'))->toMail();
|
|
||||||
$this->sendEmail();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'application-deployment-success':
|
case 'application-deployment-success':
|
||||||
$application = Application::all()->first();
|
$application = Application::all()->first();
|
||||||
$this->mail = (new DeploymentSuccess($application, 'test'))->toMail();
|
$this->mail = (new DeploymentSuccess($application, 'test'))->toMail();
|
||||||
@ -158,7 +119,7 @@ class Emails extends Command
|
|||||||
case 'application-deployment-failed':
|
case 'application-deployment-failed':
|
||||||
$application = Application::all()->first();
|
$application = Application::all()->first();
|
||||||
$preview = ApplicationPreview::all()->first();
|
$preview = ApplicationPreview::all()->first();
|
||||||
if (! $preview) {
|
if (!$preview) {
|
||||||
$preview = ApplicationPreview::create([
|
$preview = ApplicationPreview::create([
|
||||||
'application_id' => $application->id,
|
'application_id' => $application->id,
|
||||||
'pull_request_id' => 1,
|
'pull_request_id' => 1,
|
||||||
@ -179,7 +140,7 @@ class Emails extends Command
|
|||||||
case 'backup-failed':
|
case 'backup-failed':
|
||||||
$backup = ScheduledDatabaseBackup::all()->first();
|
$backup = ScheduledDatabaseBackup::all()->first();
|
||||||
$db = StandalonePostgresql::all()->first();
|
$db = StandalonePostgresql::all()->first();
|
||||||
if (! $backup) {
|
if (!$backup) {
|
||||||
$backup = ScheduledDatabaseBackup::create([
|
$backup = ScheduledDatabaseBackup::create([
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
'frequency' => 'daily',
|
'frequency' => 'daily',
|
||||||
@ -189,14 +150,14 @@ class Emails extends Command
|
|||||||
'team_id' => 0,
|
'team_id' => 0,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
$output = 'Because of an error, the backup of the database '.$db->name.' failed.';
|
$output = 'Because of an error, the backup of the database ' . $db->name . ' failed.';
|
||||||
$this->mail = (new BackupFailed($backup, $db, $output))->toMail();
|
$this->mail = (new BackupFailed($backup, $db, $output))->toMail();
|
||||||
$this->sendEmail();
|
$this->sendEmail();
|
||||||
break;
|
break;
|
||||||
case 'backup-success':
|
case 'backup-success':
|
||||||
$backup = ScheduledDatabaseBackup::all()->first();
|
$backup = ScheduledDatabaseBackup::all()->first();
|
||||||
$db = StandalonePostgresql::all()->first();
|
$db = StandalonePostgresql::all()->first();
|
||||||
if (! $backup) {
|
if (!$backup) {
|
||||||
$backup = ScheduledDatabaseBackup::create([
|
$backup = ScheduledDatabaseBackup::create([
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
'frequency' => 'daily',
|
'frequency' => 'daily',
|
||||||
@ -224,7 +185,7 @@ class Emails extends Command
|
|||||||
// $this->sendEmail();
|
// $this->sendEmail();
|
||||||
// break;
|
// break;
|
||||||
case 'waitlist-invitation-link':
|
case 'waitlist-invitation-link':
|
||||||
$this->mail = new MailMessage;
|
$this->mail = new MailMessage();
|
||||||
$this->mail->view('emails.waitlist-invitation', [
|
$this->mail->view('emails.waitlist-invitation', [
|
||||||
'loginLink' => 'https://coolify.io',
|
'loginLink' => 'https://coolify.io',
|
||||||
]);
|
]);
|
||||||
@ -241,13 +202,12 @@ class Emails extends Command
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
case 'realusers-before-trial':
|
case 'realusers-before-trial':
|
||||||
$this->mail = new MailMessage;
|
$this->mail = new MailMessage();
|
||||||
$this->mail->view('emails.before-trial-conversion');
|
$this->mail->view('emails.before-trial-conversion');
|
||||||
$this->mail->subject('Trial period has been added for all subscription plans.');
|
$this->mail->subject('Trial period has been added for all subscription plans.');
|
||||||
$teams = Team::doesntHave('subscription')->where('id', '!=', 0)->get();
|
$teams = Team::doesntHave('subscription')->where('id', '!=', 0)->get();
|
||||||
if (! $teams || $teams->isEmpty()) {
|
if (!$teams || $teams->isEmpty()) {
|
||||||
echo 'No teams found.'.PHP_EOL;
|
echo 'No teams found.' . PHP_EOL;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$emails = [];
|
$emails = [];
|
||||||
@ -259,7 +219,7 @@ class Emails extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$emails = array_unique($emails);
|
$emails = array_unique($emails);
|
||||||
$this->info('Sending to '.count($emails).' emails.');
|
$this->info("Sending to " . count($emails) . " emails.");
|
||||||
foreach ($emails as $email) {
|
foreach ($emails as $email) {
|
||||||
$this->info($email);
|
$this->info($email);
|
||||||
}
|
}
|
||||||
@ -273,7 +233,7 @@ class Emails extends Command
|
|||||||
case 'realusers-server-lost-connection':
|
case 'realusers-server-lost-connection':
|
||||||
$serverId = text('Server Id');
|
$serverId = text('Server Id');
|
||||||
$server = Server::find($serverId);
|
$server = Server::find($serverId);
|
||||||
if (! $server) {
|
if (!$server) {
|
||||||
throw new Exception('Server not found');
|
throw new Exception('Server not found');
|
||||||
}
|
}
|
||||||
$admins = [];
|
$admins = [];
|
||||||
@ -283,23 +243,22 @@ class Emails extends Command
|
|||||||
$admins[] = $member->email;
|
$admins[] = $member->email;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->info('Sending to '.count($admins).' admins.');
|
$this->info('Sending to ' . count($admins) . ' admins.');
|
||||||
foreach ($admins as $admin) {
|
foreach ($admins as $admin) {
|
||||||
$this->info($admin);
|
$this->info($admin);
|
||||||
}
|
}
|
||||||
$this->mail = new MailMessage;
|
$this->mail = new MailMessage();
|
||||||
$this->mail->view('emails.server-lost-connection', [
|
$this->mail->view('emails.server-lost-connection', [
|
||||||
'name' => $server->name,
|
'name' => $server->name,
|
||||||
]);
|
]);
|
||||||
$this->mail->subject('Action required: Server '.$server->name.' lost connection.');
|
$this->mail->subject('Action required: Server ' . $server->name . ' lost connection.');
|
||||||
foreach ($admins as $email) {
|
foreach ($admins as $email) {
|
||||||
$this->sendEmail($email);
|
$this->sendEmail($email);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private function sendEmail(string $email = null)
|
||||||
private function sendEmail(?string $email = null)
|
|
||||||
{
|
{
|
||||||
if ($email) {
|
if ($email) {
|
||||||
$this->email = $email;
|
$this->email = $email;
|
||||||
@ -310,7 +269,7 @@ class Emails extends Command
|
|||||||
fn (Message $message) => $message
|
fn (Message $message) => $message
|
||||||
->to($this->email)
|
->to($this->email)
|
||||||
->subject($this->mail->subject)
|
->subject($this->mail->subject)
|
||||||
->html((string) $this->mail->render())
|
->html((string)$this->mail->render())
|
||||||
);
|
);
|
||||||
$this->info("Email sent to $this->email successfully. 📧");
|
$this->info("Email sent to $this->email successfully. 📧");
|
||||||
}
|
}
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
|
|
||||||
class Horizon extends Command
|
|
||||||
{
|
|
||||||
protected $signature = 'start:horizon';
|
|
||||||
|
|
||||||
protected $description = 'Start Horizon';
|
|
||||||
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
if (config('coolify.is_horizon_enabled')) {
|
|
||||||
$this->info('Horizon is enabled. Starting.');
|
|
||||||
$this->call('horizon');
|
|
||||||
exit(0);
|
|
||||||
} else {
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,11 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use App\Actions\Server\StopSentinel;
|
|
||||||
use App\Enums\ApplicationDeploymentStatus;
|
use App\Enums\ApplicationDeploymentStatus;
|
||||||
use App\Jobs\CleanupHelperContainersJob;
|
use App\Jobs\CleanupHelperContainersJob;
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
use App\Models\ApplicationDeploymentQueue;
|
||||||
use App\Models\Environment;
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
@ -17,31 +15,16 @@ use Illuminate\Support\Facades\Http;
|
|||||||
class Init extends Command
|
class Init extends Command
|
||||||
{
|
{
|
||||||
protected $signature = 'app:init {--full-cleanup} {--cleanup-deployments}';
|
protected $signature = 'app:init {--full-cleanup} {--cleanup-deployments}';
|
||||||
|
|
||||||
protected $description = 'Cleanup instance related stuffs';
|
protected $description = 'Cleanup instance related stuffs';
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$this->alive();
|
$this->alive();
|
||||||
get_public_ips();
|
|
||||||
if (version_compare('4.0.0-beta.312', config('version'), '<=')) {
|
|
||||||
$servers = Server::all();
|
|
||||||
foreach ($servers as $server) {
|
|
||||||
$server->settings->update(['is_metrics_enabled' => false]);
|
|
||||||
if ($server->isFunctional()) {
|
|
||||||
StopSentinel::dispatch($server);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$full_cleanup = $this->option('full-cleanup');
|
$full_cleanup = $this->option('full-cleanup');
|
||||||
$cleanup_deployments = $this->option('cleanup-deployments');
|
$cleanup_deployments = $this->option('cleanup-deployments');
|
||||||
|
|
||||||
$this->replace_slash_in_environment_name();
|
|
||||||
if ($cleanup_deployments) {
|
if ($cleanup_deployments) {
|
||||||
echo "Running cleanup deployments.\n";
|
echo "Running cleanup deployments.\n";
|
||||||
$this->cleanup_in_progress_application_deployments();
|
$this->cleanup_in_progress_application_deployments();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ($full_cleanup) {
|
if ($full_cleanup) {
|
||||||
@ -51,30 +34,25 @@ class Init extends Command
|
|||||||
$this->cleanup_stucked_helper_containers();
|
$this->cleanup_stucked_helper_containers();
|
||||||
$this->call('cleanup:queue');
|
$this->call('cleanup:queue');
|
||||||
$this->call('cleanup:stucked-resources');
|
$this->call('cleanup:stucked-resources');
|
||||||
if (! isCloud()) {
|
try {
|
||||||
try {
|
setup_dynamic_configuration();
|
||||||
$server = Server::find(0)->first();
|
} catch (\Throwable $e) {
|
||||||
$server->setupDynamicProxyConfiguration();
|
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
|
||||||
} catch (\Throwable $e) {
|
|
||||||
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$settings = InstanceSettings::get();
|
$settings = InstanceSettings::get();
|
||||||
if (! is_null(env('AUTOUPDATE', null))) {
|
if (!is_null(env('AUTOUPDATE', null))) {
|
||||||
if (env('AUTOUPDATE') == true) {
|
if (env('AUTOUPDATE') == true) {
|
||||||
$settings->update(['is_auto_update_enabled' => true]);
|
$settings->update(['is_auto_update_enabled' => true]);
|
||||||
} else {
|
} else {
|
||||||
$settings->update(['is_auto_update_enabled' => false]);
|
$settings->update(['is_auto_update_enabled' => false]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->cleanup_stucked_helper_containers();
|
$this->cleanup_stucked_helper_containers();
|
||||||
$this->call('cleanup:stucked-resources');
|
$this->call('cleanup:stucked-resources');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function restore_coolify_db_backup()
|
private function restore_coolify_db_backup()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@ -83,7 +61,7 @@ class Init extends Command
|
|||||||
echo "Restoring Last Hour Cloud db backup\n";
|
echo "Restoring Last Hour Cloud db backup\n";
|
||||||
$database->restore();
|
$database->restore();
|
||||||
$scheduledBackup = ScheduledDatabaseBackup::find(0);
|
$scheduledBackup = ScheduledDatabaseBackup::find(0);
|
||||||
if (! $scheduledBackup) {
|
if (!$scheduledBackup) {
|
||||||
ScheduledDatabaseBackup::create([
|
ScheduledDatabaseBackup::create([
|
||||||
'id' => 0,
|
'id' => 0,
|
||||||
'enabled' => true,
|
'enabled' => true,
|
||||||
@ -99,7 +77,6 @@ class Init extends Command
|
|||||||
echo "Error in restoring Last Hour Cloud db backup: {$e->getMessage()}\n";
|
echo "Error in restoring Last Hour Cloud db backup: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function cleanup_stucked_helper_containers()
|
private function cleanup_stucked_helper_containers()
|
||||||
{
|
{
|
||||||
$servers = Server::all();
|
$servers = Server::all();
|
||||||
@ -109,7 +86,6 @@ class Init extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function alive()
|
private function alive()
|
||||||
{
|
{
|
||||||
$id = config('app.id');
|
$id = config('app.id');
|
||||||
@ -118,7 +94,6 @@ class Init extends Command
|
|||||||
$do_not_track = data_get($settings, 'do_not_track');
|
$do_not_track = data_get($settings, 'do_not_track');
|
||||||
if ($do_not_track == true) {
|
if ($do_not_track == true) {
|
||||||
echo "Skipping alive as do_not_track is enabled\n";
|
echo "Skipping alive as do_not_track is enabled\n";
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@ -164,15 +139,4 @@ class Init extends Command
|
|||||||
echo "Error: {$e->getMessage()}\n";
|
echo "Error: {$e->getMessage()}\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function replace_slash_in_environment_name()
|
|
||||||
{
|
|
||||||
$environments = Environment::all();
|
|
||||||
foreach ($environments as $environment) {
|
|
||||||
if (str_contains($environment->name, '/')) {
|
|
||||||
$environment->name = str_replace('/', '-', $environment->name);
|
|
||||||
$environment->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
namespace App\Console\Commands;
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
use function Termwind\ask;
|
use function Termwind\ask;
|
||||||
use function Termwind\render;
|
use function Termwind\render;
|
||||||
use function Termwind\style;
|
use function Termwind\style;
|
||||||
@ -33,7 +32,6 @@ class NotifyDemo extends Command
|
|||||||
|
|
||||||
if (blank($channel)) {
|
if (blank($channel)) {
|
||||||
$this->showHelp();
|
$this->showHelp();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@ class RootChangeEmail extends Command
|
|||||||
$this->info('Root user\'s email updated successfully.');
|
$this->info('Root user\'s email updated successfully.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->error('Failed to update root user\'s email.');
|
$this->error('Failed to update root user\'s email.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,12 +29,12 @@ class RootResetPassword extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
//
|
||||||
$this->info('You are about to reset the root password.');
|
$this->info('You are about to reset the root password.');
|
||||||
$password = password('Give me a new password for root user: ');
|
$password = password('Give me a new password for root user: ');
|
||||||
$passwordAgain = password('Again');
|
$passwordAgain = password('Again');
|
||||||
if ($password != $passwordAgain) {
|
if ($password != $passwordAgain) {
|
||||||
$this->error('Passwords do not match.');
|
$this->error('Passwords do not match.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->info('Updating root password...');
|
$this->info('Updating root password...');
|
||||||
@ -43,7 +43,6 @@ class RootResetPassword extends Command
|
|||||||
$this->info('Root password updated successfully.');
|
$this->info('Root password updated successfully.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->error('Failed to update root password.');
|
$this->error('Failed to update root password.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Console\Commands;
|
|
||||||
|
|
||||||
use Illuminate\Console\Command;
|
|
||||||
|
|
||||||
class Scheduler extends Command
|
|
||||||
{
|
|
||||||
protected $signature = 'start:scheduler';
|
|
||||||
|
|
||||||
protected $description = 'Start Scheduler';
|
|
||||||
|
|
||||||
public function handle()
|
|
||||||
{
|
|
||||||
if (config('coolify.is_scheduler_enabled')) {
|
|
||||||
$this->info('Scheduler is enabled. Starting.');
|
|
||||||
$this->call('schedule:work');
|
|
||||||
exit(0);
|
|
||||||
} else {
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -48,13 +48,11 @@ class ServicesDelete extends Command
|
|||||||
$this->deleteServer();
|
$this->deleteServer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function deleteServer()
|
private function deleteServer()
|
||||||
{
|
{
|
||||||
$servers = Server::all();
|
$servers = Server::all();
|
||||||
if ($servers->count() === 0) {
|
if ($servers->count() === 0) {
|
||||||
$this->error('There are no applications to delete.');
|
$this->error('There are no applications to delete.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$serversToDelete = multiselect(
|
$serversToDelete = multiselect(
|
||||||
@ -66,21 +64,19 @@ class ServicesDelete extends Command
|
|||||||
$toDelete = $servers->where('id', $server)->first();
|
$toDelete = $servers->where('id', $server)->first();
|
||||||
if ($toDelete) {
|
if ($toDelete) {
|
||||||
$this->info($toDelete);
|
$this->info($toDelete);
|
||||||
$confirmed = confirm('Are you sure you want to delete all selected resources?');
|
$confirmed = confirm("Are you sure you want to delete all selected resources?");
|
||||||
if (! $confirmed) {
|
if (!$confirmed) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$toDelete->delete();
|
$toDelete->delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function deleteApplication()
|
private function deleteApplication()
|
||||||
{
|
{
|
||||||
$applications = Application::all();
|
$applications = Application::all();
|
||||||
if ($applications->count() === 0) {
|
if ($applications->count() === 0) {
|
||||||
$this->error('There are no applications to delete.');
|
$this->error('There are no applications to delete.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$applicationsToDelete = multiselect(
|
$applicationsToDelete = multiselect(
|
||||||
@ -92,21 +88,19 @@ class ServicesDelete extends Command
|
|||||||
$toDelete = $applications->where('id', $application)->first();
|
$toDelete = $applications->where('id', $application)->first();
|
||||||
if ($toDelete) {
|
if ($toDelete) {
|
||||||
$this->info($toDelete);
|
$this->info($toDelete);
|
||||||
$confirmed = confirm('Are you sure you want to delete all selected resources? ');
|
$confirmed = confirm("Are you sure you want to delete all selected resources? ");
|
||||||
if (! $confirmed) {
|
if (!$confirmed) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
DeleteResourceJob::dispatch($toDelete);
|
DeleteResourceJob::dispatch($toDelete);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function deleteDatabase()
|
private function deleteDatabase()
|
||||||
{
|
{
|
||||||
$databases = StandalonePostgresql::all();
|
$databases = StandalonePostgresql::all();
|
||||||
if ($databases->count() === 0) {
|
if ($databases->count() === 0) {
|
||||||
$this->error('There are no databases to delete.');
|
$this->error('There are no databases to delete.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$databasesToDelete = multiselect(
|
$databasesToDelete = multiselect(
|
||||||
@ -118,21 +112,19 @@ class ServicesDelete extends Command
|
|||||||
$toDelete = $databases->where('id', $database)->first();
|
$toDelete = $databases->where('id', $database)->first();
|
||||||
if ($toDelete) {
|
if ($toDelete) {
|
||||||
$this->info($toDelete);
|
$this->info($toDelete);
|
||||||
$confirmed = confirm('Are you sure you want to delete all selected resources?');
|
$confirmed = confirm("Are you sure you want to delete all selected resources?");
|
||||||
if (! $confirmed) {
|
if (!$confirmed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DeleteResourceJob::dispatch($toDelete);
|
DeleteResourceJob::dispatch($toDelete);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function deleteService()
|
private function deleteService()
|
||||||
{
|
{
|
||||||
$services = Service::all();
|
$services = Service::all();
|
||||||
if ($services->count() === 0) {
|
if ($services->count() === 0) {
|
||||||
$this->error('There are no services to delete.');
|
$this->error('There are no services to delete.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$servicesToDelete = multiselect(
|
$servicesToDelete = multiselect(
|
||||||
@ -144,8 +136,8 @@ class ServicesDelete extends Command
|
|||||||
$toDelete = $services->where('id', $service)->first();
|
$toDelete = $services->where('id', $service)->first();
|
||||||
if ($toDelete) {
|
if ($toDelete) {
|
||||||
$this->info($toDelete);
|
$this->info($toDelete);
|
||||||
$confirmed = confirm('Are you sure you want to delete all selected resources?');
|
$confirmed = confirm("Are you sure you want to delete all selected resources?");
|
||||||
if (! $confirmed) {
|
if (!$confirmed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DeleteResourceJob::dispatch($toDelete);
|
DeleteResourceJob::dispatch($toDelete);
|
||||||
|
@ -26,6 +26,7 @@ class ServicesGenerate extends Command
|
|||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
|
// ray()->clearAll();
|
||||||
$files = array_diff(scandir(base_path('templates/compose')), ['.', '..']);
|
$files = array_diff(scandir(base_path('templates/compose')), ['.', '..']);
|
||||||
$files = array_filter($files, function ($file) {
|
$files = array_filter($files, function ($file) {
|
||||||
return strpos($file, '.yaml') !== false;
|
return strpos($file, '.yaml') !== false;
|
||||||
@ -39,7 +40,7 @@ class ServicesGenerate extends Command
|
|||||||
$serviceTemplatesJson[$name] = $parsed;
|
$serviceTemplatesJson[$name] = $parsed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$serviceTemplatesJson = json_encode($serviceTemplatesJson);
|
$serviceTemplatesJson = json_encode($serviceTemplatesJson, JSON_PRETTY_PRINT);
|
||||||
file_put_contents(base_path('templates/service-templates.json'), $serviceTemplatesJson);
|
file_put_contents(base_path('templates/service-templates.json'), $serviceTemplatesJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,20 +51,18 @@ class ServicesGenerate extends Command
|
|||||||
// $this->info($content);
|
// $this->info($content);
|
||||||
$ignore = collect(preg_grep('/^# ignore:/', explode("\n", $content)))->values();
|
$ignore = collect(preg_grep('/^# ignore:/', explode("\n", $content)))->values();
|
||||||
if ($ignore->count() > 0) {
|
if ($ignore->count() > 0) {
|
||||||
$ignore = (bool) str($ignore[0])->after('# ignore:')->trim()->value();
|
$ignore = (bool)str($ignore[0])->after('# ignore:')->trim()->value();
|
||||||
} else {
|
} else {
|
||||||
$ignore = false;
|
$ignore = false;
|
||||||
}
|
}
|
||||||
if ($ignore) {
|
if ($ignore) {
|
||||||
$this->info("Ignoring $file");
|
$this->info("Ignoring $file");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->info("Processing $file");
|
$this->info("Processing $file");
|
||||||
$documentation = collect(preg_grep('/^# documentation:/', explode("\n", $content)))->values();
|
$documentation = collect(preg_grep('/^# documentation:/', explode("\n", $content)))->values();
|
||||||
if ($documentation->count() > 0) {
|
if ($documentation->count() > 0) {
|
||||||
$documentation = str($documentation[0])->after('# documentation:')->trim()->value();
|
$documentation = str($documentation[0])->after('# documentation:')->trim()->value();
|
||||||
$documentation = str($documentation)->append('?utm_source=coolify.io');
|
|
||||||
} else {
|
} else {
|
||||||
$documentation = 'https://coolify.io/docs';
|
$documentation = 'https://coolify.io/docs';
|
||||||
}
|
}
|
||||||
@ -74,18 +73,6 @@ class ServicesGenerate extends Command
|
|||||||
} else {
|
} else {
|
||||||
$slogan = str($file)->headline()->value();
|
$slogan = str($file)->headline()->value();
|
||||||
}
|
}
|
||||||
$logo = collect(preg_grep('/^# logo:/', explode("\n", $content)))->values();
|
|
||||||
if ($logo->count() > 0) {
|
|
||||||
$logo = str($logo[0])->after('# logo:')->trim()->value();
|
|
||||||
} else {
|
|
||||||
$logo = 'svgs/unknown.svg';
|
|
||||||
}
|
|
||||||
$minversion = collect(preg_grep('/^# minversion:/', explode("\n", $content)))->values();
|
|
||||||
if ($minversion->count() > 0) {
|
|
||||||
$minversion = str($minversion[0])->after('# minversion:')->trim()->value();
|
|
||||||
} else {
|
|
||||||
$minversion = '0.0.0';
|
|
||||||
}
|
|
||||||
$env_file = collect(preg_grep('/^# env_file:/', explode("\n", $content)))->values();
|
$env_file = collect(preg_grep('/^# env_file:/', explode("\n", $content)))->values();
|
||||||
if ($env_file->count() > 0) {
|
if ($env_file->count() > 0) {
|
||||||
$env_file = str($env_file[0])->after('# env_file:')->trim()->value();
|
$env_file = str($env_file[0])->after('# env_file:')->trim()->value();
|
||||||
@ -101,12 +88,6 @@ class ServicesGenerate extends Command
|
|||||||
} else {
|
} else {
|
||||||
$tags = null;
|
$tags = null;
|
||||||
}
|
}
|
||||||
$port = collect(preg_grep('/^# port:/', explode("\n", $content)))->values();
|
|
||||||
if ($port->count() > 0) {
|
|
||||||
$port = str($port[0])->after('# port:')->trim()->value();
|
|
||||||
} else {
|
|
||||||
$port = null;
|
|
||||||
}
|
|
||||||
$json = Yaml::parse($content);
|
$json = Yaml::parse($content);
|
||||||
$yaml = base64_encode(Yaml::dump($json, 10, 2));
|
$yaml = base64_encode(Yaml::dump($json, 10, 2));
|
||||||
$payload = [
|
$payload = [
|
||||||
@ -115,18 +96,12 @@ class ServicesGenerate extends Command
|
|||||||
'slogan' => $slogan,
|
'slogan' => $slogan,
|
||||||
'compose' => $yaml,
|
'compose' => $yaml,
|
||||||
'tags' => $tags,
|
'tags' => $tags,
|
||||||
'logo' => $logo,
|
|
||||||
'minversion' => $minversion,
|
|
||||||
];
|
];
|
||||||
if ($port) {
|
|
||||||
$payload['port'] = $port;
|
|
||||||
}
|
|
||||||
if ($env_file) {
|
if ($env_file) {
|
||||||
$env_file_content = file_get_contents(base_path("templates/compose/$env_file"));
|
$env_file_content = file_get_contents(base_path("templates/compose/$env_file"));
|
||||||
$env_file_base64 = base64_encode($env_file_content);
|
$env_file_base64 = base64_encode($env_file_content);
|
||||||
$payload['envs'] = $env_file_base64;
|
$payload['envs'] = $env_file_base64;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $payload;
|
return $payload;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,31 +33,30 @@ class SyncBunny extends Command
|
|||||||
$that = $this;
|
$that = $this;
|
||||||
$only_template = $this->option('templates');
|
$only_template = $this->option('templates');
|
||||||
$only_version = $this->option('release');
|
$only_version = $this->option('release');
|
||||||
$bunny_cdn = 'https://cdn.coollabs.io';
|
$bunny_cdn = "https://cdn.coollabs.io";
|
||||||
$bunny_cdn_path = 'coolify';
|
$bunny_cdn_path = "coolify";
|
||||||
$bunny_cdn_storage_name = 'coolcdn';
|
$bunny_cdn_storage_name = "coolcdn";
|
||||||
|
|
||||||
$parent_dir = realpath(dirname(__FILE__).'/../../..');
|
$parent_dir = realpath(dirname(__FILE__) . '/../../..');
|
||||||
|
|
||||||
$compose_file = 'docker-compose.yml';
|
$compose_file = "docker-compose.yml";
|
||||||
$compose_file_prod = 'docker-compose.prod.yml';
|
$compose_file_prod = "docker-compose.prod.yml";
|
||||||
$install_script = 'install.sh';
|
$install_script = "install.sh";
|
||||||
$upgrade_script = 'upgrade.sh';
|
$upgrade_script = "upgrade.sh";
|
||||||
$production_env = '.env.production';
|
$production_env = ".env.production";
|
||||||
$service_template = 'service-templates.json';
|
$service_template = "service-templates.json";
|
||||||
|
|
||||||
$versions = 'versions.json';
|
$versions = "versions.json";
|
||||||
|
|
||||||
PendingRequest::macro('storage', function ($fileName) use ($that) {
|
PendingRequest::macro('storage', function ($fileName) use ($that) {
|
||||||
$headers = [
|
$headers = [
|
||||||
'AccessKey' => env('BUNNY_STORAGE_API_KEY'),
|
'AccessKey' => env('BUNNY_STORAGE_API_KEY'),
|
||||||
'Accept' => 'application/json',
|
'Accept' => 'application/json',
|
||||||
'Content-Type' => 'application/octet-stream',
|
'Content-Type' => 'application/octet-stream'
|
||||||
];
|
];
|
||||||
$fileStream = fopen($fileName, 'r');
|
$fileStream = fopen($fileName, "r");
|
||||||
$file = fread($fileStream, filesize($fileName));
|
$file = fread($fileStream, filesize($fileName));
|
||||||
$that->info('Uploading: '.$fileName);
|
$that->info('Uploading: ' . $fileName);
|
||||||
|
|
||||||
return PendingRequest::baseUrl('https://storage.bunnycdn.com')->withHeaders($headers)->withBody($file)->throw();
|
return PendingRequest::baseUrl('https://storage.bunnycdn.com')->withHeaders($headers)->withBody($file)->throw();
|
||||||
});
|
});
|
||||||
PendingRequest::macro('purge', function ($url) use ($that) {
|
PendingRequest::macro('purge', function ($url) use ($that) {
|
||||||
@ -65,21 +64,20 @@ class SyncBunny extends Command
|
|||||||
'AccessKey' => env('BUNNY_API_KEY'),
|
'AccessKey' => env('BUNNY_API_KEY'),
|
||||||
'Accept' => 'application/json',
|
'Accept' => 'application/json',
|
||||||
];
|
];
|
||||||
$that->info('Purging: '.$url);
|
$that->info('Purging: ' . $url);
|
||||||
|
|
||||||
return PendingRequest::withHeaders($headers)->get('https://api.bunny.net/purge', [
|
return PendingRequest::withHeaders($headers)->get('https://api.bunny.net/purge', [
|
||||||
'url' => $url,
|
"url" => $url,
|
||||||
'async' => false,
|
"async" => false
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
if (! $only_template && ! $only_version) {
|
if (!$only_template && !$only_version) {
|
||||||
$this->info('About to sync files (docker-compose.prod.yaml, upgrade.sh, install.sh, etc) to BunnyCDN.');
|
$this->info('About to sync files (docker-compose.prod.yaml, upgrade.sh, install.sh, etc) to BunnyCDN.');
|
||||||
}
|
}
|
||||||
if ($only_template) {
|
if ($only_template) {
|
||||||
$this->info('About to sync service-templates.json to BunnyCDN.');
|
$this->info('About to sync service-templates.json to BunnyCDN.');
|
||||||
$confirmed = confirm('Are you sure you want to sync?');
|
$confirmed = confirm("Are you sure you want to sync?");
|
||||||
if (! $confirmed) {
|
if (!$confirmed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Http::pool(fn (Pool $pool) => [
|
Http::pool(fn (Pool $pool) => [
|
||||||
@ -87,16 +85,15 @@ class SyncBunny extends Command
|
|||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$service_template"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$service_template"),
|
||||||
]);
|
]);
|
||||||
$this->info('Service template uploaded & purged...');
|
$this->info('Service template uploaded & purged...');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} elseif ($only_version) {
|
} else if ($only_version) {
|
||||||
$this->info('About to sync versions.json to BunnyCDN.');
|
$this->info('About to sync versions.json to BunnyCDN.');
|
||||||
$file = file_get_contents("$parent_dir/$versions");
|
$file = file_get_contents("$parent_dir/$versions");
|
||||||
$json = json_decode($file, true);
|
$json = json_decode($file, true);
|
||||||
$actual_version = data_get($json, 'coolify.v4.version');
|
$actual_version = data_get($json, 'coolify.v4.version');
|
||||||
|
|
||||||
$confirmed = confirm("Are you sure you want to sync to {$actual_version}?");
|
$confirmed = confirm("Are you sure you want to sync to {$actual_version}?");
|
||||||
if (! $confirmed) {
|
if (!$confirmed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Http::pool(fn (Pool $pool) => [
|
Http::pool(fn (Pool $pool) => [
|
||||||
@ -104,10 +101,10 @@ class SyncBunny extends Command
|
|||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$versions"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$versions"),
|
||||||
]);
|
]);
|
||||||
$this->info('versions.json uploaded & purged...');
|
$this->info('versions.json uploaded & purged...');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Http::pool(fn (Pool $pool) => [
|
Http::pool(fn (Pool $pool) => [
|
||||||
$pool->storage(fileName: "$parent_dir/$compose_file")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file"),
|
$pool->storage(fileName: "$parent_dir/$compose_file")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file"),
|
||||||
$pool->storage(fileName: "$parent_dir/$compose_file_prod")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file_prod"),
|
$pool->storage(fileName: "$parent_dir/$compose_file_prod")->put("/$bunny_cdn_storage_name/$bunny_cdn_path/$compose_file_prod"),
|
||||||
@ -122,9 +119,9 @@ class SyncBunny extends Command
|
|||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$upgrade_script"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$upgrade_script"),
|
||||||
$pool->purge("$bunny_cdn/$bunny_cdn_path/$install_script"),
|
$pool->purge("$bunny_cdn/$bunny_cdn_path/$install_script"),
|
||||||
]);
|
]);
|
||||||
$this->info('All files uploaded & purged...');
|
$this->info("All files uploaded & purged...");
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->error('Error: '.$e->getMessage());
|
$this->error("Error: " . $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,7 @@ use Illuminate\Support\Str;
|
|||||||
class WaitlistInvite extends Command
|
class WaitlistInvite extends Command
|
||||||
{
|
{
|
||||||
public Waitlist|User|null $next_patient = null;
|
public Waitlist|User|null $next_patient = null;
|
||||||
|
public string|null $password = null;
|
||||||
public ?string $password = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name and signature of the console command.
|
* The name and signature of the console command.
|
||||||
*
|
*
|
||||||
@ -40,9 +38,7 @@ class WaitlistInvite extends Command
|
|||||||
$this->main();
|
$this->main();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private function main() {
|
||||||
private function main()
|
|
||||||
{
|
|
||||||
if ($this->argument('email')) {
|
if ($this->argument('email')) {
|
||||||
if ($this->option('only-email')) {
|
if ($this->option('only-email')) {
|
||||||
$this->next_patient = User::whereEmail($this->argument('email'))->first();
|
$this->next_patient = User::whereEmail($this->argument('email'))->first();
|
||||||
@ -54,9 +50,8 @@ class WaitlistInvite extends Command
|
|||||||
} else {
|
} else {
|
||||||
$this->next_patient = Waitlist::where('email', $this->argument('email'))->first();
|
$this->next_patient = Waitlist::where('email', $this->argument('email'))->first();
|
||||||
}
|
}
|
||||||
if (! $this->next_patient) {
|
if (!$this->next_patient) {
|
||||||
$this->error("{$this->argument('email')} not found in the waitlist.");
|
$this->error("{$this->argument('email')} not found in the waitlist.");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -65,7 +60,6 @@ class WaitlistInvite extends Command
|
|||||||
if ($this->next_patient) {
|
if ($this->next_patient) {
|
||||||
if ($this->option('only-email')) {
|
if ($this->option('only-email')) {
|
||||||
$this->send_email();
|
$this->send_email();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->register_user();
|
$this->register_user();
|
||||||
@ -75,14 +69,13 @@ class WaitlistInvite extends Command
|
|||||||
$this->info('No verified user found in the waitlist. 👀');
|
$this->info('No verified user found in the waitlist. 👀');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function register_user()
|
private function register_user()
|
||||||
{
|
{
|
||||||
$already_registered = User::whereEmail($this->next_patient->email)->first();
|
$already_registered = User::whereEmail($this->next_patient->email)->first();
|
||||||
if (! $already_registered) {
|
if (!$already_registered) {
|
||||||
$this->password = Str::password();
|
$this->password = Str::password();
|
||||||
User::create([
|
User::create([
|
||||||
'name' => str($this->next_patient->email)->before('@'),
|
'name' => Str::of($this->next_patient->email)->before('@'),
|
||||||
'email' => $this->next_patient->email,
|
'email' => $this->next_patient->email,
|
||||||
'password' => Hash::make($this->password),
|
'password' => Hash::make($this->password),
|
||||||
'force_password_reset' => true,
|
'force_password_reset' => true,
|
||||||
@ -92,23 +85,21 @@ class WaitlistInvite extends Command
|
|||||||
throw new \Exception('User already registered');
|
throw new \Exception('User already registered');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function remove_from_waitlist()
|
private function remove_from_waitlist()
|
||||||
{
|
{
|
||||||
$this->next_patient->delete();
|
$this->next_patient->delete();
|
||||||
$this->info('User removed from waitlist successfully.');
|
$this->info("User removed from waitlist successfully.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private function send_email()
|
private function send_email()
|
||||||
{
|
{
|
||||||
$token = Crypt::encryptString("{$this->next_patient->email}@@@$this->password");
|
$token = Crypt::encryptString("{$this->next_patient->email}@@@$this->password");
|
||||||
$loginLink = route('auth.link', ['token' => $token]);
|
$loginLink = route('auth.link', ['token' => $token]);
|
||||||
$mail = new MailMessage;
|
$mail = new MailMessage();
|
||||||
$mail->view('emails.waitlist-invitation', [
|
$mail->view('emails.waitlist-invitation', [
|
||||||
'loginLink' => $loginLink,
|
'loginLink' => $loginLink,
|
||||||
]);
|
]);
|
||||||
$mail->subject('Congratulations! You are invited to join Coolify Cloud.');
|
$mail->subject('Congratulations! You are invited to join Coolify Cloud.');
|
||||||
send_user_an_email($mail, $this->next_patient->email);
|
send_user_an_email($mail, $this->next_patient->email);
|
||||||
$this->info('Email sent successfully. 📧');
|
$this->info("Email sent successfully. 📧");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,14 @@ namespace App\Console;
|
|||||||
|
|
||||||
use App\Jobs\CheckLogDrainContainerJob;
|
use App\Jobs\CheckLogDrainContainerJob;
|
||||||
use App\Jobs\CleanupInstanceStuffsJob;
|
use App\Jobs\CleanupInstanceStuffsJob;
|
||||||
use App\Jobs\ContainerStatusJob;
|
use App\Jobs\ComplexContainerStatusJob;
|
||||||
use App\Jobs\DatabaseBackupJob;
|
use App\Jobs\DatabaseBackupJob;
|
||||||
use App\Jobs\DockerCleanupJob;
|
|
||||||
use App\Jobs\PullCoolifyImageJob;
|
|
||||||
use App\Jobs\PullHelperImageJob;
|
|
||||||
use App\Jobs\PullSentinelImageJob;
|
|
||||||
use App\Jobs\PullTemplatesFromCDN;
|
|
||||||
use App\Jobs\ScheduledTaskJob;
|
use App\Jobs\ScheduledTaskJob;
|
||||||
|
use App\Jobs\InstanceAutoUpdateJob;
|
||||||
|
use App\Jobs\ContainerStatusJob;
|
||||||
|
use App\Jobs\PullHelperImageJob;
|
||||||
use App\Jobs\ServerStatusJob;
|
use App\Jobs\ServerStatusJob;
|
||||||
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\ScheduledDatabaseBackup;
|
use App\Models\ScheduledDatabaseBackup;
|
||||||
use App\Models\ScheduledTask;
|
use App\Models\ScheduledTask;
|
||||||
use App\Models\Server;
|
use App\Models\Server;
|
||||||
@ -22,62 +21,51 @@ use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
|||||||
|
|
||||||
class Kernel extends ConsoleKernel
|
class Kernel extends ConsoleKernel
|
||||||
{
|
{
|
||||||
private $all_servers;
|
|
||||||
|
|
||||||
protected function schedule(Schedule $schedule): void
|
protected function schedule(Schedule $schedule): void
|
||||||
{
|
{
|
||||||
$this->all_servers = Server::all();
|
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
// Instance Jobs
|
// Instance Jobs
|
||||||
$schedule->command('horizon:snapshot')->everyMinute();
|
$schedule->command('horizon:snapshot')->everyMinute();
|
||||||
$schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer();
|
$schedule->job(new CleanupInstanceStuffsJob)->everyMinute()->onOneServer();
|
||||||
$schedule->job(new PullTemplatesFromCDN)->everyTwoHours()->onOneServer();
|
// $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
||||||
// Server Jobs
|
// Server Jobs
|
||||||
$this->check_scheduled_backups($schedule);
|
$this->check_scheduled_backups($schedule);
|
||||||
$this->check_resources($schedule);
|
$this->check_resources($schedule);
|
||||||
$this->check_scheduled_backups($schedule);
|
$this->check_scheduled_backups($schedule);
|
||||||
|
$this->pull_helper_image($schedule);
|
||||||
$this->check_scheduled_tasks($schedule);
|
$this->check_scheduled_tasks($schedule);
|
||||||
$schedule->command('uploads:clear')->everyTwoMinutes();
|
|
||||||
} else {
|
} else {
|
||||||
// Instance Jobs
|
// Instance Jobs
|
||||||
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
||||||
$schedule->command('cleanup:unreachable-servers')->daily();
|
$schedule->command('cleanup:unreachable-servers')->daily();
|
||||||
$schedule->job(new PullCoolifyImageJob)->everyTenMinutes()->onOneServer();
|
|
||||||
$schedule->job(new PullTemplatesFromCDN)->everyThirtyMinutes()->onOneServer();
|
|
||||||
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
|
$schedule->job(new CleanupInstanceStuffsJob)->everyTwoMinutes()->onOneServer();
|
||||||
// $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
// $schedule->job(new CheckResaleLicenseJob)->hourly()->onOneServer();
|
||||||
|
|
||||||
// Server Jobs
|
// Server Jobs
|
||||||
|
$this->instance_auto_update($schedule);
|
||||||
$this->check_scheduled_backups($schedule);
|
$this->check_scheduled_backups($schedule);
|
||||||
$this->check_resources($schedule);
|
$this->check_resources($schedule);
|
||||||
$this->pull_images($schedule);
|
$this->pull_helper_image($schedule);
|
||||||
$this->check_scheduled_tasks($schedule);
|
$this->check_scheduled_tasks($schedule);
|
||||||
|
|
||||||
$schedule->command('cleanup:database --yes')->daily();
|
|
||||||
$schedule->command('uploads:clear')->everyTwoMinutes();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private function pull_helper_image($schedule)
|
||||||
private function pull_images($schedule)
|
|
||||||
{
|
{
|
||||||
$servers = $this->all_servers->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
|
$servers = Server::all()->where('settings.is_usable', true)->where('settings.is_reachable', true)->where('ip', '!=', '1.2.3.4');
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
if ($server->isSentinelEnabled()) {
|
$schedule->job(new PullHelperImageJob($server))->everyTenMinutes()->onOneServer();
|
||||||
$schedule->job(new PullSentinelImageJob($server))->everyFiveMinutes()->onOneServer();
|
|
||||||
}
|
|
||||||
$schedule->job(new PullHelperImageJob($server))->everyFiveMinutes()->onOneServer();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function check_resources($schedule)
|
private function check_resources($schedule)
|
||||||
{
|
{
|
||||||
if (isCloud()) {
|
if (isCloud()) {
|
||||||
$servers = $this->all_servers->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false)->where('ip', '!=', '1.2.3.4');
|
$servers = Server::all()->whereNotNull('team.subscription')->where('team.subscription.stripe_trial_already_ended', false)->where('ip', '!=', '1.2.3.4');
|
||||||
$own = Team::find(0)->servers;
|
$own = Team::find(0)->servers;
|
||||||
$servers = $servers->merge($own);
|
$servers = $servers->merge($own);
|
||||||
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
|
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
|
||||||
} else {
|
} else {
|
||||||
$servers = $this->all_servers->where('ip', '!=', '1.2.3.4');
|
$servers = Server::all()->where('ip', '!=', '1.2.3.4');
|
||||||
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
|
$containerServers = $servers->where('settings.is_swarm_worker', false)->where('settings.is_build_server', false);
|
||||||
}
|
}
|
||||||
foreach ($containerServers as $server) {
|
foreach ($containerServers as $server) {
|
||||||
@ -88,10 +76,18 @@ class Kernel extends ConsoleKernel
|
|||||||
}
|
}
|
||||||
foreach ($servers as $server) {
|
foreach ($servers as $server) {
|
||||||
$schedule->job(new ServerStatusJob($server))->everyMinute()->onOneServer();
|
$schedule->job(new ServerStatusJob($server))->everyMinute()->onOneServer();
|
||||||
$schedule->job(new DockerCleanupJob($server))->everyTenMinutes()->onOneServer();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private function instance_auto_update($schedule)
|
||||||
|
{
|
||||||
|
if (isDev()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$settings = InstanceSettings::get();
|
||||||
|
if ($settings->is_auto_update_enabled) {
|
||||||
|
$schedule->job(new InstanceAutoUpdateJob)->everyTenMinutes()->onOneServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
private function check_scheduled_backups($schedule)
|
private function check_scheduled_backups($schedule)
|
||||||
{
|
{
|
||||||
$scheduled_backups = ScheduledDatabaseBackup::all();
|
$scheduled_backups = ScheduledDatabaseBackup::all();
|
||||||
@ -99,13 +95,12 @@ class Kernel extends ConsoleKernel
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
foreach ($scheduled_backups as $scheduled_backup) {
|
foreach ($scheduled_backups as $scheduled_backup) {
|
||||||
if (! $scheduled_backup->enabled) {
|
if (!$scheduled_backup->enabled) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (is_null(data_get($scheduled_backup, 'database'))) {
|
if (is_null(data_get($scheduled_backup, 'database'))) {
|
||||||
ray('database not found');
|
ray('database not found');
|
||||||
$scheduled_backup->delete();
|
$scheduled_backup->delete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,28 +120,15 @@ class Kernel extends ConsoleKernel
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
foreach ($scheduled_tasks as $scheduled_task) {
|
foreach ($scheduled_tasks as $scheduled_task) {
|
||||||
if ($scheduled_task->enabled === false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$service = $scheduled_task->service;
|
$service = $scheduled_task->service;
|
||||||
$application = $scheduled_task->application;
|
$application = $scheduled_task->application;
|
||||||
|
|
||||||
if (! $application && ! $service) {
|
if (!$application && !$service) {
|
||||||
ray('application/service attached to scheduled task does not exist');
|
ray('application/service attached to scheduled task does not exist');
|
||||||
$scheduled_task->delete();
|
$scheduled_task->delete();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ($application) {
|
|
||||||
if (str($application->status)->contains('running') === false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($service) {
|
|
||||||
if (str($service->status())->contains('running') === false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isset(VALID_CRON_STRINGS[$scheduled_task->frequency])) {
|
if (isset(VALID_CRON_STRINGS[$scheduled_task->frequency])) {
|
||||||
$scheduled_task->frequency = VALID_CRON_STRINGS[$scheduled_task->frequency];
|
$scheduled_task->frequency = VALID_CRON_STRINGS[$scheduled_task->frequency];
|
||||||
}
|
}
|
||||||
@ -158,7 +140,7 @@ class Kernel extends ConsoleKernel
|
|||||||
|
|
||||||
protected function commands(): void
|
protected function commands(): void
|
||||||
{
|
{
|
||||||
$this->load(__DIR__.'/Commands');
|
$this->load(__DIR__ . '/Commands');
|
||||||
|
|
||||||
require base_path('routes/console.php');
|
require base_path('routes/console.php');
|
||||||
}
|
}
|
||||||
|
@ -12,18 +12,17 @@ use Spatie\LaravelData\Data;
|
|||||||
class CoolifyTaskArgs extends Data
|
class CoolifyTaskArgs extends Data
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public string $server_uuid,
|
public string $server_uuid,
|
||||||
public string $command,
|
public string $command,
|
||||||
public string $type,
|
public string $type,
|
||||||
public ?string $type_uuid = null,
|
public ?string $type_uuid = null,
|
||||||
public ?int $process_id = null,
|
public ?int $process_id = null,
|
||||||
public ?Model $model = null,
|
public ?Model $model = null,
|
||||||
public ?string $status = null,
|
public ?string $status = null ,
|
||||||
public bool $ignore_errors = false,
|
public bool $ignore_errors = false,
|
||||||
public $call_event_on_finish = null,
|
public $call_event_on_finish = null,
|
||||||
public $call_event_data = null
|
|
||||||
) {
|
) {
|
||||||
if (is_null($status)) {
|
if(is_null($status)){
|
||||||
$this->status = ProcessStatus::QUEUED->value;
|
$this->status = ProcessStatus::QUEUED->value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,8 @@ use Spatie\LaravelData\Data;
|
|||||||
class ServerMetadata extends Data
|
class ServerMetadata extends Data
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public ?ProxyTypes $type,
|
public ?ProxyTypes $type,
|
||||||
public ?ProxyStatus $status
|
public ?ProxyStatus $status
|
||||||
) {}
|
) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,5 +5,4 @@ namespace App\Enums;
|
|||||||
enum ActivityTypes: string
|
enum ActivityTypes: string
|
||||||
{
|
{
|
||||||
case INLINE = 'inline';
|
case INLINE = 'inline';
|
||||||
case COMMAND = 'command';
|
|
||||||
}
|
}
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Enums;
|
|
||||||
|
|
||||||
enum BuildPackTypes: string
|
|
||||||
{
|
|
||||||
case NIXPACKS = 'nixpacks';
|
|
||||||
case STATIC = 'static';
|
|
||||||
case DOCKERFILE = 'dockerfile';
|
|
||||||
case DOCKERCOMPOSE = 'dockercompose';
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Enums;
|
|
||||||
|
|
||||||
enum NewDatabaseTypes: string
|
|
||||||
{
|
|
||||||
case POSTGRESQL = 'postgresql';
|
|
||||||
case MYSQL = 'mysql';
|
|
||||||
case MONGODB = 'mongodb';
|
|
||||||
case REDIS = 'redis';
|
|
||||||
case MARIADB = 'mariadb';
|
|
||||||
case KEYDB = 'keydb';
|
|
||||||
case DRAGONFLY = 'dragonfly';
|
|
||||||
case CLICKHOUSE = 'clickhouse';
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Enums;
|
|
||||||
|
|
||||||
enum NewResourceTypes: string
|
|
||||||
{
|
|
||||||
case PUBLIC = 'public';
|
|
||||||
case PRIVATE_GH_APP = 'private-gh-app';
|
|
||||||
case PRIVATE_DEPLOY_KEY = 'private-deploy-key';
|
|
||||||
case DOCKERFILE = 'dockerfile';
|
|
||||||
case DOCKERCOMPOSE = 'dockercompose';
|
|
||||||
case DOCKER_IMAGE = 'docker-image';
|
|
||||||
case SERVICE = 'service';
|
|
||||||
case POSTGRESQL = 'postgresql';
|
|
||||||
case MYSQL = 'mysql';
|
|
||||||
case MONGODB = 'mongodb';
|
|
||||||
case REDIS = 'redis';
|
|
||||||
case MARIADB = 'mariadb';
|
|
||||||
case KEYDB = 'keydb';
|
|
||||||
case DRAGONFLY = 'dragonfly';
|
|
||||||
case CLICKHOUSE = 'clickhouse';
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Enums;
|
|
||||||
|
|
||||||
enum RedirectTypes: string
|
|
||||||
{
|
|
||||||
case BOTH = 'both';
|
|
||||||
case WWW = 'www';
|
|
||||||
case NON_WWW = 'non-www';
|
|
||||||
}
|
|
@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Events;
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PresenceChannel;
|
||||||
use Illuminate\Broadcasting\PrivateChannel;
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
use Illuminate\Foundation\Events\Dispatchable;
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
@ -11,16 +13,14 @@ use Illuminate\Queue\SerializesModels;
|
|||||||
class ApplicationStatusChanged implements ShouldBroadcast
|
class ApplicationStatusChanged implements ShouldBroadcast
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
public $teamId;
|
public $teamId;
|
||||||
|
|
||||||
public function __construct($teamId = null)
|
public function __construct($teamId = null)
|
||||||
{
|
{
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId)) {
|
||||||
$teamId = auth()->user()->currentTeam()->id ?? null;
|
$teamId = auth()->user()->currentTeam()->id ?? null;
|
||||||
}
|
}
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId)) {
|
||||||
throw new \Exception('Team id is null');
|
throw new \Exception("Team id is null");
|
||||||
}
|
}
|
||||||
$this->teamId = $teamId;
|
$this->teamId = $teamId;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Events;
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PresenceChannel;
|
||||||
use Illuminate\Broadcasting\PrivateChannel;
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
use Illuminate\Foundation\Events\Dispatchable;
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
@ -11,16 +13,14 @@ use Illuminate\Queue\SerializesModels;
|
|||||||
class BackupCreated implements ShouldBroadcast
|
class BackupCreated implements ShouldBroadcast
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
public $teamId;
|
public $teamId;
|
||||||
|
|
||||||
public function __construct($teamId = null)
|
public function __construct($teamId = null)
|
||||||
{
|
{
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId)) {
|
||||||
$teamId = auth()->user()->currentTeam()->id ?? null;
|
$teamId = auth()->user()->currentTeam()->id ?? null;
|
||||||
}
|
}
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId)) {
|
||||||
throw new \Exception('Team id is null');
|
throw new \Exception("Team id is null");
|
||||||
}
|
}
|
||||||
$this->teamId = $teamId;
|
$this->teamId = $teamId;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Events;
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PresenceChannel;
|
||||||
use Illuminate\Broadcasting\PrivateChannel;
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
use Illuminate\Foundation\Events\Dispatchable;
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
@ -11,28 +13,22 @@ use Illuminate\Queue\SerializesModels;
|
|||||||
class DatabaseStatusChanged implements ShouldBroadcast
|
class DatabaseStatusChanged implements ShouldBroadcast
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
public $userId;
|
||||||
public ?string $userId = null;
|
|
||||||
|
|
||||||
public function __construct($userId = null)
|
public function __construct($userId = null)
|
||||||
{
|
{
|
||||||
if (is_null($userId)) {
|
if (is_null($userId)) {
|
||||||
$userId = auth()->user()->id ?? null;
|
$userId = auth()->user()->id ?? null;
|
||||||
}
|
}
|
||||||
if (is_null($userId)) {
|
if (is_null($userId)) {
|
||||||
return false;
|
throw new \Exception("User id is null");
|
||||||
}
|
}
|
||||||
$this->userId = $userId;
|
$this->userId = $userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function broadcastOn(): ?array
|
public function broadcastOn(): array
|
||||||
{
|
{
|
||||||
if ($this->userId) {
|
return [
|
||||||
return [
|
new PrivateChannel("user.{$this->userId}"),
|
||||||
new PrivateChannel("user.{$this->userId}"),
|
];
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Events;
|
|
||||||
|
|
||||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
|
||||||
use Illuminate\Foundation\Events\Dispatchable;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
|
|
||||||
class ProxyStarted
|
|
||||||
{
|
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
|
||||||
|
|
||||||
public function __construct(public $data) {}
|
|
||||||
}
|
|
@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Events;
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PresenceChannel;
|
||||||
use Illuminate\Broadcasting\PrivateChannel;
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
use Illuminate\Foundation\Events\Dispatchable;
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
@ -11,16 +13,14 @@ use Illuminate\Queue\SerializesModels;
|
|||||||
class ProxyStatusChanged implements ShouldBroadcast
|
class ProxyStatusChanged implements ShouldBroadcast
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
public $teamId;
|
public $teamId;
|
||||||
|
|
||||||
public function __construct($teamId = null)
|
public function __construct($teamId = null)
|
||||||
{
|
{
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId)) {
|
||||||
$teamId = auth()->user()->currentTeam()->id ?? null;
|
$teamId = auth()->user()->currentTeam()->id ?? null;
|
||||||
}
|
}
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId)) {
|
||||||
throw new \Exception('Team id is null');
|
throw new \Exception("Team id is null");
|
||||||
}
|
}
|
||||||
$this->teamId = $teamId;
|
$this->teamId = $teamId;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Events;
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PresenceChannel;
|
||||||
use Illuminate\Broadcasting\PrivateChannel;
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
use Illuminate\Foundation\Events\Dispatchable;
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
@ -11,28 +13,22 @@ use Illuminate\Queue\SerializesModels;
|
|||||||
class ServiceStatusChanged implements ShouldBroadcast
|
class ServiceStatusChanged implements ShouldBroadcast
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
public $userId;
|
||||||
public ?string $userId = null;
|
|
||||||
|
|
||||||
public function __construct($userId = null)
|
public function __construct($userId = null)
|
||||||
{
|
{
|
||||||
if (is_null($userId)) {
|
if (is_null($userId)) {
|
||||||
$userId = auth()->user()->id ?? null;
|
$userId = auth()->user()->id ?? null;
|
||||||
}
|
}
|
||||||
if (is_null($userId)) {
|
if (is_null($userId)) {
|
||||||
return false;
|
throw new \Exception("User id is null");
|
||||||
}
|
}
|
||||||
$this->userId = $userId;
|
$this->userId = $userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function broadcastOn(): ?array
|
public function broadcastOn(): array
|
||||||
{
|
{
|
||||||
if (! is_null($this->userId)) {
|
return [
|
||||||
return [
|
new PrivateChannel("user.{$this->userId}"),
|
||||||
new PrivateChannel("user.{$this->userId}"),
|
];
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Events;
|
namespace App\Events;
|
||||||
|
|
||||||
|
use Illuminate\Broadcasting\Channel;
|
||||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
|
use Illuminate\Broadcasting\PresenceChannel;
|
||||||
use Illuminate\Broadcasting\PrivateChannel;
|
use Illuminate\Broadcasting\PrivateChannel;
|
||||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
use Illuminate\Foundation\Events\Dispatchable;
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
@ -11,9 +13,7 @@ use Illuminate\Queue\SerializesModels;
|
|||||||
class TestEvent implements ShouldBroadcast
|
class TestEvent implements ShouldBroadcast
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
public $teamId;
|
public $teamId;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->teamId = auth()->user()->currentTeam()->id;
|
$this->teamId = auth()->user()->currentTeam()->id;
|
||||||
|
@ -13,6 +13,7 @@ use Throwable;
|
|||||||
|
|
||||||
class Handler extends ExceptionHandler
|
class Handler extends ExceptionHandler
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of exception types with their corresponding custom log levels.
|
* A list of exception types with their corresponding custom log levels.
|
||||||
*
|
*
|
||||||
@ -21,16 +22,14 @@ class Handler extends ExceptionHandler
|
|||||||
protected $levels = [
|
protected $levels = [
|
||||||
//
|
//
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of the exception types that are not reported.
|
* A list of the exception types that are not reported.
|
||||||
*
|
*
|
||||||
* @var array<int, class-string<\Throwable>>
|
* @var array<int, class-string<\Throwable>>
|
||||||
*/
|
*/
|
||||||
protected $dontReport = [
|
protected $dontReport = [
|
||||||
ProcessException::class,
|
ProcessException::class
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of the inputs that are never flashed to the session on validation exceptions.
|
* A list of the inputs that are never flashed to the session on validation exceptions.
|
||||||
*
|
*
|
||||||
@ -41,7 +40,6 @@ class Handler extends ExceptionHandler
|
|||||||
'password',
|
'password',
|
||||||
'password_confirmation',
|
'password_confirmation',
|
||||||
];
|
];
|
||||||
|
|
||||||
private InstanceSettings $settings;
|
private InstanceSettings $settings;
|
||||||
|
|
||||||
protected function unauthenticated($request, AuthenticationException $exception)
|
protected function unauthenticated($request, AuthenticationException $exception)
|
||||||
@ -49,10 +47,8 @@ class Handler extends ExceptionHandler
|
|||||||
if ($request->is('api/*') || $request->expectsJson() || $this->shouldReturnJson($request, $exception)) {
|
if ($request->is('api/*') || $request->expectsJson() || $this->shouldReturnJson($request, $exception)) {
|
||||||
return response()->json(['message' => $exception->getMessage()], 401);
|
return response()->json(['message' => $exception->getMessage()], 401);
|
||||||
}
|
}
|
||||||
|
return redirect()->guest($exception->redirectTo() ?? route('login'));
|
||||||
return redirect()->guest($exception->redirectTo($request) ?? route('login'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the exception handling callbacks for the application.
|
* Register the exception handling callbacks for the application.
|
||||||
*/
|
*/
|
||||||
@ -60,12 +56,12 @@ class Handler extends ExceptionHandler
|
|||||||
{
|
{
|
||||||
$this->reportable(function (Throwable $e) {
|
$this->reportable(function (Throwable $e) {
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
return;
|
// return;
|
||||||
}
|
}
|
||||||
if ($e instanceof RuntimeException) {
|
if ($e instanceof RuntimeException) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->settings = \App\Models\InstanceSettings::get();
|
$this->settings = InstanceSettings::get();
|
||||||
if ($this->settings->do_not_track) {
|
if ($this->settings->do_not_track) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -76,14 +72,11 @@ class Handler extends ExceptionHandler
|
|||||||
$scope->setUser(
|
$scope->setUser(
|
||||||
[
|
[
|
||||||
'email' => $email,
|
'email' => $email,
|
||||||
'instanceAdmin' => $instanceAdmin,
|
'instanceAdmin' => $instanceAdmin
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if (str($e->getMessage())->contains('No space left on device')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ray('reporting to sentry');
|
ray('reporting to sentry');
|
||||||
Integration::captureUnhandledException($e);
|
Integration::captureUnhandledException($e);
|
||||||
});
|
});
|
||||||
|
@ -4,4 +4,7 @@ namespace App\Exceptions;
|
|||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
|
||||||
class ProcessException extends Exception {}
|
class ProcessException extends Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -9,33 +9,13 @@ use App\Actions\Database\StartPostgresql;
|
|||||||
use App\Actions\Database\StartRedis;
|
use App\Actions\Database\StartRedis;
|
||||||
use App\Actions\Service\StartService;
|
use App\Actions\Service\StartService;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
|
||||||
use App\Models\Server;
|
|
||||||
use App\Models\Tag;
|
use App\Models\Tag;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
|
||||||
class Deploy extends Controller
|
class Deploy extends Controller
|
||||||
{
|
{
|
||||||
public function deployments(Request $request)
|
|
||||||
{
|
|
||||||
$teamId = get_team_id_from_token();
|
|
||||||
if (is_null($teamId)) {
|
|
||||||
return invalid_token();
|
|
||||||
}
|
|
||||||
$servers = Server::whereTeamId($teamId)->get();
|
|
||||||
$deployments_per_server = ApplicationDeploymentQueue::whereIn("status", ["in_progress", "queued"])->whereIn("server_id", $servers->pluck("id"))->get([
|
|
||||||
"id",
|
|
||||||
"application_id",
|
|
||||||
"application_name",
|
|
||||||
"deployment_url",
|
|
||||||
"pull_request_id",
|
|
||||||
"server_name",
|
|
||||||
"server_id",
|
|
||||||
"status"
|
|
||||||
])->sortBy('id')->toArray();
|
|
||||||
return response()->json($deployments_per_server, 200);
|
|
||||||
}
|
|
||||||
public function deploy(Request $request)
|
public function deploy(Request $request)
|
||||||
{
|
{
|
||||||
$teamId = get_team_id_from_token();
|
$teamId = get_team_id_from_token();
|
||||||
@ -47,7 +27,7 @@ class Deploy extends Controller
|
|||||||
return response()->json(['error' => 'You can only use uuid or tag, not both.', 'upstream docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400);
|
return response()->json(['error' => 'You can only use uuid or tag, not both.', 'upstream docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400);
|
||||||
}
|
}
|
||||||
if (is_null($teamId)) {
|
if (is_null($teamId)) {
|
||||||
return invalid_token();
|
return response()->json(['error' => 'Invalid token.', 'upstream docs' => 'https://coolify.io/docs/api/authentication'], 400);
|
||||||
}
|
}
|
||||||
if ($tags) {
|
if ($tags) {
|
||||||
return $this->by_tags($tags, $teamId, $force);
|
return $this->by_tags($tags, $teamId, $force);
|
||||||
@ -64,22 +44,16 @@ class Deploy extends Controller
|
|||||||
if (count($uuids) === 0) {
|
if (count($uuids) === 0) {
|
||||||
return response()->json(['error' => 'No UUIDs provided.', 'upstream docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400);
|
return response()->json(['error' => 'No UUIDs provided.', 'upstream docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400);
|
||||||
}
|
}
|
||||||
$deployments = collect();
|
$message = collect([]);
|
||||||
$payload = collect();
|
|
||||||
foreach ($uuids as $uuid) {
|
foreach ($uuids as $uuid) {
|
||||||
$resource = getResourceByUuid($uuid, $teamId);
|
$resource = getResourceByUuid($uuid, $teamId);
|
||||||
if ($resource) {
|
if ($resource) {
|
||||||
['message' => $return_message, 'deployment_uuid' => $deployment_uuid] = $this->deploy_resource($resource, $force);
|
$return_message = $this->deploy_resource($resource, $force);
|
||||||
if ($deployment_uuid) {
|
$message = $message->merge($return_message);
|
||||||
$deployments->push(['message' => $return_message, 'resource_uuid' => $uuid, 'deployment_uuid' => $deployment_uuid->toString()]);
|
|
||||||
} else {
|
|
||||||
$deployments->push(['message' => $return_message, 'resource_uuid' => $uuid]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($deployments->count() > 0) {
|
if ($message->count() > 0) {
|
||||||
$payload->put('deployments', $deployments->toArray());
|
return response()->json(['message' => $message->toArray()], 200);
|
||||||
return response()->json($payload->toArray(), 200);
|
|
||||||
}
|
}
|
||||||
return response()->json(['error' => "No resources found.", 'upstream docs' => 'https://coolify.io/docs/api/deploy-webhook'], 404);
|
return response()->json(['error' => "No resources found.", 'upstream docs' => 'https://coolify.io/docs/api/deploy-webhook'], 404);
|
||||||
}
|
}
|
||||||
@ -92,12 +66,10 @@ class Deploy extends Controller
|
|||||||
return response()->json(['error' => 'No TAGs provided.', 'upstream docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400);
|
return response()->json(['error' => 'No TAGs provided.', 'upstream docs' => 'https://coolify.io/docs/api/deploy-webhook'], 400);
|
||||||
}
|
}
|
||||||
$message = collect([]);
|
$message = collect([]);
|
||||||
$deployments = collect();
|
|
||||||
$payload = collect();
|
|
||||||
foreach ($tags as $tag) {
|
foreach ($tags as $tag) {
|
||||||
$found_tag = Tag::where(['name' => $tag, 'team_id' => $team_id])->first();
|
$found_tag = Tag::where(['name' => $tag, 'team_id' => $team_id])->first();
|
||||||
if (!$found_tag) {
|
if (!$found_tag) {
|
||||||
// $message->push("Tag {$tag} not found.");
|
$message->push("Tag {$tag} not found.");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$applications = $found_tag->applications()->get();
|
$applications = $found_tag->applications()->get();
|
||||||
@ -107,78 +79,83 @@ class Deploy extends Controller
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
foreach ($applications as $resource) {
|
foreach ($applications as $resource) {
|
||||||
['message' => $return_message, 'deployment_uuid' => $deployment_uuid] = $this->deploy_resource($resource, $force);
|
$return_message = $this->deploy_resource($resource, $force);
|
||||||
if ($deployment_uuid) {
|
|
||||||
$deployments->push(['resource_uuid' => $resource->uuid, 'deployment_uuid' => $deployment_uuid->toString()]);
|
|
||||||
}
|
|
||||||
$message = $message->merge($return_message);
|
$message = $message->merge($return_message);
|
||||||
}
|
}
|
||||||
foreach ($services as $resource) {
|
foreach ($services as $resource) {
|
||||||
['message' => $return_message] = $this->deploy_resource($resource, $force);
|
$return_message = $this->deploy_resource($resource, $force);
|
||||||
$message = $message->merge($return_message);
|
$message = $message->merge($return_message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ray($message);
|
|
||||||
if ($message->count() > 0) {
|
if ($message->count() > 0) {
|
||||||
$payload->put('message', $message->toArray());
|
return response()->json(['message' => $message->toArray()], 200);
|
||||||
if ($deployments->count() > 0) {
|
|
||||||
$payload->put('details', $deployments->toArray());
|
|
||||||
}
|
|
||||||
return response()->json($payload->toArray(), 200);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return response()->json(['error' => "No resources found with this tag.", 'upstream docs' => 'https://coolify.io/docs/api/deploy-webhook'], 404);
|
return response()->json(['error' => "No resources found.", 'upstream docs' => 'https://coolify.io/docs/api/deploy-webhook'], 404);
|
||||||
}
|
}
|
||||||
public function deploy_resource($resource, bool $force = false): array
|
public function deploy_resource($resource, bool $force = false): Collection
|
||||||
{
|
{
|
||||||
$message = null;
|
$message = collect([]);
|
||||||
$deployment_uuid = null;
|
|
||||||
if (gettype($resource) !== 'object') {
|
if (gettype($resource) !== 'object') {
|
||||||
return ['message' => "Resource ($resource) not found.", 'deployment_uuid' => $deployment_uuid];
|
return $message->push("Resource ($resource) not found.");
|
||||||
}
|
}
|
||||||
$type = $resource?->getMorphClass();
|
$type = $resource?->getMorphClass();
|
||||||
if ($type === 'App\Models\Application') {
|
if ($type === 'App\Models\Application') {
|
||||||
$deployment_uuid = new Cuid2(7);
|
|
||||||
queue_application_deployment(
|
queue_application_deployment(
|
||||||
application: $resource,
|
application: $resource,
|
||||||
deployment_uuid: $deployment_uuid,
|
deployment_uuid: new Cuid2(7),
|
||||||
force_rebuild: $force,
|
force_rebuild: $force,
|
||||||
);
|
);
|
||||||
$message = "Application {$resource->name} deployment queued.";
|
$message->push("Application {$resource->name} deployment queued.");
|
||||||
} else if ($type === 'App\Models\StandalonePostgresql') {
|
} else if ($type === 'App\Models\StandalonePostgresql') {
|
||||||
|
if (str($resource->status)->startsWith('running')) {
|
||||||
|
$message->push("Database {$resource->name} already running.");
|
||||||
|
}
|
||||||
StartPostgresql::run($resource);
|
StartPostgresql::run($resource);
|
||||||
$resource->update([
|
$resource->update([
|
||||||
'started_at' => now(),
|
'started_at' => now(),
|
||||||
]);
|
]);
|
||||||
$message = "Database {$resource->name} started.";
|
$message->push("Database {$resource->name} started.");
|
||||||
} else if ($type === 'App\Models\StandaloneRedis') {
|
} else if ($type === 'App\Models\StandaloneRedis') {
|
||||||
|
if (str($resource->status)->startsWith('running')) {
|
||||||
|
$message->push("Database {$resource->name} already running.");
|
||||||
|
}
|
||||||
StartRedis::run($resource);
|
StartRedis::run($resource);
|
||||||
$resource->update([
|
$resource->update([
|
||||||
'started_at' => now(),
|
'started_at' => now(),
|
||||||
]);
|
]);
|
||||||
$message = "Database {$resource->name} started.";
|
$message->push("Database {$resource->name} started.");
|
||||||
} else if ($type === 'App\Models\StandaloneMongodb') {
|
} else if ($type === 'App\Models\StandaloneMongodb') {
|
||||||
|
if (str($resource->status)->startsWith('running')) {
|
||||||
|
$message->push("Database {$resource->name} already running.");
|
||||||
|
}
|
||||||
StartMongodb::run($resource);
|
StartMongodb::run($resource);
|
||||||
$resource->update([
|
$resource->update([
|
||||||
'started_at' => now(),
|
'started_at' => now(),
|
||||||
]);
|
]);
|
||||||
$message = "Database {$resource->name} started.";
|
$message->push("Database {$resource->name} started.");
|
||||||
} else if ($type === 'App\Models\StandaloneMysql') {
|
} else if ($type === 'App\Models\StandaloneMysql') {
|
||||||
|
if (str($resource->status)->startsWith('running')) {
|
||||||
|
$message->push("Database {$resource->name} already running.");
|
||||||
|
}
|
||||||
StartMysql::run($resource);
|
StartMysql::run($resource);
|
||||||
$resource->update([
|
$resource->update([
|
||||||
'started_at' => now(),
|
'started_at' => now(),
|
||||||
]);
|
]);
|
||||||
$message = "Database {$resource->name} started.";
|
$message->push("Database {$resource->name} started.");
|
||||||
} else if ($type === 'App\Models\StandaloneMariadb') {
|
} else if ($type === 'App\Models\StandaloneMariadb') {
|
||||||
|
if (str($resource->status)->startsWith('running')) {
|
||||||
|
$message->push("Database {$resource->name} already running.");
|
||||||
|
}
|
||||||
StartMariadb::run($resource);
|
StartMariadb::run($resource);
|
||||||
$resource->update([
|
$resource->update([
|
||||||
'started_at' => now(),
|
'started_at' => now(),
|
||||||
]);
|
]);
|
||||||
$message = "Database {$resource->name} started.";
|
$message->push("Database {$resource->name} started.");
|
||||||
} else if ($type === 'App\Models\Service') {
|
} else if ($type === 'App\Models\Service') {
|
||||||
StartService::run($resource);
|
StartService::run($resource);
|
||||||
$message = "Service {$resource->name} started. It could take a while, be patient.";
|
$message->push("Service {$resource->name} started. It could take a while, be patient.");
|
||||||
}
|
}
|
||||||
return ['message' => $message, 'deployment_uuid' => $deployment_uuid];
|
return $message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,317 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Api;
|
|
||||||
|
|
||||||
use App\Actions\Database\StartDatabase;
|
|
||||||
use App\Actions\Service\StartService;
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Models\ApplicationDeploymentQueue;
|
|
||||||
use App\Models\Server;
|
|
||||||
use App\Models\Tag;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use OpenApi\Attributes as OA;
|
|
||||||
use Visus\Cuid2\Cuid2;
|
|
||||||
|
|
||||||
class DeployController extends Controller
|
|
||||||
{
|
|
||||||
private function removeSensitiveData($deployment)
|
|
||||||
{
|
|
||||||
$token = auth()->user()->currentAccessToken();
|
|
||||||
if ($token->can('view:sensitive')) {
|
|
||||||
return serializeApiResponse($deployment);
|
|
||||||
}
|
|
||||||
|
|
||||||
$deployment->makeHidden([
|
|
||||||
'logs',
|
|
||||||
]);
|
|
||||||
|
|
||||||
return serializeApiResponse($deployment);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[OA\Get(
|
|
||||||
summary: 'List',
|
|
||||||
description: 'List currently running deployments',
|
|
||||||
path: '/deployments',
|
|
||||||
security: [
|
|
||||||
['bearerAuth' => []],
|
|
||||||
],
|
|
||||||
tags: ['Deployments'],
|
|
||||||
responses: [
|
|
||||||
new OA\Response(
|
|
||||||
response: 200,
|
|
||||||
description: 'Get all currently running deployments.',
|
|
||||||
content: [
|
|
||||||
|
|
||||||
new OA\MediaType(
|
|
||||||
mediaType: 'application/json',
|
|
||||||
schema: new OA\Schema(
|
|
||||||
type: 'array',
|
|
||||||
items: new OA\Items(ref: '#/components/schemas/ApplicationDeploymentQueue'),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
new OA\Response(
|
|
||||||
response: 401,
|
|
||||||
ref: '#/components/responses/401',
|
|
||||||
),
|
|
||||||
new OA\Response(
|
|
||||||
response: 400,
|
|
||||||
ref: '#/components/responses/400',
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)]
|
|
||||||
public function deployments(Request $request)
|
|
||||||
{
|
|
||||||
$teamId = getTeamIdFromToken();
|
|
||||||
if (is_null($teamId)) {
|
|
||||||
return invalidTokenResponse();
|
|
||||||
}
|
|
||||||
$servers = Server::whereTeamId($teamId)->get();
|
|
||||||
$deployments_per_server = ApplicationDeploymentQueue::whereIn('status', ['in_progress', 'queued'])->whereIn('server_id', $servers->pluck('id'))->get()->sortBy('id');
|
|
||||||
$deployments_per_server = $deployments_per_server->map(function ($deployment) {
|
|
||||||
return $this->removeSensitiveData($deployment);
|
|
||||||
});
|
|
||||||
|
|
||||||
return response()->json($deployments_per_server);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[OA\Get(
|
|
||||||
summary: 'Get',
|
|
||||||
description: 'Get deployment by UUID.',
|
|
||||||
path: '/deployments/{uuid}',
|
|
||||||
security: [
|
|
||||||
['bearerAuth' => []],
|
|
||||||
],
|
|
||||||
tags: ['Deployments'],
|
|
||||||
parameters: [
|
|
||||||
new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Deployment Uuid', schema: new OA\Schema(type: 'integer')),
|
|
||||||
],
|
|
||||||
responses: [
|
|
||||||
new OA\Response(
|
|
||||||
response: 200,
|
|
||||||
description: 'Get deployment by UUID.',
|
|
||||||
content: [
|
|
||||||
new OA\MediaType(
|
|
||||||
mediaType: 'application/json',
|
|
||||||
schema: new OA\Schema(
|
|
||||||
ref: '#/components/schemas/ApplicationDeploymentQueue',
|
|
||||||
)
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
new OA\Response(
|
|
||||||
response: 401,
|
|
||||||
ref: '#/components/responses/401',
|
|
||||||
),
|
|
||||||
new OA\Response(
|
|
||||||
response: 400,
|
|
||||||
ref: '#/components/responses/400',
|
|
||||||
),
|
|
||||||
new OA\Response(
|
|
||||||
response: 404,
|
|
||||||
ref: '#/components/responses/404',
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)]
|
|
||||||
public function deployment_by_uuid(Request $request)
|
|
||||||
{
|
|
||||||
$teamId = getTeamIdFromToken();
|
|
||||||
if (is_null($teamId)) {
|
|
||||||
return invalidTokenResponse();
|
|
||||||
}
|
|
||||||
$uuid = $request->route('uuid');
|
|
||||||
if (! $uuid) {
|
|
||||||
return response()->json(['message' => 'UUID is required.'], 400);
|
|
||||||
}
|
|
||||||
$deployment = ApplicationDeploymentQueue::where('deployment_uuid', $uuid)->first();
|
|
||||||
if (! $deployment) {
|
|
||||||
return response()->json(['message' => 'Deployment not found.'], 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json($this->removeSensitiveData($deployment));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[OA\Get(
|
|
||||||
summary: 'Deploy',
|
|
||||||
description: 'Deploy by tag or uuid. `Post` request also accepted.',
|
|
||||||
path: '/deploy',
|
|
||||||
security: [
|
|
||||||
['bearerAuth' => []],
|
|
||||||
],
|
|
||||||
tags: ['Deployments'],
|
|
||||||
parameters: [
|
|
||||||
new OA\Parameter(name: 'tag', in: 'query', description: 'Tag name(s). Comma separated list is also accepted.', schema: new OA\Schema(type: 'string')),
|
|
||||||
new OA\Parameter(name: 'uuid', in: 'query', description: 'Resource UUID(s). Comma separated list is also accepted.', schema: new OA\Schema(type: 'string')),
|
|
||||||
new OA\Parameter(name: 'force', in: 'query', description: 'Force rebuild (without cache)', schema: new OA\Schema(type: 'boolean')),
|
|
||||||
],
|
|
||||||
|
|
||||||
responses: [
|
|
||||||
new OA\Response(
|
|
||||||
response: 200,
|
|
||||||
description: 'Get deployment(s) Uuid\'s',
|
|
||||||
content: [
|
|
||||||
new OA\MediaType(
|
|
||||||
mediaType: 'application/json',
|
|
||||||
schema: new OA\Schema(
|
|
||||||
type: 'object',
|
|
||||||
properties: [
|
|
||||||
'deployments' => new OA\Property(
|
|
||||||
property: 'deployments',
|
|
||||||
type: 'array',
|
|
||||||
items: new OA\Items(
|
|
||||||
type: 'object',
|
|
||||||
properties: [
|
|
||||||
'message' => ['type' => 'string'],
|
|
||||||
'resource_uuid' => ['type' => 'string'],
|
|
||||||
'deployment_uuid' => ['type' => 'string'],
|
|
||||||
]
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
new OA\Response(
|
|
||||||
response: 401,
|
|
||||||
ref: '#/components/responses/401',
|
|
||||||
),
|
|
||||||
new OA\Response(
|
|
||||||
response: 400,
|
|
||||||
ref: '#/components/responses/400',
|
|
||||||
),
|
|
||||||
|
|
||||||
]
|
|
||||||
)]
|
|
||||||
public function deploy(Request $request)
|
|
||||||
{
|
|
||||||
$teamId = getTeamIdFromToken();
|
|
||||||
$uuids = $request->query->get('uuid');
|
|
||||||
$tags = $request->query->get('tag');
|
|
||||||
$force = $request->query->get('force') ?? false;
|
|
||||||
|
|
||||||
if ($uuids && $tags) {
|
|
||||||
return response()->json(['message' => 'You can only use uuid or tag, not both.'], 400);
|
|
||||||
}
|
|
||||||
if (is_null($teamId)) {
|
|
||||||
return invalidTokenResponse();
|
|
||||||
}
|
|
||||||
if ($tags) {
|
|
||||||
return $this->by_tags($tags, $teamId, $force);
|
|
||||||
} elseif ($uuids) {
|
|
||||||
return $this->by_uuids($uuids, $teamId, $force);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json(['message' => 'You must provide uuid or tag.'], 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function by_uuids(string $uuid, int $teamId, bool $force = false)
|
|
||||||
{
|
|
||||||
$uuids = explode(',', $uuid);
|
|
||||||
$uuids = collect(array_filter($uuids));
|
|
||||||
|
|
||||||
if (count($uuids) === 0) {
|
|
||||||
return response()->json(['message' => 'No UUIDs provided.'], 400);
|
|
||||||
}
|
|
||||||
$deployments = collect();
|
|
||||||
$payload = collect();
|
|
||||||
foreach ($uuids as $uuid) {
|
|
||||||
$resource = getResourceByUuid($uuid, $teamId);
|
|
||||||
if ($resource) {
|
|
||||||
['message' => $return_message, 'deployment_uuid' => $deployment_uuid] = $this->deploy_resource($resource, $force);
|
|
||||||
if ($deployment_uuid) {
|
|
||||||
$deployments->push(['message' => $return_message, 'resource_uuid' => $uuid, 'deployment_uuid' => $deployment_uuid->toString()]);
|
|
||||||
} else {
|
|
||||||
$deployments->push(['message' => $return_message, 'resource_uuid' => $uuid]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($deployments->count() > 0) {
|
|
||||||
$payload->put('deployments', $deployments->toArray());
|
|
||||||
|
|
||||||
return response()->json(serializeApiResponse($payload->toArray()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json(['message' => 'No resources found.'], 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function by_tags(string $tags, int $team_id, bool $force = false)
|
|
||||||
{
|
|
||||||
$tags = explode(',', $tags);
|
|
||||||
$tags = collect(array_filter($tags));
|
|
||||||
|
|
||||||
if (count($tags) === 0) {
|
|
||||||
return response()->json(['message' => 'No TAGs provided.'], 400);
|
|
||||||
}
|
|
||||||
$message = collect([]);
|
|
||||||
$deployments = collect();
|
|
||||||
$payload = collect();
|
|
||||||
foreach ($tags as $tag) {
|
|
||||||
$found_tag = Tag::where(['name' => $tag, 'team_id' => $team_id])->first();
|
|
||||||
if (! $found_tag) {
|
|
||||||
// $message->push("Tag {$tag} not found.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$applications = $found_tag->applications()->get();
|
|
||||||
$services = $found_tag->services()->get();
|
|
||||||
if ($applications->count() === 0 && $services->count() === 0) {
|
|
||||||
$message->push("No resources found for tag {$tag}.");
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
foreach ($applications as $resource) {
|
|
||||||
['message' => $return_message, 'deployment_uuid' => $deployment_uuid] = $this->deploy_resource($resource, $force);
|
|
||||||
if ($deployment_uuid) {
|
|
||||||
$deployments->push(['resource_uuid' => $resource->uuid, 'deployment_uuid' => $deployment_uuid->toString()]);
|
|
||||||
}
|
|
||||||
$message = $message->merge($return_message);
|
|
||||||
}
|
|
||||||
foreach ($services as $resource) {
|
|
||||||
['message' => $return_message] = $this->deploy_resource($resource, $force);
|
|
||||||
$message = $message->merge($return_message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($message->count() > 0) {
|
|
||||||
$payload->put('message', $message->toArray());
|
|
||||||
if ($deployments->count() > 0) {
|
|
||||||
$payload->put('details', $deployments->toArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json(serializeApiResponse($payload->toArray()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json(['message' => 'No resources found with this tag.'], 404);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function deploy_resource($resource, bool $force = false): array
|
|
||||||
{
|
|
||||||
$message = null;
|
|
||||||
$deployment_uuid = null;
|
|
||||||
if (gettype($resource) !== 'object') {
|
|
||||||
return ['message' => "Resource ($resource) not found.", 'deployment_uuid' => $deployment_uuid];
|
|
||||||
}
|
|
||||||
switch ($resource?->getMorphClass()) {
|
|
||||||
case 'App\Models\Application':
|
|
||||||
$deployment_uuid = new Cuid2(7);
|
|
||||||
queue_application_deployment(
|
|
||||||
application: $resource,
|
|
||||||
deployment_uuid: $deployment_uuid,
|
|
||||||
force_rebuild: $force,
|
|
||||||
);
|
|
||||||
$message = "Application {$resource->name} deployment queued.";
|
|
||||||
break;
|
|
||||||
case 'App\Models\Service':
|
|
||||||
StartService::run($resource);
|
|
||||||
$message = "Service {$resource->name} started. It could take a while, be patient.";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// Database resource
|
|
||||||
StartDatabase::dispatch($resource);
|
|
||||||
$resource->update([
|
|
||||||
'started_at' => now(),
|
|
||||||
]);
|
|
||||||
$message = "Database {$resource->name} started.";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ['message' => $message, 'deployment_uuid' => $deployment_uuid];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Api;
|
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use App\Models\EnvironmentVariable;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
|
|
||||||
class EnvironmentVariablesController extends Controller
|
|
||||||
{
|
|
||||||
public function delete_env_by_uuid(Request $request)
|
|
||||||
{
|
|
||||||
$teamId = getTeamIdFromToken();
|
|
||||||
if (is_null($teamId)) {
|
|
||||||
return invalidTokenResponse();
|
|
||||||
}
|
|
||||||
$env = EnvironmentVariable::where('uuid', $request->env_uuid)->first();
|
|
||||||
if (! $env) {
|
|
||||||
return response()->json([
|
|
||||||
'message' => 'Environment variable not found.',
|
|
||||||
], 404);
|
|
||||||
}
|
|
||||||
$found_app = $env->resource()->whereRelation('environment.project.team', 'id', $teamId)->first();
|
|
||||||
if (! $found_app) {
|
|
||||||
return response()->json([
|
|
||||||
'message' => 'Environment variable not found.',
|
|
||||||
], 404);
|
|
||||||
}
|
|
||||||
$env->delete();
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'message' => 'Environment variable deleted.',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Api;
|
|
||||||
|
|
||||||
use OpenApi\Attributes as OA;
|
|
||||||
|
|
||||||
#[OA\Info(title: 'Coolify', version: '0.1')]
|
|
||||||
#[OA\Server(url: 'https://app.coolify.io/api/v1')]
|
|
||||||
#[OA\SecurityScheme(
|
|
||||||
type: 'http',
|
|
||||||
scheme: 'bearer',
|
|
||||||
securityScheme: 'bearerAuth',
|
|
||||||
description: 'Go to `Keys & Tokens` / `API tokens` and create a new token. Use the token as the bearer token.')]
|
|
||||||
#[OA\Components(
|
|
||||||
responses: [
|
|
||||||
new OA\Response(
|
|
||||||
response: 400,
|
|
||||||
description: 'Invalid token.',
|
|
||||||
content: new OA\JsonContent(
|
|
||||||
type: 'object',
|
|
||||||
properties: [
|
|
||||||
new OA\Property(property: 'message', type: 'string', example: 'Invalid token.'),
|
|
||||||
]
|
|
||||||
)),
|
|
||||||
new OA\Response(
|
|
||||||
response: 401,
|
|
||||||
description: 'Unauthenticated.',
|
|
||||||
content: new OA\JsonContent(
|
|
||||||
type: 'object',
|
|
||||||
properties: [
|
|
||||||
new OA\Property(property: 'message', type: 'string', example: 'Unauthenticated.'),
|
|
||||||
]
|
|
||||||
)),
|
|
||||||
new OA\Response(
|
|
||||||
response: 404,
|
|
||||||
description: 'Resource not found.',
|
|
||||||
content: new OA\JsonContent(
|
|
||||||
type: 'object',
|
|
||||||
properties: [
|
|
||||||
new OA\Property(property: 'message', type: 'string', example: 'Resource not found.'),
|
|
||||||
]
|
|
||||||
)),
|
|
||||||
],
|
|
||||||
)]
|
|
||||||
class OpenApi
|
|
||||||
{
|
|
||||||
// This class is used to generate OpenAPI documentation
|
|
||||||
// for the Coolify API. It is not a controller and does
|
|
||||||
// not contain any routes. It is used to define the
|
|
||||||
// OpenAPI metadata and security scheme for the API.
|
|
||||||
}
|
|
@ -1,183 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Http\Controllers\Api;
|
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Support\Facades\Http;
|
|
||||||
use OpenApi\Attributes as OA;
|
|
||||||
|
|
||||||
class OtherController extends Controller
|
|
||||||
{
|
|
||||||
#[OA\Get(
|
|
||||||
summary: 'Version',
|
|
||||||
description: 'Get Coolify version.',
|
|
||||||
path: '/version',
|
|
||||||
security: [
|
|
||||||
['bearerAuth' => []],
|
|
||||||
],
|
|
||||||
responses: [
|
|
||||||
new OA\Response(
|
|
||||||
response: 200,
|
|
||||||
description: 'Returns the version of the application',
|
|
||||||
content: new OA\JsonContent(
|
|
||||||
type: 'string',
|
|
||||||
example: 'v4.0.0',
|
|
||||||
)),
|
|
||||||
new OA\Response(
|
|
||||||
response: 401,
|
|
||||||
ref: '#/components/responses/401',
|
|
||||||
),
|
|
||||||
new OA\Response(
|
|
||||||
response: 400,
|
|
||||||
ref: '#/components/responses/400',
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)]
|
|
||||||
public function version(Request $request)
|
|
||||||
{
|
|
||||||
return response(config('version'));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[OA\Get(
|
|
||||||
summary: 'Enable API',
|
|
||||||
description: 'Enable API (only with root permissions).',
|
|
||||||
path: '/enable',
|
|
||||||
security: [
|
|
||||||
['bearerAuth' => []],
|
|
||||||
],
|
|
||||||
responses: [
|
|
||||||
new OA\Response(
|
|
||||||
response: 200,
|
|
||||||
description: 'Enable API.',
|
|
||||||
content: new OA\JsonContent(
|
|
||||||
type: 'object',
|
|
||||||
properties: [
|
|
||||||
new OA\Property(property: 'message', type: 'string', example: 'API enabled.'),
|
|
||||||
]
|
|
||||||
)),
|
|
||||||
new OA\Response(
|
|
||||||
response: 403,
|
|
||||||
description: 'You are not allowed to enable the API.',
|
|
||||||
content: new OA\JsonContent(
|
|
||||||
type: 'object',
|
|
||||||
properties: [
|
|
||||||
new OA\Property(property: 'message', type: 'string', example: 'You are not allowed to enable the API.'),
|
|
||||||
]
|
|
||||||
)),
|
|
||||||
new OA\Response(
|
|
||||||
response: 401,
|
|
||||||
ref: '#/components/responses/401',
|
|
||||||
),
|
|
||||||
new OA\Response(
|
|
||||||
response: 400,
|
|
||||||
ref: '#/components/responses/400',
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)]
|
|
||||||
public function enable_api(Request $request)
|
|
||||||
{
|
|
||||||
$teamId = getTeamIdFromToken();
|
|
||||||
if (is_null($teamId)) {
|
|
||||||
return invalidTokenResponse();
|
|
||||||
}
|
|
||||||
if ($teamId !== '0') {
|
|
||||||
return response()->json(['message' => 'You are not allowed to enable the API.'], 403);
|
|
||||||
}
|
|
||||||
$settings = \App\Models\InstanceSettings::get();
|
|
||||||
$settings->update(['is_api_enabled' => true]);
|
|
||||||
|
|
||||||
return response()->json(['message' => 'API enabled.'], 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[OA\Get(
|
|
||||||
summary: 'Disable API',
|
|
||||||
description: 'Disable API (only with root permissions).',
|
|
||||||
path: '/disable',
|
|
||||||
security: [
|
|
||||||
['bearerAuth' => []],
|
|
||||||
],
|
|
||||||
responses: [
|
|
||||||
new OA\Response(
|
|
||||||
response: 200,
|
|
||||||
description: 'Disable API.',
|
|
||||||
content: new OA\JsonContent(
|
|
||||||
type: 'object',
|
|
||||||
properties: [
|
|
||||||
new OA\Property(property: 'message', type: 'string', example: 'API disabled.'),
|
|
||||||
]
|
|
||||||
)),
|
|
||||||
new OA\Response(
|
|
||||||
response: 403,
|
|
||||||
description: 'You are not allowed to disable the API.',
|
|
||||||
content: new OA\JsonContent(
|
|
||||||
type: 'object',
|
|
||||||
properties: [
|
|
||||||
new OA\Property(property: 'message', type: 'string', example: 'You are not allowed to disable the API.'),
|
|
||||||
]
|
|
||||||
)),
|
|
||||||
new OA\Response(
|
|
||||||
response: 401,
|
|
||||||
ref: '#/components/responses/401',
|
|
||||||
),
|
|
||||||
new OA\Response(
|
|
||||||
response: 400,
|
|
||||||
ref: '#/components/responses/400',
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)]
|
|
||||||
public function disable_api(Request $request)
|
|
||||||
{
|
|
||||||
$teamId = getTeamIdFromToken();
|
|
||||||
if (is_null($teamId)) {
|
|
||||||
return invalidTokenResponse();
|
|
||||||
}
|
|
||||||
if ($teamId !== '0') {
|
|
||||||
return response()->json(['message' => 'You are not allowed to disable the API.'], 403);
|
|
||||||
}
|
|
||||||
$settings = \App\Models\InstanceSettings::get();
|
|
||||||
$settings->update(['is_api_enabled' => false]);
|
|
||||||
|
|
||||||
return response()->json(['message' => 'API disabled.'], 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function feedback(Request $request)
|
|
||||||
{
|
|
||||||
$content = $request->input('content');
|
|
||||||
$webhook_url = config('coolify.feedback_discord_webhook');
|
|
||||||
if ($webhook_url) {
|
|
||||||
Http::post($webhook_url, [
|
|
||||||
'content' => $content,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json(['message' => 'Feedback sent.'], 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[OA\Get(
|
|
||||||
summary: 'Healthcheck',
|
|
||||||
description: 'Healthcheck endpoint.',
|
|
||||||
path: '/healthcheck',
|
|
||||||
responses: [
|
|
||||||
new OA\Response(
|
|
||||||
response: 200,
|
|
||||||
description: 'Healthcheck endpoint.',
|
|
||||||
content: new OA\JsonContent(
|
|
||||||
type: 'string',
|
|
||||||
example: 'OK',
|
|
||||||
)),
|
|
||||||
new OA\Response(
|
|
||||||
response: 401,
|
|
||||||
ref: '#/components/responses/401',
|
|
||||||
),
|
|
||||||
new OA\Response(
|
|
||||||
response: 400,
|
|
||||||
ref: '#/components/responses/400',
|
|
||||||
),
|
|
||||||
]
|
|
||||||
)]
|
|
||||||
public function healthcheck(Request $request)
|
|
||||||
{
|
|
||||||
return 'OK';
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user