mirror of
https://github.com/cupcakearmy/coolify.git
synced 2024-10-22 08:04:19 +02:00
Merge branch 'next' into env-vars
This commit is contained in:
commit
e7b050a4da
@ -1,16 +1,31 @@
|
||||
APP_NAME=Coolify-localhost
|
||||
APP_ID=development
|
||||
# Coolify Configuration
|
||||
APP_ENV=local
|
||||
APP_NAME="Coolify Development"
|
||||
APP_ID=development
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
APP_PORT=8000
|
||||
APP_DEBUG=true
|
||||
MUX_ENABLED=false
|
||||
|
||||
# Enable Laravel Telescope for debugging
|
||||
TELESCOPE_ENABLED=false
|
||||
|
||||
# Selenium Driver URL for Dusk
|
||||
DUSK_DRIVER_URL=http://selenium:4444
|
||||
|
||||
## For Andras only
|
||||
# To purge cache
|
||||
# PostgreSQL Database Configuration
|
||||
DB_DATABASE=coolify
|
||||
DB_USERNAME=coolify
|
||||
DB_PASSWORD=password
|
||||
DB_HOST=host.docker.internal
|
||||
DB_PORT=5432
|
||||
|
||||
#Set custom ray port
|
||||
RAY_PORT=
|
||||
|
||||
# Special Keys for Andras
|
||||
# For cache purging
|
||||
BUNNY_API_KEY=
|
||||
# To upload assets
|
||||
# For asset uploads
|
||||
BUNNY_STORAGE_API_KEY=
|
||||
|
@ -1,10 +1,16 @@
|
||||
# Coolify Configuration
|
||||
APP_ID=
|
||||
APP_NAME=Coolify
|
||||
APP_KEY=
|
||||
|
||||
# PostgreSQL Database Configuration
|
||||
DB_DATABASE=coolify
|
||||
DB_USERNAME=
|
||||
DB_PASSWORD=
|
||||
|
||||
# Redis Configuration
|
||||
REDIS_PASSWORD=
|
||||
|
||||
# Pusher Configuration
|
||||
PUSHER_APP_ID=
|
||||
PUSHER_APP_KEY=
|
||||
PUSHER_APP_SECRET=
|
||||
|
189
CONTRIBUTING.md
Normal file
189
CONTRIBUTING.md
Normal file
@ -0,0 +1,189 @@
|
||||
# Contributing
|
||||
|
||||
> "First, thanks for considering contributing 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 `#contribute` channel.
|
||||
|
||||
|
||||
## Code Contribution
|
||||
|
||||
## 1. Setup your development environment
|
||||
|
||||
Follow the steps below for your operating system:
|
||||
|
||||
### Windows
|
||||
|
||||
1. Install `docker-ce`, Docker Desktop (or similar):
|
||||
- Docker CE (recommended):
|
||||
- Install Windows Subsystem for Linux v2 (WSL2) by following this guide: [Install WSL](https://learn.microsoft.com/en-us/windows/wsl/install)
|
||||
- After installing WSL2, install Docker CE for your Linux distribution by following this guide: [Install Docker Engine](https://docs.docker.com/engine/install/)
|
||||
- Make sure to choose the appropriate Linux distribution (e.g., Ubuntu) when following the Docker installation guide
|
||||
- Install Docker Desktop (easier):
|
||||
- Download and install [Docker Desktop for Windows](https://docs.docker.com/desktop/install/windows-install/)
|
||||
- Ensure WSL2 backend is enabled in Docker Desktop settings
|
||||
|
||||
2. Install Spin:
|
||||
- Follow the instructions to install Spin on Windows from the [Spin documentation](https://serversideup.net/open-source/spin/docs/installation/install-windows#download-and-install-spin-into-wsl2)
|
||||
|
||||
### MacOS
|
||||
|
||||
1. Install Orbstack, Docker Desktop (or similar):
|
||||
- Orbstack (recommended, as it is a faster and lighter alternative to Docker Desktop):
|
||||
- Download and install [Orbstack](https://docs.orbstack.dev/quick-start#installation)
|
||||
- Docker Desktop:
|
||||
- Download and install [Docker Desktop for Mac](https://docs.docker.com/desktop/install/mac-install/)
|
||||
|
||||
2. Install Spin:
|
||||
- Follow the instructions to install Spin on MacOS from the [Spin documentation](https://serversideup.net/open-source/spin/docs/installation/install-macos/#download-and-install-spin)
|
||||
|
||||
### Linux
|
||||
|
||||
1. Install Docker Engine, Docker Desktop (or similar):
|
||||
- Docker Engine (recommended, as there is no VM overhead):
|
||||
- Follow the official [Docker Engine installation guide](https://docs.docker.com/engine/install/) for your Linux distribution
|
||||
- Docker Desktop:
|
||||
- If you want a GUI, you can use [Docker Desktop for Linux](https://docs.docker.com/desktop/install/linux-install/)
|
||||
|
||||
2. Install Spin:
|
||||
- Follow the instructions to install Spin on Linux from the [Spin documentation](https://serversideup.net/open-source/spin/docs/installation/install-linux#configure-docker-permissions)
|
||||
|
||||
|
||||
## 2. Verify installation (optional)
|
||||
|
||||
After installing Docker (or Orbstack) and Spin, verify the installation:
|
||||
|
||||
1. Open a terminal or command prompt
|
||||
2. Run the following commands:
|
||||
```bash
|
||||
docker --version
|
||||
spin --version
|
||||
```
|
||||
You should see version information for both Docker and Spin.
|
||||
|
||||
|
||||
## 3. Fork the Coolify repository and setup your local repository
|
||||
|
||||
1. Fork the [Coolify](https://github.com/coollabsio/coolify) repository to your GitHub account.
|
||||
|
||||
2. Install a code editor on your machine (below are some popular choices, choose one):
|
||||
|
||||
- Visual Studio Code (recommended free):
|
||||
- Windows/macOS/Linux: Download and install from [https://code.visualstudio.com/download](https://code.visualstudio.com/download)
|
||||
|
||||
- Cursor (recommended but paid for getting the full benefits):
|
||||
- Windows/macOS/Linux: Download and install from [https://www.cursor.com/](https://www.cursor.com/)
|
||||
|
||||
- Zed (very fast code editor):
|
||||
- macOS/Linux: Download and install from [https://zed.dev/download](https://zed.dev/download)
|
||||
- Windows: Not available yet
|
||||
|
||||
3. Clone the Coolify Repository from your fork to your local machine
|
||||
- Use `git clone` in the command line
|
||||
- Use GitHub Desktop (recommended):
|
||||
- Download and install from [https://desktop.github.com/](https://desktop.github.com/)
|
||||
- Open GitHub Desktop and login with your GitHub account
|
||||
- Click on `File` -> `Clone Repository` select `github.com` as the repository location, then select your forked Coolify repository, choose the local path and then click `Clone`
|
||||
|
||||
4. Open the cloned Coolify Repository in your chosen code editor.
|
||||
|
||||
|
||||
## 4. Set up Environment Variables
|
||||
|
||||
1. In the Code Editor, locate the `.env.development.example` file in the root directory of your local Coolify repository.
|
||||
|
||||
2. Duplicate the `.env.development.example` file and rename the copy to `.env`.
|
||||
|
||||
3. Open the new `.env` file and review its contents. Adjust any environment variables as needed for your development setup.
|
||||
|
||||
4. If you encounter errors during database migrations, update the database connection settings in your `.env` file. Use the IP address or hostname of your PostgreSQL database container. You can find this information by running `docker ps` after executing `spin up`.
|
||||
|
||||
5. Save the changes to your `.env` file.
|
||||
|
||||
|
||||
## 5. Start Coolify
|
||||
|
||||
1. Open a terminal in the local Coolify directory.
|
||||
|
||||
2. Run the following command in the terminal (leave that terminal open):
|
||||
```
|
||||
spin up
|
||||
```
|
||||
Note: You may see some errors, but don't worry; this is expected.
|
||||
|
||||
3. If you encounter permission errors, especially on macOS, use:
|
||||
```
|
||||
sudo spin up
|
||||
```
|
||||
|
||||
Note: If you change environment variables afterwards or anything seems broken, press Ctrl + C to stop the process and run `spin up` again.
|
||||
|
||||
|
||||
## 6. Start Development
|
||||
|
||||
1. Access your Coolify instance:
|
||||
- URL: `http://localhost:8000`
|
||||
- Login: `test@example.com`
|
||||
- Password: `password`
|
||||
|
||||
2. Additional development tools:
|
||||
- Laravel Horizon (scheduler): `http://localhost:8000/horizon`
|
||||
Note: Only accessible when logged in as root user
|
||||
- Mailpit (email catcher): `http://localhost:8025`
|
||||
- Telescope (debugging tool): `http://localhost:8000/telescope`
|
||||
Note: Disabled by default (so the database is not overloaded), enable by adding the following environment variable to your `.env` file:
|
||||
```env
|
||||
TELESCOPE_ENABLED=true
|
||||
```
|
||||
|
||||
|
||||
## 7. Development Notes
|
||||
|
||||
When working on Coolify, keep the following in mind:
|
||||
|
||||
1. **Database Migrations**: After switching branches or making changes to the database structure, always run migrations:
|
||||
```bash
|
||||
docker exec -it coolify php artisan migrate
|
||||
```
|
||||
|
||||
2. **Resetting Development Setup**: To reset your development setup to a clean database with default values:
|
||||
```bash
|
||||
docker exec -it coolify php artisan migrate:fresh --seed
|
||||
```
|
||||
|
||||
3. **Troubleshooting**: If you encounter unexpected behavior, ensure your database is up-to-date with the latest migrations and if possible reset the development setup to eliminate any envrionement specific issues.
|
||||
|
||||
Remember, forgetting to migrate the database can cause problems, so make it a habit to run migrations after pulling changes or switching branches.
|
||||
|
||||
|
||||
## 8. Contributing a New Service
|
||||
|
||||
To add a new service to Coolify, please refer to our documentation:
|
||||
[Adding a New Service](https://coolify.io/docs/knowledge-base/add-a-service)
|
||||
|
||||
|
||||
## 9. Create a Pull Request
|
||||
|
||||
1. After making changes or adding a new service:
|
||||
- Commit your changes to your forked repository.
|
||||
- Push the changes to your GitHub account.
|
||||
|
||||
2. Creating the Pull Request (PR):
|
||||
- Navigate to the main Coolify repository on GitHub.
|
||||
- Click the "Pull requests" tab.
|
||||
- Click the green "New pull request" button.
|
||||
- Choose your fork and branch as the compare branch.
|
||||
- Click "Create pull request".
|
||||
|
||||
3. Filling out the PR details:
|
||||
- Give your PR a descriptive title.
|
||||
- In the description, explain the changes you've made.
|
||||
- Reference any related issues by using keywords like "Fixes #123" or "Closes #456".
|
||||
|
||||
4. Important note:
|
||||
Always set the base branch for your PR to the `next` branch of the Coolify repository, not the `main` branch.
|
||||
|
||||
5. Submit your PR:
|
||||
- Review your changes one last time.
|
||||
- Click "Create pull request" to submit.
|
||||
|
||||
After submission, maintainers will review your PR and may request changes or provide feedback.
|
@ -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).
|
||||
|
@ -19,14 +19,19 @@ public function handle(Service $service)
|
||||
ray('Stopping service: '.$service->name);
|
||||
$applications = $service->applications()->get();
|
||||
foreach ($applications as $application) {
|
||||
instant_remote_process(command: ["docker stop --time=30 {$application->name}-{$service->uuid}"], server: $server, throwError: false);
|
||||
if ($applications->count() < 6) {
|
||||
instant_remote_process(command: ["docker stop --time=10 {$application->name}-{$service->uuid}"], server: $server, throwError: false);
|
||||
}
|
||||
instant_remote_process(command: ["docker rm {$application->name}-{$service->uuid}"], server: $server, throwError: false);
|
||||
instant_remote_process(command: ["docker rm -f {$application->name}-{$service->uuid}"], server: $server, throwError: false);
|
||||
$application->update(['status' => 'exited']);
|
||||
}
|
||||
$dbs = $service->databases()->get();
|
||||
foreach ($dbs as $db) {
|
||||
instant_remote_process(command: ["docker stop --time=30 {$db->name}-{$service->uuid}"], server: $server, throwError: false);
|
||||
if ($dbs->count() < 6) {
|
||||
|
||||
instant_remote_process(command: ["docker stop --time=10 {$db->name}-{$service->uuid}"], server: $server, throwError: false);
|
||||
}
|
||||
instant_remote_process(command: ["docker rm {$db->name}-{$service->uuid}"], server: $server, throwError: false);
|
||||
instant_remote_process(command: ["docker rm -f {$db->name}-{$service->uuid}"], server: $server, throwError: false);
|
||||
$db->update(['status' => 'exited']);
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationPreview;
|
||||
use App\Models\ScheduledTask;
|
||||
use App\Models\Service;
|
||||
use App\Models\ServiceApplication;
|
||||
@ -42,6 +43,17 @@ private function cleanup_stucked_resources()
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in cleaning stuck application: {$e->getMessage()}\n";
|
||||
}
|
||||
try {
|
||||
$applicationsPreviews = ApplicationPreview::get();
|
||||
foreach ($applicationsPreviews as $applicationPreview) {
|
||||
if (! data_get($applicationPreview, 'application')) {
|
||||
echo "Deleting stuck application preview: {$applicationPreview->uuid}\n";
|
||||
$applicationPreview->delete();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in cleaning stuck application: {$e->getMessage()}\n";
|
||||
}
|
||||
try {
|
||||
$postgresqls = StandalonePostgresql::withTrashed()->whereNotNull('deleted_at')->get();
|
||||
foreach ($postgresqls as $postgresql) {
|
||||
|
@ -443,7 +443,6 @@ private function deploy_docker_compose_buildpack()
|
||||
$fileStorage->deleteStorageOnServer();
|
||||
$fileStorage->saveStorageOnServer();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -455,7 +454,7 @@ private function deploy_docker_compose_buildpack()
|
||||
$yaml = $composeFile = $this->application->docker_compose_raw;
|
||||
$this->save_environment_variables();
|
||||
} else {
|
||||
$composeFile = $this->application->oldParser(pull_request_id: $this->pull_request_id, preview_id: data_get($this->preview, 'id'));
|
||||
$composeFile = $this->application->parse(pull_request_id: $this->pull_request_id, preview_id: data_get($this->preview, 'id'));
|
||||
$this->save_environment_variables();
|
||||
if (! is_null($this->env_filename)) {
|
||||
$services = collect($composeFile['services']);
|
||||
@ -476,7 +475,8 @@ private function deploy_docker_compose_buildpack()
|
||||
}
|
||||
$this->docker_compose_base64 = base64_encode($yaml);
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d | tee {$this->workdir}{$this->docker_compose_location} > /dev/null"), 'hidden' => true,
|
||||
executeInDocker($this->deployment_uuid, "echo '{$this->docker_compose_base64}' | base64 -d | tee {$this->workdir}{$this->docker_compose_location} > /dev/null"),
|
||||
'hidden' => true,
|
||||
]);
|
||||
// Build new container to limit downtime.
|
||||
$this->application_deployment_queue->addLogEntry('Pulling & building required images.');
|
||||
@ -506,9 +506,13 @@ private function deploy_docker_compose_buildpack()
|
||||
// TODO
|
||||
} else {
|
||||
$this->execute_remote_command([
|
||||
"docker network inspect '{$networkId}' >/dev/null 2>&1 || docker network create --attachable '{$networkId}' >/dev/null || true", 'hidden' => true, 'ignore_errors' => true,
|
||||
"docker network inspect '{$networkId}' >/dev/null 2>&1 || docker network create --attachable '{$networkId}' >/dev/null || true",
|
||||
'hidden' => true,
|
||||
'ignore_errors' => true,
|
||||
], [
|
||||
"docker network connect {$networkId} coolify-proxy || true", 'hidden' => true, 'ignore_errors' => true,
|
||||
"docker network connect {$networkId} coolify-proxy || true",
|
||||
'hidden' => true,
|
||||
'ignore_errors' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
@ -561,7 +565,6 @@ private function deploy_docker_compose_buildpack()
|
||||
);
|
||||
$this->write_deployment_configurations();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -744,7 +747,8 @@ private function push_to_docker_registry()
|
||||
$this->application_deployment_queue->addLogEntry("Pushing image to docker registry ({$this->production_image_name}).");
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "docker push {$this->production_image_name}"), 'hidden' => true,
|
||||
executeInDocker($this->deployment_uuid, "docker push {$this->production_image_name}"),
|
||||
'hidden' => true,
|
||||
],
|
||||
);
|
||||
if ($this->application->docker_registry_image_tag) {
|
||||
@ -752,10 +756,14 @@ private function push_to_docker_registry()
|
||||
$this->application_deployment_queue->addLogEntry("Tagging and pushing image with {$this->application->docker_registry_image_tag} tag.");
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "docker tag {$this->production_image_name} {$this->application->docker_registry_image_name}:{$this->application->docker_registry_image_tag}"), 'ignore_errors' => true, 'hidden' => true,
|
||||
executeInDocker($this->deployment_uuid, "docker tag {$this->production_image_name} {$this->application->docker_registry_image_name}:{$this->application->docker_registry_image_tag}"),
|
||||
'ignore_errors' => true,
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "docker push {$this->application->docker_registry_image_name}:{$this->application->docker_registry_image_tag}"), 'ignore_errors' => true, 'hidden' => true,
|
||||
executeInDocker($this->deployment_uuid, "docker push {$this->application->docker_registry_image_name}:{$this->application->docker_registry_image_tag}"),
|
||||
'ignore_errors' => true,
|
||||
'hidden' => true,
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -852,14 +860,20 @@ private function should_skip_build()
|
||||
private function check_image_locally_or_remotely()
|
||||
{
|
||||
$this->execute_remote_command([
|
||||
"docker images -q {$this->production_image_name} 2>/dev/null", 'hidden' => true, 'save' => 'local_image_found',
|
||||
"docker images -q {$this->production_image_name} 2>/dev/null",
|
||||
'hidden' => true,
|
||||
'save' => 'local_image_found',
|
||||
]);
|
||||
if (str($this->saved_outputs->get('local_image_found'))->isEmpty() && $this->application->docker_registry_image_name) {
|
||||
$this->execute_remote_command([
|
||||
"docker pull {$this->production_image_name} 2>/dev/null", 'ignore_errors' => true, 'hidden' => true,
|
||||
"docker pull {$this->production_image_name} 2>/dev/null",
|
||||
'ignore_errors' => true,
|
||||
'hidden' => true,
|
||||
]);
|
||||
$this->execute_remote_command([
|
||||
"docker images -q {$this->production_image_name} 2>/dev/null", 'hidden' => true, 'save' => 'local_image_found',
|
||||
"docker images -q {$this->production_image_name} 2>/dev/null",
|
||||
'hidden' => true,
|
||||
'save' => 'local_image_found',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -892,12 +906,14 @@ private function save_environment_variables()
|
||||
}
|
||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_FQDN')->isEmpty()) {
|
||||
$envs->push("COOLIFY_FQDN={$this->preview->fqdn}");
|
||||
$envs->push("COOLIFY_DOMAIN_URL={$this->preview->fqdn}");
|
||||
}
|
||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_URL')->isEmpty()) {
|
||||
$url = str($this->preview->fqdn)->replace('http://', '')->replace('https://', '');
|
||||
$envs->push("COOLIFY_URL={$url}");
|
||||
$envs->push("COOLIFY_DOMAIN_FQDN={$url}");
|
||||
}
|
||||
if ($this->application->compose_parsing_version === '1' || $this->application->compose_parsing_version === '2') {
|
||||
if ($this->application->build_pack !== 'dockercompose' || $this->application->compose_parsing_version === '1' || $this->application->compose_parsing_version === '2') {
|
||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
||||
$envs->push("COOLIFY_BRANCH={$local_branch}");
|
||||
}
|
||||
@ -942,13 +958,21 @@ private function save_environment_variables()
|
||||
}
|
||||
}
|
||||
if ($this->application->environment_variables->where('key', 'COOLIFY_FQDN')->isEmpty()) {
|
||||
$envs->push("COOLIFY_FQDN={$this->application->fqdn}");
|
||||
if ($this->application->compose_parsing_version === '3') {
|
||||
$envs->push("COOLIFY_URL={$this->application->fqdn}");
|
||||
} else {
|
||||
$envs->push("COOLIFY_FQDN={$this->application->fqdn}");
|
||||
}
|
||||
}
|
||||
if ($this->application->environment_variables->where('key', 'COOLIFY_URL')->isEmpty()) {
|
||||
$url = str($this->application->fqdn)->replace('http://', '')->replace('https://', '');
|
||||
$envs->push("COOLIFY_URL={$url}");
|
||||
if ($this->application->compose_parsing_version === '3') {
|
||||
$envs->push("COOLIFY_FQDN={$url}");
|
||||
} else {
|
||||
$envs->push("COOLIFY_URL={$url}");
|
||||
}
|
||||
}
|
||||
if ($this->application->compose_parsing_version === '1' || $this->application->compose_parsing_version === '2') {
|
||||
if ($this->application->build_pack !== 'dockercompose' || $this->application->compose_parsing_version === '1' || $this->application->compose_parsing_version === '2') {
|
||||
if ($this->application->environment_variables->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
||||
$envs->push("COOLIFY_BRANCH={$local_branch}");
|
||||
}
|
||||
@ -1417,7 +1441,8 @@ private function clone_repository()
|
||||
}
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
$importCommands, 'hidden' => true,
|
||||
$importCommands,
|
||||
'hidden' => true,
|
||||
]
|
||||
);
|
||||
$this->create_workdir();
|
||||
@ -1627,7 +1652,10 @@ private function generate_compose_file()
|
||||
// Check for custom HEALTHCHECK
|
||||
if ($this->application->build_pack === 'dockerfile' || $this->application->dockerfile) {
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"), 'hidden' => true, 'save' => 'dockerfile_from_repo', 'ignore_errors' => true,
|
||||
executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"),
|
||||
'hidden' => true,
|
||||
'save' => 'dockerfile_from_repo',
|
||||
'ignore_errors' => true,
|
||||
]);
|
||||
$dockerfile = collect(str($this->saved_outputs->get('dockerfile_from_repo'))->trim()->explode("\n"));
|
||||
$this->application->parseHealthcheckFromDockerfile($dockerfile);
|
||||
@ -1893,7 +1921,8 @@ private function pull_latest_image($image)
|
||||
$this->application_deployment_queue->addLogEntry("Pulling latest image ($image) from the registry.");
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "docker pull {$image}"), 'hidden' => true,
|
||||
executeInDocker($this->deployment_uuid, "docker pull {$image}"),
|
||||
'hidden' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
@ -1943,12 +1972,14 @@ private function build_image()
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]);
|
||||
if ($this->force_rebuild) {
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"), 'hidden' => true,
|
||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"),
|
||||
'hidden' => true,
|
||||
]);
|
||||
$build_command = "docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->build_image_name} {$this->workdir}";
|
||||
} else {
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"), 'hidden' => true,
|
||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->build_image_name} {$this->workdir} -o {$this->workdir}"),
|
||||
'hidden' => true,
|
||||
]);
|
||||
$build_command = "docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->build_image_name} {$this->workdir}";
|
||||
}
|
||||
@ -1956,10 +1987,12 @@ private function build_image()
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), 'hidden' => true,
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), 'hidden' => true,
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
]
|
||||
);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]);
|
||||
@ -1973,10 +2006,12 @@ private function build_image()
|
||||
}
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), 'hidden' => true,
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), 'hidden' => true,
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
@ -2013,10 +2048,12 @@ private function build_image()
|
||||
executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d | tee {$this->workdir}/nginx.conf > /dev/null"),
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), 'hidden' => true,
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), 'hidden' => true,
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
]
|
||||
);
|
||||
} else {
|
||||
@ -2030,10 +2067,12 @@ private function build_image()
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), 'hidden' => true,
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), 'hidden' => true,
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
]
|
||||
);
|
||||
} else {
|
||||
@ -2042,22 +2081,26 @@ private function build_image()
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, "echo '{$this->nixpacks_plan}' | base64 -d | tee /artifacts/thegameplan.json > /dev/null"), 'hidden' => true]);
|
||||
if ($this->force_rebuild) {
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"), 'hidden' => true,
|
||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --no-cache --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"),
|
||||
'hidden' => true,
|
||||
]);
|
||||
$build_command = "docker build --no-cache {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
} else {
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"), 'hidden' => true,
|
||||
executeInDocker($this->deployment_uuid, "nixpacks build -c /artifacts/thegameplan.json --cache-key '{$this->application->uuid}' --no-error-without-start -n {$this->production_image_name} {$this->workdir} -o {$this->workdir}"),
|
||||
'hidden' => true,
|
||||
]);
|
||||
$build_command = "docker build {$this->addHosts} --network host -f {$this->workdir}/.nixpacks/Dockerfile {$this->build_args} --progress plain -t {$this->production_image_name} {$this->workdir}";
|
||||
}
|
||||
$base64_build_command = base64_encode($build_command);
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), 'hidden' => true,
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), 'hidden' => true,
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
]
|
||||
);
|
||||
$this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm /artifacts/thegameplan.json'), 'hidden' => true]);
|
||||
@ -2071,10 +2114,12 @@ private function build_image()
|
||||
}
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"), 'hidden' => true,
|
||||
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee /artifacts/build.sh > /dev/null"),
|
||||
'hidden' => true,
|
||||
],
|
||||
[
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'), 'hidden' => true,
|
||||
executeInDocker($this->deployment_uuid, 'bash /artifacts/build.sh'),
|
||||
'hidden' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
@ -2100,7 +2145,6 @@ private function graceful_shutdown_container(string $containerName, int $timeout
|
||||
$this->execute_remote_command(
|
||||
["docker rm -f $containerName", 'hidden' => true, 'ignore_errors' => true]
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
private function stop_running_container(bool $force = false)
|
||||
@ -2178,7 +2222,9 @@ private function generate_build_env_variables()
|
||||
private function add_build_env_variables_to_dockerfile()
|
||||
{
|
||||
$this->execute_remote_command([
|
||||
executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"), 'hidden' => true, 'save' => 'dockerfile',
|
||||
executeInDocker($this->deployment_uuid, "cat {$this->workdir}{$this->dockerfile_location}"),
|
||||
'hidden' => true,
|
||||
'save' => 'dockerfile',
|
||||
]);
|
||||
$dockerfile = collect(str($this->saved_outputs->get('dockerfile'))->trim()->explode("\n"));
|
||||
if ($this->pull_request_id === 0) {
|
||||
@ -2223,7 +2269,8 @@ private function run_pre_deployment_command()
|
||||
$exec = "docker exec {$containerName} {$cmd}";
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
'command' => $exec, 'hidden' => true,
|
||||
'command' => $exec,
|
||||
'hidden' => true,
|
||||
],
|
||||
);
|
||||
|
||||
@ -2250,7 +2297,9 @@ private function run_post_deployment_command()
|
||||
try {
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
'command' => $exec, 'hidden' => true, 'save' => 'post-deployment-command-output',
|
||||
'command' => $exec,
|
||||
'hidden' => true,
|
||||
'save' => 'post-deployment-command-output',
|
||||
],
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
|
@ -50,15 +50,6 @@ public function get_deployments()
|
||||
])->sortBy('id')->groupBy('server_name')->toArray();
|
||||
}
|
||||
|
||||
// public function getIptables()
|
||||
// {
|
||||
// $servers = Server::ownedByCurrentTeam()->get();
|
||||
// foreach ($servers as $server) {
|
||||
// checkRequiredCommands($server);
|
||||
// $iptables = instant_remote_process(['docker run --privileged --net=host --pid=host --ipc=host --volume /:/host busybox chroot /host bash -c "iptables -L -n | jc --iptables"'], $server);
|
||||
// ray($iptables);
|
||||
// }
|
||||
// }
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.dashboard');
|
||||
|
@ -68,7 +68,7 @@ public function instantSave()
|
||||
if ($this->application->settings->is_raw_compose_deployment_enabled) {
|
||||
$this->application->oldRawParser();
|
||||
} else {
|
||||
$this->application->oldParser();
|
||||
$this->application->parse();
|
||||
}
|
||||
$this->application->settings->save();
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
use App\Models\Application;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Livewire\Component;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
@ -132,7 +131,7 @@ class General extends Component
|
||||
public function mount()
|
||||
{
|
||||
try {
|
||||
$this->parsedServices = $this->application->oldParser();
|
||||
$this->parsedServices = $this->application->parse();
|
||||
if (is_null($this->parsedServices) || empty($this->parsedServices)) {
|
||||
$this->dispatch('error', 'Failed to parse your docker-compose file. Please check the syntax and try again.');
|
||||
|
||||
@ -197,7 +196,7 @@ public function loadComposeFile($isInit = false)
|
||||
|
||||
// Must reload the application to get the latest database changes
|
||||
// Why? Not sure, but it works.
|
||||
$this->application->refresh();
|
||||
// $this->application->refresh();
|
||||
|
||||
['parsedServices' => $this->parsedServices, 'initialDockerComposeLocation' => $this->initialDockerComposeLocation] = $this->application->loadComposeFile($isInit);
|
||||
if (is_null($this->parsedServices)) {
|
||||
@ -205,7 +204,7 @@ public function loadComposeFile($isInit = false)
|
||||
|
||||
return;
|
||||
}
|
||||
$this->application->oldParser();
|
||||
$this->application->parse();
|
||||
$this->dispatch('success', 'Docker compose file loaded.');
|
||||
$this->dispatch('compose_loaded');
|
||||
$this->dispatch('refreshStorages');
|
||||
|
@ -79,8 +79,15 @@ public function generate_preview($preview_id)
|
||||
|
||||
return;
|
||||
}
|
||||
$fqdn = generateFqdn($this->application->destination->server, $this->application->uuid);
|
||||
if ($this->application->build_pack === 'dockercompose') {
|
||||
$preview->generate_preview_fqdn_compose();
|
||||
$this->application->refresh();
|
||||
$this->dispatch('success', 'Domain generated.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$fqdn = generateFqdn($this->application->destination->server, $this->application->uuid);
|
||||
$url = Url::fromString($fqdn);
|
||||
$template = $this->application->preview_url_template;
|
||||
$host = $url->getHost();
|
||||
|
@ -91,18 +91,16 @@ public function mount()
|
||||
$oneClickDotEnvs->each(function ($value) use ($service) {
|
||||
$key = str()->before($value, '=');
|
||||
$value = str(str()->after($value, '='));
|
||||
$generatedValue = $value;
|
||||
if ($value->contains('SERVICE_')) {
|
||||
$command = $value->after('SERVICE_')->beforeLast('_');
|
||||
$generatedValue = generateEnvValue($command->value(), $service);
|
||||
if ($value) {
|
||||
EnvironmentVariable::create([
|
||||
'key' => $key,
|
||||
'value' => $value,
|
||||
'service_id' => $service->id,
|
||||
'is_build_time' => false,
|
||||
'is_preview' => false,
|
||||
]);
|
||||
}
|
||||
EnvironmentVariable::create([
|
||||
'key' => $key,
|
||||
'value' => $generatedValue,
|
||||
'service_id' => $service->id,
|
||||
'is_build_time' => false,
|
||||
'is_preview' => false,
|
||||
]);
|
||||
|
||||
});
|
||||
}
|
||||
$service->parse(isNew: true);
|
||||
|
@ -76,6 +76,12 @@ public function check_status()
|
||||
{
|
||||
try {
|
||||
GetContainersStatus::run($this->service->server);
|
||||
$this->service->applications->each(function ($application) {
|
||||
$application->refresh();
|
||||
});
|
||||
$this->service->databases->each(function ($database) {
|
||||
$database->refresh();
|
||||
});
|
||||
$this->dispatch('$refresh');
|
||||
} catch (\Exception $e) {
|
||||
return handleError($e, $this);
|
||||
|
@ -26,7 +26,7 @@ public function mount()
|
||||
$this->containerNames = $this->containerNames->merge($this->resource->databases()->pluck('name'));
|
||||
} elseif ($this->resource->type() == 'application') {
|
||||
if ($this->resource->build_pack === 'dockercompose') {
|
||||
$parsed = $this->resource->oldParser();
|
||||
$parsed = $this->resource->parse();
|
||||
$containers = collect(data_get($parsed, 'services'))->keys();
|
||||
$this->containerNames = $containers;
|
||||
} else {
|
||||
|
@ -3,8 +3,6 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\ApplicationDeploymentStatus;
|
||||
use App\Enums\ProxyTypes;
|
||||
use App\Jobs\ServerFilesFromServerJob;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
@ -104,6 +102,8 @@ class Application extends BaseModel
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
private static $parserVersion = '3';
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
protected $appends = ['server_status'];
|
||||
@ -127,7 +127,7 @@ protected static function booted()
|
||||
ApplicationSetting::create([
|
||||
'application_id' => $application->id,
|
||||
]);
|
||||
$application->compose_parsing_version = '2';
|
||||
$application->compose_parsing_version = self::$parserVersion;
|
||||
$application->save();
|
||||
});
|
||||
static::forceDeleting(function ($application) {
|
||||
@ -140,6 +140,7 @@ protected static function booted()
|
||||
$task->delete();
|
||||
}
|
||||
$application->tags()->detach();
|
||||
$application->previews()->delete();
|
||||
});
|
||||
}
|
||||
|
||||
@ -1085,544 +1086,10 @@ public function oldRawParser()
|
||||
instant_remote_process($commands, $this->destination->server, false);
|
||||
}
|
||||
|
||||
public function newParser(int $pull_request_id = 0, ?int $preview_id = null)
|
||||
{
|
||||
$pullRequestId = $pull_request_id;
|
||||
$isPullRequest = $pullRequestId == 0 ? false : true;
|
||||
|
||||
$uuid = data_get($this, 'uuid');
|
||||
$server = data_get($this, 'destination.server');
|
||||
$compose = data_get($this, 'docker_compose_raw');
|
||||
try {
|
||||
$yaml = Yaml::parse($compose);
|
||||
} catch (\Exception $e) {
|
||||
return;
|
||||
}
|
||||
$services = data_get($yaml, 'services', collect([]));
|
||||
$topLevel = collect([
|
||||
'volumes' => collect(data_get($yaml, 'volumes', [])),
|
||||
'networks' => collect(data_get($yaml, 'networks', [])),
|
||||
'configs' => collect(data_get($yaml, 'configs', [])),
|
||||
'secrets' => collect(data_get($yaml, 'secrets', [])),
|
||||
]);
|
||||
|
||||
// If there are predefined volumes, make sure they are not null
|
||||
if ($topLevel->get('volumes')->count() > 0) {
|
||||
$temp = collect([]);
|
||||
foreach ($topLevel['volumes'] as $volumeName => $volume) {
|
||||
if (is_null($volume)) {
|
||||
continue;
|
||||
}
|
||||
$temp->put($volumeName, $volume);
|
||||
}
|
||||
$topLevel['volumes'] = $temp;
|
||||
}
|
||||
// Get the base docker network
|
||||
$baseNetwork = collect([$uuid]);
|
||||
if ($isPullRequest) {
|
||||
$baseNetwork = collect(["{$uuid}-{$pullRequestId}"]);
|
||||
}
|
||||
$parsedServices = collect([]);
|
||||
$fileStorages = $this->fileStorages();
|
||||
// Let's loop through the services
|
||||
foreach ($services as $serviceName => $service) {
|
||||
$image = data_get_str($service, 'image');
|
||||
$restart = data_get_str($service, 'restart', RESTART_MODE);
|
||||
$logging = data_get($service, 'logging');
|
||||
|
||||
if ($server->isLogDrainEnabled() && $this->isLogDrainEnabled()) {
|
||||
$logging = generate_fluentd_configuration();
|
||||
}
|
||||
|
||||
$volumes = collect(data_get($service, 'volumes', []));
|
||||
$networks = collect(data_get($service, 'networks', []));
|
||||
$depends_on = collect(data_get($service, 'depends_on', []));
|
||||
$labels = collect(data_get($service, 'labels', []));
|
||||
$environment = collect(data_get($service, 'environment', []));
|
||||
$buildArgs = collect(data_get($service, 'build.args', []));
|
||||
$environment = $environment->merge($buildArgs);
|
||||
|
||||
$baseName = generateApplicationContainerName(
|
||||
application: $this,
|
||||
pull_request_id: $pullRequestId
|
||||
);
|
||||
$containerName = "$serviceName-$baseName";
|
||||
$volumesParsed = collect([]);
|
||||
|
||||
if ($volumes->count() > 0) {
|
||||
foreach ($volumes as $index => $volume) {
|
||||
$type = null;
|
||||
$source = null;
|
||||
$target = null;
|
||||
$content = null;
|
||||
$isDirectory = false;
|
||||
if (is_string($volume)) {
|
||||
$source = str($volume)->before(':');
|
||||
$target = str($volume)->after(':')->beforeLast(':');
|
||||
$foundConfig = $fileStorages->whereMountPath($target)->first();
|
||||
if (sourceIsLocal($source)) {
|
||||
$type = str('bind');
|
||||
if ($foundConfig) {
|
||||
$contentNotNull_temp = data_get($foundConfig, 'content');
|
||||
if ($contentNotNull_temp) {
|
||||
$content = $contentNotNull_temp;
|
||||
}
|
||||
$isDirectory = data_get($foundConfig, 'is_directory');
|
||||
} else {
|
||||
// By default, we cannot determine if the bind is a directory or not, so we set it to directory
|
||||
$isDirectory = true;
|
||||
}
|
||||
} else {
|
||||
$type = str('volume');
|
||||
}
|
||||
} elseif (is_array($volume)) {
|
||||
$type = data_get_str($volume, 'type');
|
||||
$source = data_get_str($volume, 'source');
|
||||
$target = data_get_str($volume, 'target');
|
||||
$content = data_get($volume, 'content');
|
||||
$isDirectory = (bool) data_get($volume, 'isDirectory', null) || (bool) data_get($volume, 'is_directory', null);
|
||||
|
||||
$foundConfig = $fileStorages->whereMountPath($target)->first();
|
||||
if ($foundConfig) {
|
||||
$contentNotNull_temp = data_get($foundConfig, 'content');
|
||||
if ($contentNotNull_temp) {
|
||||
$content = $contentNotNull_temp;
|
||||
}
|
||||
$isDirectory = data_get($foundConfig, 'is_directory');
|
||||
} else {
|
||||
// if isDirectory is not set (or false) & content is also not set, we assume it is a directory
|
||||
if ((is_null($isDirectory) || ! $isDirectory) && is_null($content)) {
|
||||
$isDirectory = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($type->value() === 'bind') {
|
||||
if ($source->value() === '/var/run/docker.sock') {
|
||||
return $volume;
|
||||
}
|
||||
if ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
||||
return $volume;
|
||||
}
|
||||
$mainDirectory = str(base_configuration_dir().'/applications/'.$uuid);
|
||||
$source = replaceLocalSource($source, $mainDirectory);
|
||||
if ($isPullRequest) {
|
||||
$source = $source."-pr-$pullRequestId";
|
||||
}
|
||||
if (
|
||||
! $this?->settings?->is_preserve_repository_enabled || $foundConfig?->is_based_on_git
|
||||
) {
|
||||
LocalFileVolume::updateOrCreate(
|
||||
[
|
||||
'mount_path' => $target,
|
||||
'resource_id' => $this->id,
|
||||
'resource_type' => get_class($this),
|
||||
],
|
||||
[
|
||||
'fs_path' => $source,
|
||||
'mount_path' => $target,
|
||||
'content' => $content,
|
||||
'is_directory' => $isDirectory,
|
||||
'resource_id' => $this->id,
|
||||
'resource_type' => get_class($this),
|
||||
]
|
||||
);
|
||||
}
|
||||
$volume = "$source:$target";
|
||||
} elseif ($type->value() === 'volume') {
|
||||
if ($topLevel->get('volumes')->has($source->value())) {
|
||||
$temp = $topLevel->get('volumes')->get($source->value());
|
||||
if (data_get($temp, 'driver_opts.type') === 'cifs') {
|
||||
return $volume;
|
||||
}
|
||||
if (data_get($temp, 'driver_opts.type') === 'nfs') {
|
||||
return $volume;
|
||||
}
|
||||
}
|
||||
$slugWithoutUuid = Str::slug($source, '-');
|
||||
$name = "{$uuid}_{$slugWithoutUuid}";
|
||||
if ($isPullRequest) {
|
||||
$name = "{$name}-pr-$pullRequestId";
|
||||
}
|
||||
if (is_string($volume)) {
|
||||
$source = str($volume)->before(':');
|
||||
$target = str($volume)->after(':')->beforeLast(':');
|
||||
$source = $name;
|
||||
$volume = "$source:$target";
|
||||
} elseif (is_array($volume)) {
|
||||
data_set($volume, 'source', $name);
|
||||
}
|
||||
$topLevel->get('volumes')->put($name, [
|
||||
'name' => $name,
|
||||
]);
|
||||
|
||||
LocalPersistentVolume::updateOrCreate(
|
||||
[
|
||||
'mount_path' => $target,
|
||||
'resource_id' => $this->id,
|
||||
'resource_type' => get_class($this),
|
||||
],
|
||||
[
|
||||
'name' => $name,
|
||||
'mount_path' => $target,
|
||||
'resource_id' => $this->id,
|
||||
'resource_type' => get_class($this),
|
||||
]
|
||||
);
|
||||
}
|
||||
dispatch(new ServerFilesFromServerJob($this));
|
||||
$volumesParsed->put($index, $volume);
|
||||
}
|
||||
}
|
||||
|
||||
if ($depends_on?->count() > 0) {
|
||||
if ($isPullRequest) {
|
||||
$newDependsOn = collect([]);
|
||||
$depends_on->each(function ($dependency, $condition) use ($pullRequestId, $newDependsOn) {
|
||||
if (is_numeric($condition)) {
|
||||
$dependency = "$dependency-pr-$pullRequestId";
|
||||
|
||||
$newDependsOn->put($condition, $dependency);
|
||||
} else {
|
||||
$condition = "$condition-pr-$pullRequestId";
|
||||
$newDependsOn->put($condition, $dependency);
|
||||
}
|
||||
});
|
||||
$depends_on = $newDependsOn;
|
||||
}
|
||||
|
||||
}
|
||||
if ($topLevel->get('networks')?->count() > 0) {
|
||||
foreach ($topLevel->get('networks') as $networkName => $network) {
|
||||
if ($networkName === 'default') {
|
||||
continue;
|
||||
}
|
||||
// ignore aliases
|
||||
if ($network['aliases'] ?? false) {
|
||||
continue;
|
||||
}
|
||||
$networkExists = $networks->contains(function ($value, $key) use ($networkName) {
|
||||
return $value == $networkName || $key == $networkName;
|
||||
});
|
||||
if (! $networkExists) {
|
||||
$networks->put($networkName, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
$baseNetworkExists = $networks->contains(function ($value, $_) use ($baseNetwork) {
|
||||
return $value == $baseNetwork;
|
||||
});
|
||||
if (! $baseNetworkExists) {
|
||||
foreach ($baseNetwork as $network) {
|
||||
$topLevel->get('networks')->put($network, [
|
||||
'name' => $network,
|
||||
'external' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
$networks_temp = collect();
|
||||
|
||||
foreach ($networks as $key => $network) {
|
||||
if (gettype($network) === 'string') {
|
||||
// networks:
|
||||
// - appwrite
|
||||
$networks_temp->put($network, null);
|
||||
} elseif (gettype($network) === 'array') {
|
||||
// networks:
|
||||
// default:
|
||||
// ipv4_address: 192.168.203.254
|
||||
$networks_temp->put($key, $network);
|
||||
}
|
||||
}
|
||||
foreach ($baseNetwork as $key => $network) {
|
||||
$networks_temp->put($network, null);
|
||||
}
|
||||
|
||||
if (data_get($this, 'settings.connect_to_docker_network')) {
|
||||
$network = $this->destination->network;
|
||||
$networks_temp->put($network, null);
|
||||
$topLevel->get('networks')->put($network, [
|
||||
'name' => $network,
|
||||
'external' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($environment as $key => $value) {
|
||||
if (is_numeric($key)) {
|
||||
if (is_array($value)) {
|
||||
// - SESSION_SECRET: 123
|
||||
// - SESSION_SECRET:
|
||||
$key = str(collect($value)->keys()->first());
|
||||
$value = str(collect($value)->values()->first());
|
||||
} else {
|
||||
$variable = str($value);
|
||||
if ($variable->contains('=')) {
|
||||
// - SESSION_SECRET=123
|
||||
// - SESSION_SECRET=
|
||||
$key = $variable->before('=');
|
||||
$value = $variable->after('=');
|
||||
} else {
|
||||
// - SESSION_SECRET
|
||||
$key = $variable;
|
||||
$value = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// SESSION_SECRET: 123
|
||||
// SESSION_SECRET:
|
||||
$key = str($key);
|
||||
$value = str($value);
|
||||
}
|
||||
|
||||
// Auto generate FQDN and URL
|
||||
if ($key->startsWith('SERVICE_FQDN') || $value->startsWith('$SERVICE_FQDN') || $value->startsWith('${SERVICE_FQDN')) {
|
||||
if ($value->contains('SERVICE_FQDN')) {
|
||||
$key = str(replaceVariables($value));
|
||||
$value = null;
|
||||
}
|
||||
$name = $key->after('SERVICE_FQDN_')->beforeLast('_')->lower();
|
||||
$fqdn = generateFqdn($server, "{$name->value()}-{$uuid}");
|
||||
|
||||
$url = str($fqdn)->replace('http://', '')->replace('https://', '')->replace('www.', '');
|
||||
$keyUrl = $key->replace('SERVICE_FQDN', 'SERVICE_URL');
|
||||
// TODO: is this needed?
|
||||
// if (substr_count($key->value(), '_') === 3) {
|
||||
// // SERVICE_FQDN_UMAMI_1000
|
||||
// $port = $key->afterLast('_');
|
||||
// } else {
|
||||
// // SERVICE_FQDN_UMAMI
|
||||
// $port = null;
|
||||
// }
|
||||
// if ($port) {
|
||||
// $fqdn = "$fqdn:$port";
|
||||
// }
|
||||
if ($value && get_class($value) === 'Illuminate\Support\Stringable') {
|
||||
$path = $value->value();
|
||||
$fqdn = "$fqdn$path";
|
||||
}
|
||||
|
||||
// ray([
|
||||
// 'key' => $key,
|
||||
// 'value' => $fqdn,
|
||||
// ]);
|
||||
// ray($this->environment_variables()->where('key', $key)->where('application_id', $this->id)->first());
|
||||
$this->environment_variables()->where('key', $key)->where('application_id', $this->id)->firstOrCreate([
|
||||
'key' => $key,
|
||||
'application_id' => $this->id,
|
||||
], [
|
||||
'value' => $fqdn,
|
||||
'is_build_time' => false,
|
||||
'is_preview' => false,
|
||||
]);
|
||||
$this->environment_variables()->where('key', $keyUrl)->where('application_id', $this->id)->firstOrCreate([
|
||||
'key' => $keyUrl,
|
||||
'application_id' => $this->id,
|
||||
], [
|
||||
'value' => $url,
|
||||
'is_build_time' => false,
|
||||
'is_preview' => false,
|
||||
]);
|
||||
} elseif ($value->startsWith('$')) {
|
||||
// If the value is a variable then we will add it to Coolify's DB
|
||||
|
||||
// ${VARIABLE} will be VARIABLE instead
|
||||
$value = str(replaceVariables($value));
|
||||
if ($value->contains(':-')) {
|
||||
$key = $value->before(':');
|
||||
$defaultValue = $value->after(':-');
|
||||
} elseif ($value->contains('-')) {
|
||||
$key = $value->before('-');
|
||||
$defaultValue = $value->after('-');
|
||||
} elseif ($value->contains(':?')) {
|
||||
$key = $value->before(':');
|
||||
$defaultValue = $value->after(':?');
|
||||
} elseif ($value->contains('?')) {
|
||||
$key = $value->before('?');
|
||||
$defaultValue = $value->after('?');
|
||||
} else {
|
||||
$key = $value;
|
||||
$defaultValue = null;
|
||||
}
|
||||
$this->environment_variables()->where('key', $key)->where('application_id', $this->id)->firstOrCreate([
|
||||
'key' => $key,
|
||||
'application_id' => $this->id,
|
||||
'is_preview' => false,
|
||||
], [
|
||||
'value' => $defaultValue,
|
||||
'is_build_time' => false,
|
||||
'is_preview' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
$branch = $this->git_branch;
|
||||
if ($pullRequestId !== 0) {
|
||||
$branch = "pull/{$pullRequestId}/head";
|
||||
}
|
||||
if ($this->environment_variables->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
||||
$environment->put('COOLIFY_BRANCH', $branch);
|
||||
}
|
||||
|
||||
if ($this->environment_variables->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) {
|
||||
$environment->put('COOLIFY_CONTAINER_NAME', $containerName);
|
||||
}
|
||||
add_coolify_default_environment_variables($this->database, $environment, $this->environment_variables);
|
||||
|
||||
// Remove SERVICE_FQDN and SERVICE_URL from environment
|
||||
$environment = $environment->filter(function ($value, $key) {
|
||||
return ! str($key)->startsWith('SERVICE_FQDN') && ! str($key)->startsWith('SERVICE_URL');
|
||||
});
|
||||
|
||||
// Labels
|
||||
$fqdns = collect([]);
|
||||
|
||||
$domains = collect(json_decode($this->docker_compose_domains)) ?? collect([]);
|
||||
if ($domains->count() !== 0) {
|
||||
$fqdns = data_get($domains, "$serviceName.domain");
|
||||
if (! $fqdns) {
|
||||
$fqdns = collect([]);
|
||||
} else {
|
||||
$fqdns = str($fqdns)->explode(',');
|
||||
if ($isPullRequest) {
|
||||
$preview = $this->previews()->find($preview_id);
|
||||
$docker_compose_domains = collect(json_decode(data_get($preview, 'docker_compose_domains')));
|
||||
if ($docker_compose_domains->count() > 0) {
|
||||
$found_fqdn = data_get($docker_compose_domains, "$serviceName.domain");
|
||||
if ($found_fqdn) {
|
||||
$fqdns = collect($found_fqdn);
|
||||
} else {
|
||||
$fqdns = collect([]);
|
||||
}
|
||||
} else {
|
||||
$fqdns = $fqdns->map(function ($fqdn) use ($pullRequestId) {
|
||||
$preview = ApplicationPreview::findPreviewByApplicationAndPullId($this->id, $pullRequestId);
|
||||
$url = Url::fromString($fqdn);
|
||||
$template = $this->preview_url_template;
|
||||
$host = $url->getHost();
|
||||
$schema = $url->getScheme();
|
||||
$random = new Cuid2;
|
||||
$preview_fqdn = str_replace('{{random}}', $random, $template);
|
||||
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
|
||||
$preview_fqdn = str_replace('{{pr_id}}', $pullRequestId, $preview_fqdn);
|
||||
$preview_fqdn = "$schema://$preview_fqdn";
|
||||
$preview->fqdn = $preview_fqdn;
|
||||
$preview->save();
|
||||
|
||||
return $preview_fqdn;
|
||||
});
|
||||
}
|
||||
}
|
||||
$shouldGenerateLabelsExactly = $server->settings->generate_exact_labels;
|
||||
if ($shouldGenerateLabelsExactly) {
|
||||
switch ($server->proxyType()) {
|
||||
case ProxyTypes::TRAEFIK->value:
|
||||
$labels = $labels->merge(
|
||||
fqdnLabelsForTraefik(
|
||||
uuid: $this->uuid,
|
||||
domains: $fqdns,
|
||||
serviceLabels: $labels,
|
||||
generate_unique_uuid: $this->build_pack === 'dockercompose',
|
||||
image: $image,
|
||||
is_force_https_enabled: $this->isForceHttpsEnabled(),
|
||||
is_gzip_enabled: $this->isGzipEnabled(),
|
||||
is_stripprefix_enabled: $this->isStripprefixEnabled(),
|
||||
)
|
||||
);
|
||||
break;
|
||||
case ProxyTypes::CADDY->value:
|
||||
$labels = $labels->merge(
|
||||
fqdnLabelsForCaddy(
|
||||
network: $this->destination->network,
|
||||
uuid: $this->uuid,
|
||||
domains: $fqdns,
|
||||
serviceLabels: $labels,
|
||||
image: $image,
|
||||
is_force_https_enabled: $this->isForceHttpsEnabled(),
|
||||
is_gzip_enabled: $this->isGzipEnabled(),
|
||||
is_stripprefix_enabled: $this->isStripprefixEnabled(),
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$labels = $labels->merge(
|
||||
fqdnLabelsForTraefik(
|
||||
uuid: $this->uuid,
|
||||
domains: $fqdns,
|
||||
serviceLabels: $labels,
|
||||
generate_unique_uuid: $this->build_pack === 'dockercompose',
|
||||
image: $image,
|
||||
is_force_https_enabled: $this->isForceHttpsEnabled(),
|
||||
is_gzip_enabled: $this->isGzipEnabled(),
|
||||
is_stripprefix_enabled: $this->isStripprefixEnabled(),
|
||||
)
|
||||
);
|
||||
$labels = $labels->merge(
|
||||
fqdnLabelsForCaddy(
|
||||
network: $this->destination->network,
|
||||
uuid: $this->uuid,
|
||||
domains: $fqdns,
|
||||
serviceLabels: $labels,
|
||||
image: $image,
|
||||
is_force_https_enabled: $this->isForceHttpsEnabled(),
|
||||
is_gzip_enabled: $this->isGzipEnabled(),
|
||||
is_stripprefix_enabled: $this->isStripprefixEnabled(),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$defaultLabels = defaultLabels(
|
||||
id: $this->id,
|
||||
name: $containerName,
|
||||
pull_request_id: $pullRequestId,
|
||||
type: 'application');
|
||||
$labels = $labels->merge($defaultLabels);
|
||||
|
||||
if ($labels->count() > 0 && $this->settings->is_container_label_escape_enabled) {
|
||||
$labels = $labels->map(function ($value, $key) {
|
||||
return escapeDollarSign($value);
|
||||
});
|
||||
}
|
||||
$payload = collect($service)->merge([
|
||||
'restart' => $restart->value(),
|
||||
'container_name' => $containerName,
|
||||
'volumes' => $volumesParsed,
|
||||
'networks' => $networks_temp,
|
||||
'labels' => $labels,
|
||||
'environment' => $environment,
|
||||
]);
|
||||
|
||||
if ($logging) {
|
||||
$payload['logging'] = $logging;
|
||||
}
|
||||
if ($depends_on->count() > 0) {
|
||||
$payload['depends_on'] = $depends_on;
|
||||
}
|
||||
if ($isPullRequest) {
|
||||
$serviceName = "{$serviceName}-pr-{$pullRequestId}";
|
||||
}
|
||||
$parsedServices->put($serviceName, $payload);
|
||||
|
||||
}
|
||||
|
||||
$topLevel->put('services', $parsedServices);
|
||||
$customOrder = ['services', 'volumes', 'networks', 'configs', 'secrets'];
|
||||
|
||||
$topLevel = $topLevel->sortBy(function ($value, $key) use ($customOrder) {
|
||||
return array_search($key, $customOrder);
|
||||
});
|
||||
$this->docker_compose = Yaml::dump(convertToArray($topLevel), 10, 2);
|
||||
data_forget($this, 'environment_variables');
|
||||
data_forget($this, 'environment_variables_preview');
|
||||
$this->save();
|
||||
|
||||
return $topLevel;
|
||||
}
|
||||
|
||||
public function oldParser(int $pull_request_id = 0, ?int $preview_id = null)
|
||||
public function parse(int $pull_request_id = 0, ?int $preview_id = null)
|
||||
{
|
||||
if ($this->compose_parsing_version === '3') {
|
||||
return $this->newParser($pull_request_id, $preview_id);
|
||||
return newParser($this, $pull_request_id, $preview_id);
|
||||
} elseif ($this->docker_compose_raw) {
|
||||
return parseDockerComposeFile(resource: $this, isNew: false, pull_request_id: $pull_request_id, preview_id: $preview_id);
|
||||
} else {
|
||||
@ -1675,7 +1142,7 @@ public function loadComposeFile($isInit = false)
|
||||
if ($composeFileContent) {
|
||||
$this->docker_compose_raw = $composeFileContent;
|
||||
$this->save();
|
||||
$parsedServices = $this->oldParser();
|
||||
$parsedServices = $this->parse();
|
||||
if ($this->docker_compose_domains) {
|
||||
$json = collect(json_decode($this->docker_compose_domains));
|
||||
$names = collect(data_get($parsedServices, 'services'))->keys()->toArray();
|
||||
|
@ -12,9 +12,9 @@ class ApplicationPreview extends BaseModel
|
||||
protected static function booted()
|
||||
{
|
||||
static::deleting(function ($preview) {
|
||||
if ($preview->application->build_pack === 'dockercompose') {
|
||||
if (data_get($preview, 'application.build_pack') === 'dockercompose') {
|
||||
$server = $preview->application->destination->server;
|
||||
$composeFile = $preview->application->oldParser(pull_request_id: $preview->pull_request_id);
|
||||
$composeFile = $preview->application->parse(pull_request_id: $preview->pull_request_id);
|
||||
$volumes = data_get($composeFile, 'volumes');
|
||||
$networks = data_get($composeFile, 'networks');
|
||||
$networkKeys = collect($networks)->keys();
|
||||
|
@ -2,14 +2,11 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\ProxyTypes;
|
||||
use App\Jobs\ServerFilesFromServerJob;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use OpenApi\Attributes as OA;
|
||||
use Spatie\Url\Url;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
@ -42,6 +39,8 @@ class Service extends BaseModel
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
private static $parserVersion = '3';
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
protected $appends = ['server_status'];
|
||||
@ -49,7 +48,7 @@ class Service extends BaseModel
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function ($service) {
|
||||
$service->compose_parsing_version = '2';
|
||||
$service->compose_parsing_version = self::$parserVersion;
|
||||
$service->save();
|
||||
});
|
||||
}
|
||||
@ -676,6 +675,32 @@ public function extraFields()
|
||||
|
||||
$fields->put('GitLab', $data->toArray());
|
||||
break;
|
||||
case str($image)->contains('code-server'):
|
||||
$data = collect([]);
|
||||
$password = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_64_PASSWORDCODESERVER')->first();
|
||||
if ($password) {
|
||||
$data = $data->merge([
|
||||
'Password' => [
|
||||
'key' => data_get($password, 'key'),
|
||||
'value' => data_get($password, 'value'),
|
||||
'rules' => 'required',
|
||||
'isPassword' => true,
|
||||
],
|
||||
]);
|
||||
}
|
||||
$sudoPassword = $this->environment_variables()->where('key', 'SERVICE_PASSWORD_SUDOCODESERVER')->first();
|
||||
if ($sudoPassword) {
|
||||
$data = $data->merge([
|
||||
'Sudo Password' => [
|
||||
'key' => data_get($sudoPassword, 'key'),
|
||||
'value' => data_get($sudoPassword, 'value'),
|
||||
'rules' => 'required',
|
||||
'isPassword' => true,
|
||||
],
|
||||
]);
|
||||
}
|
||||
$fields->put('Code Server', $data->toArray());
|
||||
break;
|
||||
}
|
||||
}
|
||||
$databases = $this->databases()->get();
|
||||
@ -722,8 +747,8 @@ public function extraFields()
|
||||
$fields->put('PostgreSQL', $data->toArray());
|
||||
break;
|
||||
case str($image)->contains('mysql'):
|
||||
$userVariables = ['SERVICE_USER_MYSQL', 'SERVICE_USER_WORDPRESS'];
|
||||
$passwordVariables = ['SERVICE_PASSWORD_MYSQL', 'SERVICE_PASSWORD_WORDPRESS'];
|
||||
$userVariables = ['SERVICE_USER_MYSQL', 'SERVICE_USER_WORDPRESS', 'MYSQL_USER'];
|
||||
$passwordVariables = ['SERVICE_PASSWORD_MYSQL', 'SERVICE_PASSWORD_WORDPRESS', 'MYSQL_PASSWORD'];
|
||||
$rootPasswordVariables = ['SERVICE_PASSWORD_MYSQLROOT', 'SERVICE_PASSWORD_ROOT'];
|
||||
$dbNameVariables = ['MYSQL_DATABASE'];
|
||||
$mysql_user = $this->environment_variables()->whereIn('key', $userVariables)->first();
|
||||
@ -772,10 +797,10 @@ public function extraFields()
|
||||
$fields->put('MySQL', $data->toArray());
|
||||
break;
|
||||
case str($image)->contains('mariadb'):
|
||||
$userVariables = ['SERVICE_USER_MARIADB', 'SERVICE_USER_WORDPRESS', '_APP_DB_USER'];
|
||||
$passwordVariables = ['SERVICE_PASSWORD_MARIADB', 'SERVICE_PASSWORD_WORDPRESS', '_APP_DB_PASS'];
|
||||
$rootPasswordVariables = ['SERVICE_PASSWORD_MARIADBROOT', 'SERVICE_PASSWORD_ROOT', '_APP_DB_ROOT_PASS'];
|
||||
$dbNameVariables = ['SERVICE_DATABASE_MARIADB', 'SERVICE_DATABASE_WORDPRESS', '_APP_DB_SCHEMA'];
|
||||
$userVariables = ['SERVICE_USER_MARIADB', 'SERVICE_USER_WORDPRESS', '_APP_DB_USER', 'SERVICE_USER_MYSQL', 'MYSQL_USER'];
|
||||
$passwordVariables = ['SERVICE_PASSWORD_MARIADB', 'SERVICE_PASSWORD_WORDPRESS', '_APP_DB_PASS', 'MYSQL_PASSWORD'];
|
||||
$rootPasswordVariables = ['SERVICE_PASSWORD_MARIADBROOT', 'SERVICE_PASSWORD_ROOT', '_APP_DB_ROOT_PASS', 'MYSQL_ROOT_PASSWORD'];
|
||||
$dbNameVariables = ['SERVICE_DATABASE_MARIADB', 'SERVICE_DATABASE_WORDPRESS', '_APP_DB_SCHEMA', 'MYSQL_DATABASE'];
|
||||
$mariadb_user = $this->environment_variables()->whereIn('key', $userVariables)->first();
|
||||
$mariadb_password = $this->environment_variables()->whereIn('key', $passwordVariables)->first();
|
||||
$mariadb_root_password = $this->environment_variables()->whereIn('key', $rootPasswordVariables)->first();
|
||||
@ -822,6 +847,7 @@ public function extraFields()
|
||||
}
|
||||
$fields->put('MariaDB', $data->toArray());
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -956,7 +982,8 @@ public function scheduled_tasks(): HasMany
|
||||
|
||||
public function environment_variables(): HasMany
|
||||
{
|
||||
return $this->hasMany(EnvironmentVariable::class)->orderBy('key', 'asc');
|
||||
|
||||
return $this->hasMany(EnvironmentVariable::class)->orderByRaw("key LIKE 'SERVICE%' DESC, value ASC");
|
||||
}
|
||||
|
||||
public function environment_variables_preview(): HasMany
|
||||
@ -992,531 +1019,10 @@ public function saveComposeConfigs()
|
||||
instant_remote_process($commands, $this->server);
|
||||
}
|
||||
|
||||
public function newParser()
|
||||
{
|
||||
return newParser($this);
|
||||
|
||||
$uuid = data_get($this, 'uuid');
|
||||
$server = data_get($this, 'destination.server');
|
||||
$compose = data_get($this, 'docker_compose_raw');
|
||||
try {
|
||||
$yaml = Yaml::parse($compose);
|
||||
} catch (\Exception $e) {
|
||||
return;
|
||||
}
|
||||
$allServices = get_service_templates();
|
||||
$services = data_get($yaml, 'services', collect([]));
|
||||
$topLevel = collect([
|
||||
'volumes' => collect(data_get($yaml, 'volumes', [])),
|
||||
'networks' => collect(data_get($yaml, 'networks', [])),
|
||||
'configs' => collect(data_get($yaml, 'configs', [])),
|
||||
'secrets' => collect(data_get($yaml, 'secrets', [])),
|
||||
]);
|
||||
// If there are predefined volumes, make sure they are not null
|
||||
if ($topLevel->get('volumes')->count() > 0) {
|
||||
$temp = collect([]);
|
||||
foreach ($topLevel['volumes'] as $volumeName => $volume) {
|
||||
if (is_null($volume)) {
|
||||
continue;
|
||||
}
|
||||
$temp->put($volumeName, $volume);
|
||||
}
|
||||
$topLevel['volumes'] = $temp;
|
||||
}
|
||||
// Get the base docker network
|
||||
$baseNetwork = collect([$uuid]);
|
||||
$parsedServices = collect([]);
|
||||
|
||||
// Let's loop through the services
|
||||
foreach ($services as $serviceName => $service) {
|
||||
if ($serviceName === 'registry') {
|
||||
$tempServiceName = 'docker-registry';
|
||||
} else {
|
||||
$tempServiceName = $serviceName;
|
||||
}
|
||||
if (str(data_get($service, 'image'))->contains('glitchtip')) {
|
||||
$tempServiceName = 'glitchtip';
|
||||
}
|
||||
if ($serviceName === 'supabase-kong') {
|
||||
$tempServiceName = 'supabase';
|
||||
}
|
||||
$serviceDefinition = data_get($allServices, $tempServiceName);
|
||||
$predefinedPort = data_get($serviceDefinition, 'port');
|
||||
if ($serviceName === 'plausible') {
|
||||
$predefinedPort = '8000';
|
||||
}
|
||||
$image = data_get_str($service, 'image');
|
||||
$restart = data_get_str($service, 'restart', RESTART_MODE);
|
||||
$logging = data_get($service, 'logging');
|
||||
|
||||
if ($server->isLogDrainEnabled() && $this->isLogDrainEnabled()) {
|
||||
$logging = generate_fluentd_configuration();
|
||||
}
|
||||
|
||||
$volumes = collect(data_get($service, 'volumes', []));
|
||||
$networks = collect(data_get($service, 'networks', []));
|
||||
$labels = collect(data_get($service, 'labels', []));
|
||||
$environment = collect(data_get($service, 'environment', []));
|
||||
$buildArgs = collect(data_get($service, 'build.args', []));
|
||||
$environment = $environment->merge($buildArgs);
|
||||
$hasHostNetworkMode = data_get($service, 'network_mode') === 'host' ? true : false;
|
||||
|
||||
$containerName = "$serviceName-{$this->uuid}";
|
||||
$isDatabase = isDatabaseImage(data_get_str($service, 'image'));
|
||||
$volumesParsed = collect([]);
|
||||
|
||||
if ($isDatabase) {
|
||||
$savedService = ServiceDatabase::firstOrCreate([
|
||||
'name' => $serviceName,
|
||||
'image' => $image,
|
||||
'service_id' => $this->id,
|
||||
]);
|
||||
} else {
|
||||
$savedService = ServiceApplication::firstOrCreate([
|
||||
'name' => $serviceName,
|
||||
'image' => $image,
|
||||
'service_id' => $this->id,
|
||||
]);
|
||||
}
|
||||
$fileStorages = $savedService->fileStorages();
|
||||
|
||||
// Check if image changed
|
||||
if ($savedService->image !== $image) {
|
||||
$savedService->image = $image;
|
||||
$savedService->save();
|
||||
}
|
||||
if ($volumes->count() > 0) {
|
||||
foreach ($volumes as $index => $volume) {
|
||||
$type = null;
|
||||
$source = null;
|
||||
$target = null;
|
||||
$content = null;
|
||||
$isDirectory = false;
|
||||
if (is_string($volume)) {
|
||||
$source = str($volume)->before(':');
|
||||
$target = str($volume)->after(':')->beforeLast(':');
|
||||
$foundConfig = $fileStorages->whereMountPath($target)->first();
|
||||
if (sourceIsLocal($source)) {
|
||||
$type = str('bind');
|
||||
if ($foundConfig) {
|
||||
$contentNotNull_temp = data_get($foundConfig, 'content');
|
||||
if ($contentNotNull_temp) {
|
||||
$content = $contentNotNull_temp;
|
||||
}
|
||||
$isDirectory = data_get($foundConfig, 'is_directory');
|
||||
} else {
|
||||
// By default, we cannot determine if the bind is a directory or not, so we set it to directory
|
||||
$isDirectory = true;
|
||||
}
|
||||
} else {
|
||||
$type = str('volume');
|
||||
}
|
||||
} elseif (is_array($volume)) {
|
||||
$type = data_get_str($volume, 'type');
|
||||
$source = data_get_str($volume, 'source');
|
||||
$target = data_get_str($volume, 'target');
|
||||
$content = data_get($volume, 'content');
|
||||
$isDirectory = (bool) data_get($volume, 'isDirectory', null) || (bool) data_get($volume, 'is_directory', null);
|
||||
|
||||
$foundConfig = $fileStorages->whereMountPath($target)->first();
|
||||
if ($foundConfig) {
|
||||
$contentNotNull_temp = data_get($foundConfig, 'content');
|
||||
if ($contentNotNull_temp) {
|
||||
$content = $contentNotNull_temp;
|
||||
}
|
||||
$isDirectory = data_get($foundConfig, 'is_directory');
|
||||
} else {
|
||||
// if isDirectory is not set (or false) & content is also not set, we assume it is a directory
|
||||
if ((is_null($isDirectory) || ! $isDirectory) && is_null($content)) {
|
||||
$isDirectory = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($type->value() === 'bind') {
|
||||
if ($source->value() === '/var/run/docker.sock') {
|
||||
return $volume;
|
||||
}
|
||||
if ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
||||
return $volume;
|
||||
}
|
||||
$mainDirectory = str(base_configuration_dir().'/applications/'.$uuid);
|
||||
$source = replaceLocalSource($source, $mainDirectory);
|
||||
|
||||
LocalFileVolume::updateOrCreate(
|
||||
[
|
||||
'mount_path' => $target,
|
||||
'resource_id' => $savedService->id,
|
||||
'resource_type' => get_class($savedService),
|
||||
],
|
||||
[
|
||||
'fs_path' => $source,
|
||||
'mount_path' => $target,
|
||||
'content' => $content,
|
||||
'is_directory' => $isDirectory,
|
||||
'resource_id' => $savedService->id,
|
||||
'resource_type' => get_class($savedService),
|
||||
]
|
||||
);
|
||||
$volume = "$source:$target";
|
||||
} elseif ($type->value() === 'volume') {
|
||||
if ($topLevel->get('volumes')->has($source->value())) {
|
||||
$temp = $topLevel->get('volumes')->get($source->value());
|
||||
if (data_get($temp, 'driver_opts.type') === 'cifs') {
|
||||
return $volume;
|
||||
}
|
||||
if (data_get($temp, 'driver_opts.type') === 'nfs') {
|
||||
return $volume;
|
||||
}
|
||||
}
|
||||
$slugWithoutUuid = Str::slug($source, '-');
|
||||
$name = "{$uuid}_{$slugWithoutUuid}";
|
||||
if (is_string($volume)) {
|
||||
$source = str($volume)->before(':');
|
||||
$target = str($volume)->after(':')->beforeLast(':');
|
||||
$source = $name;
|
||||
$volume = "$source:$target";
|
||||
} elseif (is_array($volume)) {
|
||||
data_set($volume, 'source', $name);
|
||||
}
|
||||
$topLevel->get('volumes')->put($name, [
|
||||
'name' => $name,
|
||||
]);
|
||||
|
||||
LocalPersistentVolume::updateOrCreate(
|
||||
[
|
||||
'mount_path' => $target,
|
||||
'resource_id' => $savedService->id,
|
||||
'resource_type' => get_class($savedService),
|
||||
],
|
||||
[
|
||||
'name' => $name,
|
||||
'mount_path' => $target,
|
||||
'resource_id' => $savedService->id,
|
||||
'resource_type' => get_class($savedService),
|
||||
]
|
||||
);
|
||||
}
|
||||
dispatch(new ServerFilesFromServerJob($savedService));
|
||||
$volumesParsed->put($index, $volume);
|
||||
}
|
||||
}
|
||||
if ($topLevel->get('networks')?->count() > 0) {
|
||||
foreach ($topLevel->get('networks') as $networkName => $network) {
|
||||
if ($networkName === 'default') {
|
||||
continue;
|
||||
}
|
||||
// ignore aliases
|
||||
if ($network['aliases'] ?? false) {
|
||||
continue;
|
||||
}
|
||||
$networkExists = $networks->contains(function ($value, $key) use ($networkName) {
|
||||
return $value == $networkName || $key == $networkName;
|
||||
});
|
||||
if (! $networkExists) {
|
||||
$networks->put($networkName, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
$baseNetworkExists = $networks->contains(function ($value, $_) use ($baseNetwork) {
|
||||
return $value == $baseNetwork;
|
||||
});
|
||||
if (! $baseNetworkExists) {
|
||||
foreach ($baseNetwork as $network) {
|
||||
$topLevel->get('networks')->put($network, [
|
||||
'name' => $network,
|
||||
'external' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
$networks_temp = collect();
|
||||
|
||||
foreach ($networks as $key => $network) {
|
||||
if (gettype($network) === 'string') {
|
||||
// networks:
|
||||
// - appwrite
|
||||
$networks_temp->put($network, null);
|
||||
} elseif (gettype($network) === 'array') {
|
||||
// networks:
|
||||
// default:
|
||||
// ipv4_address: 192.168.203.254
|
||||
$networks_temp->put($key, $network);
|
||||
}
|
||||
}
|
||||
foreach ($baseNetwork as $key => $network) {
|
||||
$networks_temp->put($network, null);
|
||||
}
|
||||
|
||||
// Convert
|
||||
// - SESSION_SECRET: 123 to - SESSION_SECRET=123
|
||||
$convertedServiceVariables = collect([]);
|
||||
foreach ($environment as $variableName => $variable) {
|
||||
if (is_numeric($variableName)) {
|
||||
if (is_array($variable)) {
|
||||
$key = str(collect($variable)->keys()->first());
|
||||
$value = str(collect($variable)->values()->first());
|
||||
$variable = "$key=$value";
|
||||
$convertedServiceVariables->put($variableName, $variable);
|
||||
} elseif (is_string($variable)) {
|
||||
$convertedServiceVariables->put($variableName, $variable);
|
||||
}
|
||||
} elseif (is_string($variableName)) {
|
||||
$convertedServiceVariables->put($variableName, $variable);
|
||||
}
|
||||
}
|
||||
$environment = $convertedServiceVariables;
|
||||
|
||||
// filter magic environments
|
||||
$magicEnvironments = $environment->filter(function ($value, $key) {
|
||||
return str($key)->startsWith('SERVICE_FQDN') || str($key)->startsWith('SERVICE_URL') || str($value)->startsWith('SERVICE_FQDN') || str($value)->startsWith('SERVICE_URL');
|
||||
});
|
||||
if ($magicEnvironments->count() > 0) {
|
||||
foreach ($magicEnvironments as $key => $value) {
|
||||
$key = str($key);
|
||||
$value = str($value);
|
||||
$command = $key->after('SERVICE_')->beforeLast('_');
|
||||
if ($command->value() === 'FQDN') {
|
||||
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
|
||||
if ($value && get_class($value) === 'Illuminate\Support\Stringable' && $value->startsWith('/')) {
|
||||
$path = $value->value();
|
||||
$value = "$fqdn$path";
|
||||
} else {
|
||||
$value = $fqdn;
|
||||
}
|
||||
} elseif ($command->value() === 'URL') {
|
||||
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
|
||||
$value = str($fqdn)->replace('http://', '')->replace('https://', '')->replace('www.', '');
|
||||
}
|
||||
if (! $isDatabase && ! $this->environment_variables()->where('key', $key)->where('service_id', $this->id)->first()) {
|
||||
$savedService->fqdn = $value;
|
||||
$savedService->save();
|
||||
}
|
||||
$this->environment_variables()->where('key', $key)->where('service_id', $this->id)->firstOrCreate([
|
||||
'key' => $key,
|
||||
'service_id' => $this->id,
|
||||
], [
|
||||
'value' => $value,
|
||||
'is_build_time' => false,
|
||||
'is_preview' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
foreach ($environment as $key => $value) {
|
||||
if (is_numeric($key)) {
|
||||
if (is_array($value)) {
|
||||
// - SESSION_SECRET: 123
|
||||
// - SESSION_SECRET:
|
||||
$key = str(collect($value)->keys()->first());
|
||||
$value = str(collect($value)->values()->first());
|
||||
} else {
|
||||
$variable = str($value);
|
||||
if ($variable->contains('=')) {
|
||||
// - SESSION_SECRET=123
|
||||
// - SESSION_SECRET=
|
||||
$key = $variable->before('=');
|
||||
$value = $variable->after('=');
|
||||
} else {
|
||||
// - SESSION_SECRET
|
||||
$key = $variable;
|
||||
$value = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// SESSION_SECRET: 123
|
||||
// SESSION_SECRET:
|
||||
$key = str($key);
|
||||
$value = str($value);
|
||||
}
|
||||
|
||||
// Auto generate FQDN and URL
|
||||
// environment:
|
||||
// - SERVICE_FQDN_UMAMI=/umami
|
||||
// - FQDN=$SERVICE_FQDN_UMAMI
|
||||
// - URL=$SERVICE_URL_UMAMI
|
||||
// - TEST=${TEST:-initial}
|
||||
// - HARDCODED=stuff
|
||||
|
||||
if ($value->startsWith('$')) {
|
||||
$value = str(replaceVariables($value));
|
||||
if ($value->startsWith('SERVICE_')) {
|
||||
// $value = SERVICE_FQDN_UMAMI
|
||||
$command = $value->after('SERVICE_')->beforeLast('_');
|
||||
if ($command->value() === 'FQDN') {
|
||||
if ($magicEnvironments->has($value->value())) {
|
||||
$found = $magicEnvironments->get($value->value());
|
||||
if ($found) {
|
||||
$found = $this->environment_variables()->where('key', $value->value())->where('service_id', $this->id)->first();
|
||||
if ($found) {
|
||||
$value = $found->value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
|
||||
if ($value && get_class($value) === 'Illuminate\Support\Stringable' && $value->startsWith('/')) {
|
||||
$path = $value->value();
|
||||
$value = "$fqdn$path";
|
||||
} else {
|
||||
$value = $fqdn;
|
||||
}
|
||||
}
|
||||
} elseif ($command->value() === 'URL') {
|
||||
if ($magicEnvironments->has($value->value())) {
|
||||
$found = $magicEnvironments->get($value->value());
|
||||
if ($found) {
|
||||
$found = $this->environment_variables()->where('key', $value->value())->where('service_id', $this->id)->first();
|
||||
if ($found) {
|
||||
$value = str($found->value)->replace('http://', '')->replace('https://', '')->replace('www.', '');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
|
||||
$value = str($fqdn)->replace('http://', '')->replace('https://', '')->replace('www.', '');
|
||||
}
|
||||
} else {
|
||||
$value = generateEnvValue($command, $this);
|
||||
}
|
||||
$this->environment_variables()->where('key', $key)->where('service_id', $this->id)->firstOrCreate([
|
||||
'key' => $key,
|
||||
'service_id' => $this->id,
|
||||
], [
|
||||
'value' => $value,
|
||||
'is_build_time' => false,
|
||||
'is_preview' => false,
|
||||
]);
|
||||
} else {
|
||||
if ($value->contains(':-')) {
|
||||
$key = $value->before(':');
|
||||
$value = $value->after(':-');
|
||||
} elseif ($value->contains('-')) {
|
||||
$key = $value->before('-');
|
||||
$value = $value->after('-');
|
||||
} elseif ($value->contains(':?')) {
|
||||
$key = $value->before(':');
|
||||
$value = $value->after(':?');
|
||||
} elseif ($value->contains('?')) {
|
||||
$key = $value->before('?');
|
||||
$value = $value->after('?');
|
||||
} else {
|
||||
$key = $value;
|
||||
$value = null;
|
||||
}
|
||||
$this->environment_variables()->where('key', $key)->where('service_id', $this->id)->firstOrCreate([
|
||||
'key' => $key,
|
||||
'service_id' => $this->id,
|
||||
], [
|
||||
'value' => $value,
|
||||
'is_build_time' => false,
|
||||
'is_preview' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->environment_variables->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) {
|
||||
$environment->put('COOLIFY_CONTAINER_NAME', $containerName);
|
||||
}
|
||||
// Remove SERVICE_FQDN and SERVICE_URL from environment
|
||||
$environment = $environment->filter(function ($value, $key) {
|
||||
return ! str($key)->startsWith('SERVICE_FQDN') && ! str($key)->startsWith('SERVICE_URL');
|
||||
});
|
||||
|
||||
}
|
||||
if ($savedService->serviceType()) {
|
||||
$fqdns = generateServiceSpecificFqdns($savedService);
|
||||
} else {
|
||||
$fqdns = collect(data_get($savedService, 'fqdns'))->filter();
|
||||
}
|
||||
$defaultLabels = defaultLabels($this->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id);
|
||||
$serviceLabels = $labels->merge($defaultLabels);
|
||||
if (! $isDatabase && $fqdns->count() > 0) {
|
||||
if ($fqdns) {
|
||||
$shouldGenerateLabelsExactly = $this->server->settings->generate_exact_labels;
|
||||
if ($shouldGenerateLabelsExactly) {
|
||||
switch ($this->server->proxyType()) {
|
||||
case ProxyTypes::TRAEFIK->value:
|
||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik(
|
||||
uuid: $this->uuid,
|
||||
domains: $fqdns,
|
||||
is_force_https_enabled: true,
|
||||
serviceLabels: $serviceLabels,
|
||||
is_gzip_enabled: $savedService->isGzipEnabled(),
|
||||
is_stripprefix_enabled: $savedService->isStripprefixEnabled(),
|
||||
service_name: $serviceName,
|
||||
image: data_get($service, 'image')
|
||||
));
|
||||
break;
|
||||
case ProxyTypes::CADDY->value:
|
||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy(
|
||||
network: $this->destination->network,
|
||||
uuid: $this->uuid,
|
||||
domains: $fqdns,
|
||||
is_force_https_enabled: true,
|
||||
serviceLabels: $serviceLabels,
|
||||
is_gzip_enabled: $savedService->isGzipEnabled(),
|
||||
is_stripprefix_enabled: $savedService->isStripprefixEnabled(),
|
||||
service_name: $serviceName,
|
||||
image: data_get($service, 'image')
|
||||
));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik(
|
||||
uuid: $this->uuid,
|
||||
domains: $fqdns,
|
||||
is_force_https_enabled: true,
|
||||
serviceLabels: $serviceLabels,
|
||||
is_gzip_enabled: $savedService->isGzipEnabled(),
|
||||
is_stripprefix_enabled: $savedService->isStripprefixEnabled(),
|
||||
service_name: $serviceName,
|
||||
image: data_get($service, 'image')
|
||||
));
|
||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy(
|
||||
network: $this->destination->network,
|
||||
uuid: $this->uuid,
|
||||
domains: $fqdns,
|
||||
is_force_https_enabled: true,
|
||||
serviceLabels: $serviceLabels,
|
||||
is_gzip_enabled: $savedService->isGzipEnabled(),
|
||||
is_stripprefix_enabled: $savedService->isStripprefixEnabled(),
|
||||
service_name: $serviceName,
|
||||
image: data_get($service, 'image')
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
$payload = collect($service)->merge([
|
||||
'restart' => $restart->value(),
|
||||
'container_name' => $containerName,
|
||||
'volumes' => $volumesParsed,
|
||||
'networks' => $networks_temp,
|
||||
'labels' => $serviceLabels,
|
||||
'environment' => $environment,
|
||||
]);
|
||||
|
||||
if ($logging) {
|
||||
$payload['logging'] = $logging;
|
||||
}
|
||||
|
||||
$parsedServices->put($serviceName, $payload);
|
||||
}
|
||||
|
||||
$topLevel->put('services', $parsedServices);
|
||||
$customOrder = ['services', 'volumes', 'networks', 'configs', 'secrets'];
|
||||
|
||||
$topLevel = $topLevel->sortBy(function ($value, $key) use ($customOrder) {
|
||||
return array_search($key, $customOrder);
|
||||
});
|
||||
$this->docker_compose = Yaml::dump(convertToArray($topLevel), 10, 2);
|
||||
data_forget($this, 'environment_variables');
|
||||
data_forget($this, 'environment_variables_preview');
|
||||
$this->save();
|
||||
|
||||
return $topLevel;
|
||||
|
||||
}
|
||||
|
||||
public function parse(bool $isNew = false): Collection
|
||||
{
|
||||
if ($this->compose_parsing_version === '3') {
|
||||
return $this->newParser();
|
||||
return newParser($this);
|
||||
} elseif ($this->docker_compose_raw) {
|
||||
return parseDockerComposeFile($this, $isNew);
|
||||
} else {
|
||||
|
@ -253,7 +253,7 @@ function generateServiceSpecificFqdns(ServiceApplication|Application $resource)
|
||||
|
||||
return $payload;
|
||||
}
|
||||
function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, ?string $image = null, string $redirect_direction = 'both')
|
||||
function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains, bool $is_force_https_enabled = false, $onlyPort = null, ?Collection $serviceLabels = null, ?bool $is_gzip_enabled = true, ?bool $is_stripprefix_enabled = true, ?string $service_name = null, ?string $image = null, string $redirect_direction = 'both', ?string $predefinedPort = null)
|
||||
{
|
||||
$labels = collect([]);
|
||||
if ($serviceLabels) {
|
||||
@ -272,6 +272,9 @@ function fqdnLabelsForCaddy(string $network, string $uuid, Collection $domains,
|
||||
if (is_null($port) && ! is_null($onlyPort)) {
|
||||
$port = $onlyPort;
|
||||
}
|
||||
if (is_null($port) && $predefinedPort) {
|
||||
$port = $predefinedPort;
|
||||
}
|
||||
$labels->push("caddy_{$loop}={$schema}://{$host}");
|
||||
$labels->push("caddy_{$loop}.header=-Server");
|
||||
$labels->push("caddy_{$loop}.try_files={path} /index.html /index.php");
|
||||
|
@ -146,7 +146,6 @@ function generate_default_proxy_configuration(Server $server)
|
||||
'coolify.managed=true',
|
||||
];
|
||||
$config = [
|
||||
'version' => '3.8',
|
||||
'networks' => $array_of_networks->toArray(),
|
||||
'services' => [
|
||||
'traefik' => [
|
||||
|
@ -852,7 +852,7 @@ function parseEnvVariable(Str|string $value)
|
||||
'port' => $port,
|
||||
];
|
||||
}
|
||||
function generateEnvValue(string $command, ?Service $service = null)
|
||||
function generateEnvValue(string $command, Service|Application|null $service = null)
|
||||
{
|
||||
switch ($command) {
|
||||
case 'PASSWORD':
|
||||
@ -927,7 +927,8 @@ function generateEnvValue(string $command, ?Service $service = null)
|
||||
$generatedValue = $token->toString();
|
||||
break;
|
||||
default:
|
||||
$generatedValue = Str::random(16);
|
||||
// $generatedValue = Str::random(16);
|
||||
$generatedValue = null;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1341,7 +1342,6 @@ function parseServiceVolumes($serviceVolumes, $resource, $topLevelVolumes, $pull
|
||||
$isDirectory = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if ($type?->value() === 'bind') {
|
||||
if ($source->value() === '/var/run/docker.sock') {
|
||||
@ -1990,7 +1990,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
} else {
|
||||
$fqdns = collect(data_get($savedService, 'fqdns'))->filter();
|
||||
}
|
||||
ray($savedService);
|
||||
$defaultLabels = defaultLabels($resource->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id);
|
||||
$serviceLabels = $serviceLabels->merge($defaultLabels);
|
||||
if (! $isDatabase && $fqdns->count() > 0) {
|
||||
@ -2076,7 +2075,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
updateCompose($savedService);
|
||||
|
||||
return $service;
|
||||
|
||||
});
|
||||
|
||||
$envs_from_coolify = $resource->environment_variables()->get();
|
||||
@ -2514,7 +2512,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
}
|
||||
}
|
||||
if ($collectedPorts->count() > 0) {
|
||||
// ray($collectedPorts->implode(','));
|
||||
ray($collectedPorts->implode(','));
|
||||
}
|
||||
$definedNetworkExists = $topLevelNetworks->contains(function ($value, $_) use ($definedNetwork) {
|
||||
return $value == $definedNetwork;
|
||||
@ -2864,13 +2862,16 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
||||
return collect($finalServices);
|
||||
}
|
||||
}
|
||||
function newParser(Application|Service $resource, int $pull_request_id = 0)
|
||||
function newParser(Application|Service $resource, int $pull_request_id = 0, ?int $preview_id = null): Collection
|
||||
{
|
||||
$isApplication = $resource instanceof Application;
|
||||
$isService = $resource instanceof Service;
|
||||
|
||||
$uuid = data_get($resource, 'uuid');
|
||||
$compose = data_get($resource, 'docker_compose_raw');
|
||||
if (! $compose) {
|
||||
return collect([]);
|
||||
}
|
||||
|
||||
if ($isApplication) {
|
||||
$nameOfId = 'application_id';
|
||||
@ -2883,13 +2884,13 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
||||
$server = data_get($resource, 'server');
|
||||
$allServices = get_service_templates();
|
||||
} else {
|
||||
return;
|
||||
return collect([]);
|
||||
}
|
||||
|
||||
try {
|
||||
$yaml = Yaml::parse($compose);
|
||||
} catch (\Exception $e) {
|
||||
return;
|
||||
return collect([]);
|
||||
}
|
||||
|
||||
$services = data_get($yaml, 'services', collect([]));
|
||||
@ -2930,6 +2931,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
||||
$depends_on = collect(data_get($service, 'depends_on', []));
|
||||
$labels = collect(data_get($service, 'labels', []));
|
||||
$environment = collect(data_get($service, 'environment', []));
|
||||
$ports = collect(data_get($service, 'ports', []));
|
||||
$buildArgs = collect(data_get($service, 'build.args', []));
|
||||
$environment = $environment->merge($buildArgs);
|
||||
$isDatabase = isDatabaseImage(data_get_str($service, 'image'));
|
||||
@ -2941,6 +2943,7 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
||||
pull_request_id: $pullRequestId
|
||||
);
|
||||
$containerName = "$serviceName-$baseName";
|
||||
$predefinedPort = null;
|
||||
} elseif ($isService) {
|
||||
$containerName = "$serviceName-{$resource->uuid}";
|
||||
|
||||
@ -2979,6 +2982,9 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
||||
$savedService->save();
|
||||
}
|
||||
}
|
||||
|
||||
$originalResource = $isApplication ? $resource : $savedService;
|
||||
|
||||
if ($volumes->count() > 0) {
|
||||
foreach ($volumes as $index => $volume) {
|
||||
$type = null;
|
||||
@ -3028,30 +3034,33 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
||||
}
|
||||
if ($type->value() === 'bind') {
|
||||
if ($source->value() === '/var/run/docker.sock') {
|
||||
return $volume;
|
||||
}
|
||||
if ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
||||
return $volume;
|
||||
}
|
||||
$mainDirectory = str(base_configuration_dir().'/applications/'.$uuid);
|
||||
$source = replaceLocalSource($source, $mainDirectory);
|
||||
$volume = $source->value().':'.$target->value();
|
||||
} elseif ($source->value() === '/tmp' || $source->value() === '/tmp/') {
|
||||
$volume = $source->value().':'.$target->value();
|
||||
} else {
|
||||
$mainDirectory = str(base_configuration_dir().'/applications/'.$uuid);
|
||||
$source = replaceLocalSource($source, $mainDirectory);
|
||||
if ($isApplication && $isPullRequest) {
|
||||
$source = $source."-pr-$pullRequestId";
|
||||
}
|
||||
|
||||
LocalFileVolume::updateOrCreate(
|
||||
[
|
||||
'mount_path' => $target,
|
||||
'resource_id' => $savedService->id,
|
||||
'resource_type' => get_class($savedService),
|
||||
],
|
||||
[
|
||||
'fs_path' => $source,
|
||||
'mount_path' => $target,
|
||||
'content' => $content,
|
||||
'is_directory' => $isDirectory,
|
||||
'resource_id' => $savedService->id,
|
||||
'resource_type' => get_class($savedService),
|
||||
]
|
||||
);
|
||||
$volume = "$source:$target";
|
||||
LocalFileVolume::updateOrCreate(
|
||||
[
|
||||
'mount_path' => $target,
|
||||
'resource_id' => $originalResource->id,
|
||||
'resource_type' => get_class($originalResource),
|
||||
],
|
||||
[
|
||||
'fs_path' => $source,
|
||||
'mount_path' => $target,
|
||||
'content' => $content,
|
||||
'is_directory' => $isDirectory,
|
||||
'resource_id' => $originalResource->id,
|
||||
'resource_type' => get_class($originalResource),
|
||||
]
|
||||
);
|
||||
$volume = "$source:$target";
|
||||
}
|
||||
} elseif ($type->value() === 'volume') {
|
||||
if ($topLevel->get('volumes')->has($source->value())) {
|
||||
$temp = $topLevel->get('volumes')->get($source->value());
|
||||
@ -3064,6 +3073,10 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
||||
}
|
||||
$slugWithoutUuid = Str::slug($source, '-');
|
||||
$name = "{$uuid}_{$slugWithoutUuid}";
|
||||
|
||||
if ($isApplication && $isPullRequest) {
|
||||
$name = "{$name}-pr-$pullRequestId";
|
||||
}
|
||||
if (is_string($volume)) {
|
||||
$source = str($volume)->before(':');
|
||||
$target = str($volume)->after(':')->beforeLast(':');
|
||||
@ -3079,21 +3092,38 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
||||
LocalPersistentVolume::updateOrCreate(
|
||||
[
|
||||
'mount_path' => $target,
|
||||
'resource_id' => $savedService->id,
|
||||
'resource_type' => get_class($savedService),
|
||||
'resource_id' => $originalResource->id,
|
||||
'resource_type' => get_class($originalResource),
|
||||
],
|
||||
[
|
||||
'name' => $name,
|
||||
'mount_path' => $target,
|
||||
'resource_id' => $savedService->id,
|
||||
'resource_type' => get_class($savedService),
|
||||
'resource_id' => $originalResource->id,
|
||||
'resource_type' => get_class($originalResource),
|
||||
]
|
||||
);
|
||||
}
|
||||
dispatch(new ServerFilesFromServerJob($savedService));
|
||||
dispatch(new ServerFilesFromServerJob($originalResource));
|
||||
$volumesParsed->put($index, $volume);
|
||||
}
|
||||
}
|
||||
|
||||
if ($depends_on?->count() > 0) {
|
||||
if ($isApplication && $isPullRequest) {
|
||||
$newDependsOn = collect([]);
|
||||
$depends_on->each(function ($dependency, $condition) use ($pullRequestId, $newDependsOn) {
|
||||
if (is_numeric($condition)) {
|
||||
$dependency = "$dependency-pr-$pullRequestId";
|
||||
|
||||
$newDependsOn->put($condition, $dependency);
|
||||
} else {
|
||||
$condition = "$condition-pr-$pullRequestId";
|
||||
$newDependsOn->put($condition, $dependency);
|
||||
}
|
||||
});
|
||||
$depends_on = $newDependsOn;
|
||||
}
|
||||
}
|
||||
if ($topLevel->get('networks')?->count() > 0) {
|
||||
foreach ($topLevel->get('networks') as $networkName => $network) {
|
||||
if ($networkName === 'default') {
|
||||
@ -3122,6 +3152,27 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Collect/create/update ports
|
||||
$collectedPorts = collect([]);
|
||||
if ($ports->count() > 0) {
|
||||
foreach ($ports as $sport) {
|
||||
if (is_string($sport) || is_numeric($sport)) {
|
||||
$collectedPorts->push($sport);
|
||||
}
|
||||
if (is_array($sport)) {
|
||||
$target = data_get($sport, 'target');
|
||||
$published = data_get($sport, 'published');
|
||||
$protocol = data_get($sport, 'protocol');
|
||||
$collectedPorts->push("$target:$published/$protocol");
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($isService) {
|
||||
$originalResource->ports = $collectedPorts->implode(',');
|
||||
$originalResource->save();
|
||||
}
|
||||
|
||||
$networks_temp = collect();
|
||||
|
||||
foreach ($networks as $key => $network) {
|
||||
@ -3150,27 +3201,18 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
||||
]);
|
||||
}
|
||||
}
|
||||
// convert environment variables a different format
|
||||
$convertedServiceVariables = collect([]);
|
||||
foreach ($environment as $variableName => $variable) {
|
||||
if (is_numeric($variableName)) {
|
||||
if (is_array($variable)) {
|
||||
$key = str(collect($variable)->keys()->first());
|
||||
$value = str(collect($variable)->values()->first());
|
||||
$variable = "$key=$value";
|
||||
$convertedServiceVariables->put($variableName, $variable);
|
||||
} elseif (is_string($variable)) {
|
||||
if (is_numeric($variableName)) {
|
||||
$convertedServiceVariables->put($variable, null);
|
||||
} else {
|
||||
$convertedServiceVariables->put($variableName, $variable);
|
||||
}
|
||||
}
|
||||
} elseif (is_string($variableName)) {
|
||||
$convertedServiceVariables->put($variableName, $variable);
|
||||
}
|
||||
}
|
||||
$environment = $convertedServiceVariables;
|
||||
// convert environment variables to one format
|
||||
$environment = convertComposeEnvironmentToArray($environment);
|
||||
|
||||
// Add Coolify defined environments
|
||||
$allEnvironments = $resource->environment_variables()->get(['key', 'value']);
|
||||
|
||||
$allEnvironments = $allEnvironments->mapWithKeys(function ($item) {
|
||||
return [$item['key'] => $item['value']];
|
||||
});
|
||||
|
||||
// remove $environment from $allEnvironments
|
||||
$coolifyDefinedEnvironments = $allEnvironments->diffKeys($environment);
|
||||
|
||||
// filter magic environments
|
||||
$magicEnvironments = $environment->filter(function ($value, $key) {
|
||||
@ -3178,17 +3220,64 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
||||
|
||||
return str($key)->startsWith('SERVICE_') || str($value)->startsWith('SERVICE_');
|
||||
});
|
||||
ray($magicEnvironments);
|
||||
// TODO: go through all the magic environments and handle all kinds of cases, FQDN, URL, PASSWORD, USER, etc.
|
||||
$normalEnvironments = $environment->diffKeys($magicEnvironments);
|
||||
if ($magicEnvironments->count() > 0) {
|
||||
foreach ($magicEnvironments as $key => $value) {
|
||||
$key = str($key);
|
||||
$value = str($value);
|
||||
$command = $key->after('SERVICE_')->before('_');
|
||||
if ($command->value() === 'FQDN') {
|
||||
$value = str(replaceVariables(str($value)));
|
||||
$originalValue = $value;
|
||||
$keyCommand = $key->after('SERVICE_')->before('_');
|
||||
$valueCommand = $value->after('SERVICE_')->before('_');
|
||||
if ($key->startsWith('SERVICE_FQDN_')) {
|
||||
$fqdnFor = $key->after('SERVICE_FQDN_')->lower()->value();
|
||||
if (str($fqdnFor)->contains('_')) {
|
||||
$fqdnFor = str($fqdnFor)->before('_');
|
||||
}
|
||||
} elseif ($value->startsWith('SERVICE_FQDN_')) {
|
||||
$fqdnFor = $value->after('SERVICE_FQDN_')->lower()->value();
|
||||
if (str($fqdnFor)->contains('_')) {
|
||||
$fqdnFor = str($fqdnFor)->before('_');
|
||||
}
|
||||
} else {
|
||||
$fqdnFor = null;
|
||||
}
|
||||
if ($keyCommand->value() === 'FQDN' || $valueCommand->value() === 'FQDN') {
|
||||
if ($isApplication) {
|
||||
$fqdn = generateFqdn($server, "{$resource->name}-$uuid");
|
||||
} elseif ($isService) {
|
||||
if ($fqdnFor) {
|
||||
$fqdn = generateFqdn($server, "$fqdnFor-$uuid");
|
||||
} else {
|
||||
$fqdn = generateFqdn($server, "{$savedService->name}-$uuid");
|
||||
}
|
||||
}
|
||||
if ($value && get_class($value) === 'Illuminate\Support\Stringable' && $value->startsWith('/')) {
|
||||
$path = $value->value();
|
||||
if ($value === '/') {
|
||||
$value = "$fqdn";
|
||||
} else {
|
||||
$value = "$fqdn$path";
|
||||
}
|
||||
} else {
|
||||
$value = $fqdn;
|
||||
}
|
||||
if (! $isDatabase) {
|
||||
if ($isApplication && is_null($resource->fqdn)) {
|
||||
data_forget($resource, 'environment_variables');
|
||||
data_forget($resource, 'environment_variables_preview');
|
||||
$resource->fqdn = $value;
|
||||
$resource->save();
|
||||
} elseif ($isService && is_null($savedService->fqdn)) {
|
||||
if ($key->startsWith('SERVICE_FQDN_')) {
|
||||
$savedService->fqdn = $value;
|
||||
$savedService->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} elseif ($keyCommand->value() === 'URL' || $valueCommand->value() === 'URL') {
|
||||
if ($isApplication) {
|
||||
$fqdn = generateFqdn($server, "{$resource->name}-{$uuid}");
|
||||
|
||||
} elseif ($isService) {
|
||||
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
|
||||
}
|
||||
@ -3198,6 +3287,64 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
||||
} else {
|
||||
$value = $fqdn;
|
||||
}
|
||||
$value = str($fqdn)->replace('http://', '')->replace('https://', '');
|
||||
} else {
|
||||
$generatedValue = generateEnvValue($valueCommand, $resource);
|
||||
if ($generatedValue) {
|
||||
$value = $generatedValue;
|
||||
}
|
||||
}
|
||||
if (str($fqdnFor)->startsWith('/')) {
|
||||
$fqdnFor = null;
|
||||
}
|
||||
// Lets save the magic value to the environment variables
|
||||
if (! $originalValue->startsWith('/')) {
|
||||
if ($key->startsWith('SERVICE_')) {
|
||||
$originalValue = $key;
|
||||
}
|
||||
$resource->environment_variables()->where('key', $originalValue->value())->where($nameOfId, $resource->id)->firstOrCreate([
|
||||
'key' => $originalValue->value(),
|
||||
$nameOfId => $resource->id,
|
||||
], [
|
||||
'value' => $value,
|
||||
'is_build_time' => false,
|
||||
'is_preview' => false,
|
||||
]);
|
||||
}
|
||||
// Save the original value to the environment variables
|
||||
if ($originalValue->startsWith('SERVICE_')) {
|
||||
$value = "$$originalValue";
|
||||
}
|
||||
$resource->environment_variables()->where('key', $key->value())->where($nameOfId, $resource->id)->firstOrCreate([
|
||||
'key' => $key->value(),
|
||||
$nameOfId => $resource->id,
|
||||
], [
|
||||
'value' => "$value",
|
||||
'is_build_time' => false,
|
||||
'is_preview' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
foreach ($normalEnvironments as $key => $value) {
|
||||
$key = str($key);
|
||||
$value = str($value);
|
||||
if ($value->startsWith('$')) {
|
||||
$value = str(replaceVariables(str($value)));
|
||||
if ($value->contains(':-')) {
|
||||
$key = $value->before(':');
|
||||
$value = $value->after(':-');
|
||||
} elseif ($value->contains('-')) {
|
||||
$key = $value->before('-');
|
||||
$value = $value->after('-');
|
||||
} elseif ($value->contains(':?')) {
|
||||
$key = $value->before(':');
|
||||
$value = $value->after(':?');
|
||||
} elseif ($value->contains('?')) {
|
||||
$key = $value->before('?');
|
||||
$value = $value->after('?');
|
||||
} else {
|
||||
$key = $value;
|
||||
$value = null;
|
||||
}
|
||||
$resource->environment_variables()->where('key', $key)->where($nameOfId, $resource->id)->firstOrCreate([
|
||||
'key' => $key,
|
||||
@ -3209,106 +3356,177 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
||||
]);
|
||||
}
|
||||
}
|
||||
foreach ($environment as $key => $value) {
|
||||
if (is_numeric($key)) {
|
||||
if (is_array($value)) {
|
||||
// - SESSION_SECRET: 123
|
||||
// - SESSION_SECRET:
|
||||
$key = str(collect($value)->keys()->first());
|
||||
$value = str(collect($value)->values()->first());
|
||||
} else {
|
||||
$variable = str($value);
|
||||
if ($variable->contains('=')) {
|
||||
// - SESSION_SECRET=123
|
||||
// - SESSION_SECRET=
|
||||
$key = $variable->before('=');
|
||||
$value = $variable->after('=');
|
||||
} else {
|
||||
// - SESSION_SECRET
|
||||
$key = $variable;
|
||||
$value = null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// SESSION_SECRET: 123
|
||||
// SESSION_SECRET:
|
||||
$key = str($key);
|
||||
$value = str($value);
|
||||
if ($isApplication) {
|
||||
$branch = $originalResource->git_branch;
|
||||
if ($pullRequestId !== 0) {
|
||||
$branch = "pull/{$pullRequestId}/head";
|
||||
}
|
||||
// Auto generate FQDN and URL
|
||||
// environment:
|
||||
// - SERVICE_FQDN_UMAMI=/umami
|
||||
// - FQDN=$SERVICE_FQDN_UMAMI
|
||||
// - URL=$SERVICE_URL_UMAMI
|
||||
// - TEST=${TEST:-initial}
|
||||
// - HARDCODED=stuff
|
||||
if ($value->startsWith('$')) {
|
||||
$value = str(replaceVariables($value));
|
||||
if ($value->startsWith('SERVICE_')) {
|
||||
$command = $value->after('SERVICE_')->before('_');
|
||||
if ($command->value() === 'FQDN') {
|
||||
if ($magicEnvironments->has($value->value())) {
|
||||
$found = $magicEnvironments->get($value->value());
|
||||
if ($found) {
|
||||
$found = $resource->environment_variables()->where('key', $value->value())->where($nameOfId, $resource->id)->first();
|
||||
if ($found) {
|
||||
$value = $found->value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($isApplication) {
|
||||
$fqdn = generateFqdn($server, "{$resource->name}-{$uuid}");
|
||||
|
||||
} elseif ($isService) {
|
||||
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
|
||||
}
|
||||
if ($value && get_class($value) === 'Illuminate\Support\Stringable' && $value->startsWith('/')) {
|
||||
$path = $value->value();
|
||||
$value = "$fqdn$path";
|
||||
} else {
|
||||
$value = $fqdn;
|
||||
}
|
||||
}
|
||||
} elseif ($command->value() === 'URL') {
|
||||
if ($magicEnvironments->has($value->value())) {
|
||||
$found = $magicEnvironments->get($value->value());
|
||||
if ($found) {
|
||||
$found = $resource->environment_variables()->where('key', $value->value())->where($nameOfId, $resource->id)->first();
|
||||
if ($found) {
|
||||
$value = $found->value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($isApplication) {
|
||||
$fqdn = generateFqdn($server, "{$resource->name}-{$uuid}");
|
||||
} elseif ($isService) {
|
||||
$fqdn = generateFqdn($server, "{$savedService->name}-{$uuid}");
|
||||
}
|
||||
$value = str($fqdn)->replace('http://', '')->replace('https://', '')->replace('www.', '');
|
||||
}
|
||||
} else {
|
||||
$value = generateEnvValue($command, $resource);
|
||||
}
|
||||
$resource->environment_variables()->where('key', $key)->where($nameOfId, $resource->id)->firstOrCreate([
|
||||
'key' => $key,
|
||||
$nameOfId => $resource->id,
|
||||
], [
|
||||
'value' => $value,
|
||||
'is_build_time' => false,
|
||||
'is_preview' => false,
|
||||
]);
|
||||
}
|
||||
if ($originalResource->environment_variables->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
||||
$environment->put('COOLIFY_BRANCH', $branch);
|
||||
}
|
||||
}
|
||||
$payload = collect($service)->merge([
|
||||
'restart' => $restart->value(),
|
||||
'container_name' => $containerName,
|
||||
'volumes' => $volumesParsed,
|
||||
'networks' => $networks_temp,
|
||||
'labels' => $labels,
|
||||
'environment' => $environment,
|
||||
]);
|
||||
|
||||
// Add COOLIFY_CONTAINER_NAME to environment
|
||||
if ($resource->environment_variables->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) {
|
||||
$environment->put('COOLIFY_CONTAINER_NAME', $containerName);
|
||||
}
|
||||
|
||||
if ($isApplication) {
|
||||
$domains = collect(json_decode($resource->docker_compose_domains)) ?? collect([]);
|
||||
$fqdns = data_get($domains, "$serviceName.domain");
|
||||
if ($fqdns) {
|
||||
$fqdns = str($fqdns)->explode(',');
|
||||
if ($isPullRequest) {
|
||||
$preview = $resource->previews()->find($preview_id);
|
||||
$docker_compose_domains = collect(json_decode(data_get($preview, 'docker_compose_domains')));
|
||||
if ($docker_compose_domains->count() > 0) {
|
||||
$found_fqdn = data_get($docker_compose_domains, "$serviceName.domain");
|
||||
if ($found_fqdn) {
|
||||
$fqdns = collect($found_fqdn);
|
||||
} else {
|
||||
$fqdns = collect([]);
|
||||
}
|
||||
} else {
|
||||
$fqdns = $fqdns->map(function ($fqdn) use ($pullRequestId, $resource) {
|
||||
$preview = ApplicationPreview::findPreviewByApplicationAndPullId($resource->id, $pullRequestId);
|
||||
$url = Url::fromString($fqdn);
|
||||
$template = $resource->preview_url_template;
|
||||
$host = $url->getHost();
|
||||
$schema = $url->getScheme();
|
||||
$random = new Cuid2;
|
||||
$preview_fqdn = str_replace('{{random}}', $random, $template);
|
||||
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
|
||||
$preview_fqdn = str_replace('{{pr_id}}', $pullRequestId, $preview_fqdn);
|
||||
$preview_fqdn = "$schema://$preview_fqdn";
|
||||
$preview->fqdn = $preview_fqdn;
|
||||
$preview->save();
|
||||
|
||||
return $preview_fqdn;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
$defaultLabels = defaultLabels(
|
||||
id: $resource->id,
|
||||
name: $containerName,
|
||||
pull_request_id: $pullRequestId,
|
||||
type: 'application'
|
||||
);
|
||||
} elseif ($isService) {
|
||||
if ($savedService->serviceType()) {
|
||||
$fqdns = generateServiceSpecificFqdns($savedService);
|
||||
} else {
|
||||
$fqdns = collect(data_get($savedService, 'fqdns'))->filter();
|
||||
}
|
||||
$defaultLabels = defaultLabels($resource->id, $containerName, type: 'service', subType: $isDatabase ? 'database' : 'application', subId: $savedService->id);
|
||||
}
|
||||
// Add COOLIFY_FQDN & COOLIFY_URL to environment
|
||||
if (! $isDatabase && $fqdns?->count() > 0) {
|
||||
$environment->put('COOLIFY_URL', $fqdns->implode(','));
|
||||
|
||||
$urls = $fqdns->map(function ($fqdn) {
|
||||
return str($fqdn)->replace('http://', '')->replace('https://', '');
|
||||
});
|
||||
$environment->put('COOLIFY_FQDN', $urls->implode(','));
|
||||
}
|
||||
|
||||
$serviceLabels = $labels->merge($defaultLabels);
|
||||
if (! $isDatabase && $fqdns?->count() > 0) {
|
||||
if ($isApplication) {
|
||||
$shouldGenerateLabelsExactly = $resource->destination->server->settings->generate_exact_labels;
|
||||
$uuid = $resource->uuid;
|
||||
$network = $resource->destination->network;
|
||||
if ($isPullRequest) {
|
||||
$uuid = "{$resource->uuid}-{$pullRequestId}";
|
||||
}
|
||||
if ($isPullRequest) {
|
||||
$network = "{$resource->destination->network}-{$pullRequestId}";
|
||||
}
|
||||
} else {
|
||||
$shouldGenerateLabelsExactly = $resource->server->settings->generate_exact_labels;
|
||||
$uuid = $resource->uuid;
|
||||
$network = $resource->destination->network;
|
||||
}
|
||||
if ($shouldGenerateLabelsExactly) {
|
||||
switch ($server->proxyType()) {
|
||||
case ProxyTypes::TRAEFIK->value:
|
||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik(
|
||||
uuid: $uuid,
|
||||
domains: $fqdns,
|
||||
is_force_https_enabled: true,
|
||||
serviceLabels: $serviceLabels,
|
||||
is_gzip_enabled: $originalResource->isGzipEnabled(),
|
||||
is_stripprefix_enabled: $originalResource->isStripprefixEnabled(),
|
||||
service_name: $serviceName,
|
||||
image: $image
|
||||
));
|
||||
break;
|
||||
case ProxyTypes::CADDY->value:
|
||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy(
|
||||
network: $network,
|
||||
uuid: $uuid,
|
||||
domains: $fqdns,
|
||||
is_force_https_enabled: true,
|
||||
serviceLabels: $serviceLabels,
|
||||
is_gzip_enabled: $originalResource->isGzipEnabled(),
|
||||
is_stripprefix_enabled: $originalResource->isStripprefixEnabled(),
|
||||
service_name: $serviceName,
|
||||
image: $image,
|
||||
predefinedPort: $predefinedPort
|
||||
));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik(
|
||||
uuid: $uuid,
|
||||
domains: $fqdns,
|
||||
is_force_https_enabled: true,
|
||||
serviceLabels: $serviceLabels,
|
||||
is_gzip_enabled: $originalResource->isGzipEnabled(),
|
||||
is_stripprefix_enabled: $originalResource->isStripprefixEnabled(),
|
||||
service_name: $serviceName,
|
||||
image: $image
|
||||
));
|
||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForCaddy(
|
||||
network: $network,
|
||||
uuid: $uuid,
|
||||
domains: $fqdns,
|
||||
is_force_https_enabled: true,
|
||||
serviceLabels: $serviceLabels,
|
||||
is_gzip_enabled: $originalResource->isGzipEnabled(),
|
||||
is_stripprefix_enabled: $originalResource->isStripprefixEnabled(),
|
||||
service_name: $serviceName,
|
||||
image: $image,
|
||||
predefinedPort: $predefinedPort
|
||||
|
||||
));
|
||||
}
|
||||
}
|
||||
if ($isService) {
|
||||
if (data_get($service, 'restart') === 'no' || data_get($service, 'exclude_from_hc')) {
|
||||
$savedService->update(['exclude_from_status' => true]);
|
||||
}
|
||||
}
|
||||
data_forget($service, 'volumes.*.content');
|
||||
data_forget($service, 'volumes.*.isDirectory');
|
||||
data_forget($service, 'volumes.*.is_directory');
|
||||
data_forget($service, 'exclude_from_hc');
|
||||
|
||||
$payload = collect($service)->merge([
|
||||
'container_name' => $containerName,
|
||||
'restart' => $restart->value(),
|
||||
'networks' => $networks_temp,
|
||||
'labels' => $serviceLabels,
|
||||
]);
|
||||
if ($ports->count() > 0) {
|
||||
$payload['ports'] = $ports;
|
||||
}
|
||||
if ($volumesParsed->count() > 0) {
|
||||
$payload['volumes'] = $volumesParsed;
|
||||
}
|
||||
if ($environment->count() > 0 || $coolifyDefinedEnvironments->count() > 0) {
|
||||
$payload['environment'] = $environment->merge($coolifyDefinedEnvironments);
|
||||
}
|
||||
if ($logging) {
|
||||
$payload['logging'] = $logging;
|
||||
}
|
||||
@ -3318,14 +3536,17 @@ function newParser(Application|Service $resource, int $pull_request_id = 0)
|
||||
if ($isApplication && $isPullRequest) {
|
||||
$serviceName = "{$serviceName}-pr-{$pullRequestId}";
|
||||
}
|
||||
|
||||
$parsedServices->put($serviceName, $payload);
|
||||
}
|
||||
ray($parsedServices);
|
||||
$topLevel->put('services', $parsedServices);
|
||||
$customOrder = ['services', 'volumes', 'networks', 'configs', 'secrets'];
|
||||
|
||||
$topLevel = $topLevel->sortBy(function ($value, $key) use ($customOrder) {
|
||||
return array_search($key, $customOrder);
|
||||
});
|
||||
|
||||
$resource->docker_compose = Yaml::dump(convertToArray($topLevel), 10, 2);
|
||||
data_forget($resource, 'environment_variables');
|
||||
data_forget($resource, 'environment_variables_preview');
|
||||
@ -3374,4 +3595,35 @@ function add_coolify_default_environment_variables(StandaloneRedis|StandalonePos
|
||||
if ($where_to_check != null && $where_to_check->where('key', 'COOLIFY_PROJECT_NAME')->isEmpty()) {
|
||||
$where_to_add->push("COOLIFY_PROJECT_NAME={$resource->project()->name}");
|
||||
}
|
||||
|
||||
function convertComposeEnvironmentToArray($environment)
|
||||
{
|
||||
$convertedServiceVariables = collect([]);
|
||||
foreach ($environment as $variableName => $variableValue) {
|
||||
if (is_array($variableValue)) {
|
||||
$key = str(collect($variableValue)->keys()->first());
|
||||
$value = str(collect($variableValue)->values()->first());
|
||||
} elseif (is_string($variableValue)) {
|
||||
if (str($variableValue)->contains('=')) {
|
||||
$key = str($variableValue)->before('=');
|
||||
$value = str($variableValue)->after('=');
|
||||
} else {
|
||||
if (is_numeric($variableName)) {
|
||||
$key = str($variableValue);
|
||||
$value = null;
|
||||
} else {
|
||||
$key = str($variableName);
|
||||
if ($variableValue) {
|
||||
$value = str($variableValue);
|
||||
} else {
|
||||
$value = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$convertedServiceVariables->put($key->value(), $value?->value() ?? null);
|
||||
}
|
||||
|
||||
return $convertedServiceVariables;
|
||||
|
||||
}
|
||||
|
@ -13,19 +13,18 @@ services:
|
||||
- /data/coolify/backups:/var/www/html/storage/app/backups
|
||||
- /data/coolify/webhooks-during-maintenance:/var/www/html/storage/app/webhooks-during-maintenance
|
||||
environment:
|
||||
- PHP_MEMORY_LIMIT
|
||||
- APP_ID
|
||||
- APP_ENV=production
|
||||
- APP_DEBUG
|
||||
- APP_NAME
|
||||
- APP_NAME=Coolify
|
||||
- APP_ID
|
||||
- APP_KEY
|
||||
- APP_URL
|
||||
- DB_CONNECTION
|
||||
- DB_HOST
|
||||
- DB_PORT
|
||||
- APP_DEBUG
|
||||
- DB_DATABASE
|
||||
- DB_USERNAME
|
||||
- DB_PASSWORD
|
||||
- DB_HOST
|
||||
- DB_PORT
|
||||
- DB_CONNECTION
|
||||
- QUEUE_CONNECTION
|
||||
- REDIS_HOST
|
||||
- REDIS_PASSWORD
|
||||
@ -34,6 +33,7 @@ services:
|
||||
- HORIZON_BALANCE_MAX_SHIFT
|
||||
- HORIZON_BALANCE_COOLDOWN
|
||||
- SSL_MODE=off
|
||||
- PHP_MEMORY_LIMIT
|
||||
- PHP_PM_CONTROL=dynamic
|
||||
- PHP_PM_START_SERVERS=1
|
||||
- PHP_PM_MIN_SPARE_SERVERS=1
|
||||
@ -83,18 +83,20 @@ services:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
soketi:
|
||||
condition: service_healthy
|
||||
postgres:
|
||||
volumes:
|
||||
- coolify-db:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_USER: "${DB_USERNAME:-coolify}"
|
||||
POSTGRES_USER: "${DB_USERNAME}"
|
||||
POSTGRES_PASSWORD: "${DB_PASSWORD}"
|
||||
POSTGRES_DB: "${DB_DATABASE:-coolify}"
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
"CMD-SHELL",
|
||||
"pg_isready -U ${DB_USERNAME:-coolify}",
|
||||
"pg_isready -U ${DB_USERNAME}",
|
||||
"-d",
|
||||
"${DB_DATABASE:-coolify}"
|
||||
]
|
||||
|
@ -71,14 +71,14 @@ services:
|
||||
volumes:
|
||||
- coolify-db:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_USER: "${DB_USERNAME:-coolify}"
|
||||
POSTGRES_USER: "${DB_USERNAME}"
|
||||
POSTGRES_PASSWORD: "${DB_PASSWORD}"
|
||||
POSTGRES_DB: "${DB_DATABASE:-coolify}"
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
"CMD-SHELL",
|
||||
"pg_isready -U ${DB_USERNAME:-coolify}",
|
||||
"pg_isready -U ${DB_USERNAME}",
|
||||
"-d",
|
||||
"${DB_DATABASE:-coolify}"
|
||||
]
|
||||
|
@ -10,6 +10,7 @@ services:
|
||||
depends_on:
|
||||
- postgres
|
||||
- redis
|
||||
- soketi
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
container_name: coolify-db
|
||||
@ -32,4 +33,4 @@ networks:
|
||||
coolify:
|
||||
name: coolify
|
||||
driver: bridge
|
||||
external: true
|
||||
external: false
|
||||
|
@ -4,9 +4,9 @@
|
||||
<h4>{{ data_get($resource, 'name', 'unknown') }}</h4>
|
||||
@endif --}}
|
||||
@if ($fileStorage->is_directory)
|
||||
<h4 class="dark:text-white">Directory Mount</h4>
|
||||
<h4 class="dark:text-white pt-4 border-t dark:border-coolgray-200">Directory Mount</h4>
|
||||
@else
|
||||
<h4 class="dark:text-white">File Mount</h4>
|
||||
<h4 class="dark:text-white pt-4 border-t dark:border-coolgray-200">File Mount</h4>
|
||||
@endif
|
||||
|
||||
<x-forms.input label="Source Path" :value="$fileStorage->fs_path" readonly />
|
||||
|
@ -35,8 +35,6 @@
|
||||
<livewire:project.shared.storages.all :resource="$resource" />
|
||||
@endif
|
||||
@if ($fileStorage->count() > 0)
|
||||
|
||||
<h3 class="mt-4 pt-2 border-t dark:border-coolgray-200">Mounts</h3>
|
||||
<div class="flex flex-col gap-2">
|
||||
@foreach ($fileStorage->sort() as $fileStorage)
|
||||
<livewire:project.service.file-storage :fileStorage="$fileStorage"
|
||||
|
@ -72,7 +72,7 @@ class="px-4 py-2 cursor-pointer hover:bg-gray-100 dark:hover:bg-coolgray-300 tex
|
||||
<x-forms.checkbox instantSave id="is_dns_validation_enabled" label="Enabled" />
|
||||
</div>
|
||||
<x-forms.input id="settings.custom_dns_servers" label="DNS Servers"
|
||||
helper="DNS servers for validation FQDNs againts. A comma separated list of DNS servers."
|
||||
helper="DNS servers to validate FQDNs against. A comma separated list of DNS servers."
|
||||
placeholder="1.1.1.1,8.8.8.8" />
|
||||
</div>
|
||||
|
||||
|
@ -6,11 +6,12 @@ set -e # Exit immediately if a command exits with a non-zero status
|
||||
#set -u # Treat unset variables as an error and exit
|
||||
set -o pipefail # Cause a pipeline to return the status of the last command that exited with a non-zero status
|
||||
|
||||
VERSION="1.3.3"
|
||||
VERSION="1.3.4"
|
||||
DOCKER_VERSION="26.0"
|
||||
|
||||
CDN="https://cdn.coollabs.io/coolify"
|
||||
OS_TYPE=$(grep -w "ID" /etc/os-release | cut -d "=" -f 2 | tr -d '"')
|
||||
ENV_FILE="/data/coolify/source/.env"
|
||||
|
||||
# Check if the OS is manjaro, if so, change it to arch
|
||||
if [ "$OS_TYPE" = "manjaro" ] || [ "$OS_TYPE" = "manjaro-arm" ]; then
|
||||
@ -93,7 +94,10 @@ centos | fedora | rhel | ol | rocky | almalinux | amzn)
|
||||
if ! command -v dnf >/dev/null; then
|
||||
yum install -y dnf >/dev/null
|
||||
fi
|
||||
dnf install -y curl wget git jq >/dev/null
|
||||
if ! command -v curl >/dev/null; then
|
||||
dnf install -y curl >/dev/null
|
||||
fi
|
||||
dnf install -y wget git jq >/dev/null
|
||||
fi
|
||||
;;
|
||||
sles | opensuse-leap | opensuse-tumbleweed)
|
||||
@ -285,15 +289,23 @@ curl -fsSL $CDN/.env.production -o /data/coolify/source/.env.production
|
||||
curl -fsSL $CDN/upgrade.sh -o /data/coolify/source/upgrade.sh
|
||||
|
||||
# Copy .env.example if .env does not exist
|
||||
if [ ! -f /data/coolify/source/.env ]; then
|
||||
cp /data/coolify/source/.env.production /data/coolify/source/.env
|
||||
sed -i "s|APP_ID=.*|APP_ID=$(openssl rand -hex 16)|g" /data/coolify/source/.env
|
||||
sed -i "s|APP_KEY=.*|APP_KEY=base64:$(openssl rand -base64 32)|g" /data/coolify/source/.env
|
||||
sed -i "s|DB_PASSWORD=.*|DB_PASSWORD=$(openssl rand -base64 32)|g" /data/coolify/source/.env
|
||||
sed -i "s|REDIS_PASSWORD=.*|REDIS_PASSWORD=$(openssl rand -base64 32)|g" /data/coolify/source/.env
|
||||
sed -i "s|PUSHER_APP_ID=.*|PUSHER_APP_ID=$(openssl rand -hex 32)|g" /data/coolify/source/.env
|
||||
sed -i "s|PUSHER_APP_KEY=.*|PUSHER_APP_KEY=$(openssl rand -hex 32)|g" /data/coolify/source/.env
|
||||
sed -i "s|PUSHER_APP_SECRET=.*|PUSHER_APP_SECRET=$(openssl rand -hex 32)|g" /data/coolify/source/.env
|
||||
if [ ! -f $ENV_FILE ]; then
|
||||
cp /data/coolify/source/.env.production $ENV_FILE
|
||||
# Generate a secure APP_ID and APP_KEY
|
||||
sed -i "s|^APP_ID=.*|APP_ID=$(openssl rand -hex 16)|" "$ENV_FILE"
|
||||
sed -i "s|^APP_KEY=.*|APP_KEY=base64:$(openssl rand -base64 32)|" "$ENV_FILE"
|
||||
|
||||
# Generate a secure Postgres DB username and password
|
||||
sed -i "s|^DB_USERNAME=.*|DB_USERNAME=$(openssl rand -hex 16)|" "$ENV_FILE"
|
||||
sed -i "s|^DB_PASSWORD=.*|DB_PASSWORD=$(openssl rand -base64 32)|" "$ENV_FILE"
|
||||
|
||||
# Generate a secure Redis password
|
||||
sed -i "s|^REDIS_PASSWORD=.*|REDIS_PASSWORD=$(openssl rand -base64 32)|" "$ENV_FILE"
|
||||
|
||||
# Generate secure Pusher credentials
|
||||
sed -i "s|^PUSHER_APP_ID=.*|PUSHER_APP_ID=$(openssl rand -hex 32)|" "$ENV_FILE"
|
||||
sed -i "s|^PUSHER_APP_KEY=.*|PUSHER_APP_KEY=$(openssl rand -hex 32)|" "$ENV_FILE"
|
||||
sed -i "s|^PUSHER_APP_SECRET=.*|PUSHER_APP_SECRET=$(openssl rand -hex 32)|" "$ENV_FILE"
|
||||
fi
|
||||
|
||||
# Merge .env and .env.production. New values will be added to .env
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
## Do not modify this file. You will lose the ability to autoupdate!
|
||||
|
||||
VERSION="1.0.5"
|
||||
VERSION="1.0.6"
|
||||
CDN="https://cdn.coollabs.io/coolify"
|
||||
|
||||
curl -fsSL $CDN/docker-compose.yml -o /data/coolify/source/docker-compose.yml
|
||||
@ -25,7 +25,8 @@ if grep -q "PUSHER_APP_SECRET=$" /data/coolify/source/.env; then
|
||||
fi
|
||||
|
||||
# Make sure coolify network exists
|
||||
docker network create --attachable coolify 2>/dev/null
|
||||
# It is created when starting Coolify with docker compose
|
||||
# docker network create --attachable coolify 2>/dev/null
|
||||
# docker network create --attachable --driver=overlay coolify-overlay 2>/dev/null
|
||||
|
||||
if [ -f /data/coolify/source/docker-compose.custom.yml ]; then
|
||||
|
@ -8,6 +8,7 @@ services:
|
||||
activepieces:
|
||||
image: "ghcr.io/activepieces/activepieces:latest"
|
||||
environment:
|
||||
- SERVICE_FQDN_ACTIVEPIECES
|
||||
- AP_API_KEY=$SERVICE_PASSWORD_64_APIKEY
|
||||
- AP_ENCRYPTION_KEY=$SERVICE_PASSWORD_ENCRYPTIONKEY
|
||||
- AP_ENGINE_EXECUTABLE_PATH=dist/packages/engine/main.js
|
||||
|
@ -3,9 +3,6 @@ _APP_LOCALE=en
|
||||
_APP_OPTIONS_ABUSE=enabled
|
||||
_APP_OPTIONS_FORCE_HTTPS=disabled
|
||||
_APP_OPENSSL_KEY_V1=
|
||||
_APP_DOMAIN=
|
||||
_APP_DOMAIN_TARGET=
|
||||
_APP_DOMAIN_FUNCTIONS=
|
||||
_APP_CONSOLE_WHITELIST_ROOT=enabled
|
||||
_APP_CONSOLE_WHITELIST_EMAILS=
|
||||
_APP_CONSOLE_WHITELIST_IPS=
|
||||
@ -28,9 +25,6 @@ _APP_REDIS_PASS=
|
||||
_APP_DB_HOST=appwrite-mariadb
|
||||
_APP_DB_PORT=3306
|
||||
_APP_DB_SCHEMA=appwrite
|
||||
_APP_DB_USER=$SERVICE_USER_MYSQL
|
||||
_APP_DB_PASS=$SERVICE_PASSWORD_MYSQL
|
||||
_APP_DB_ROOT_PASS=$SERVICE_PASSWORD_ROOTMYSQL
|
||||
_APP_SMTP_HOST=
|
||||
_APP_SMTP_PORT=
|
||||
_APP_SMTP_SECURE=
|
||||
@ -72,7 +66,6 @@ _APP_FUNCTIONS_CPUS=0
|
||||
_APP_FUNCTIONS_MEMORY=0
|
||||
_APP_FUNCTIONS_MEMORY_SWAP=0
|
||||
_APP_FUNCTIONS_RUNTIMES=node-20.0,php-8.2,python-3.11,ruby-3.2
|
||||
_APP_EXECUTOR_SECRET=$SERVICE_PASSWORD_64_APPWRITE
|
||||
_APP_EXECUTOR_HOST=http://appwrite-executor/v1
|
||||
_APP_EXECUTOR_RUNTIME_NETWORK=appwrite_runtimes
|
||||
_APP_FUNCTIONS_INACTIVE_THRESHOLD=60
|
||||
|
@ -52,8 +52,9 @@ services:
|
||||
- _APP_DB_HOST
|
||||
- _APP_DB_PORT
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_DB_USER=$SERVICE_USER_MARIADB
|
||||
- _APP_DB_PASS=$SERVICE_PASSWORD_MARIADB
|
||||
- _APP_DB_ROOT_PASS=$SERVICE_PASSWORD_MARIADBROOT
|
||||
- _APP_SMTP_HOST
|
||||
- _APP_SMTP_PORT
|
||||
- _APP_SMTP_SECURE
|
||||
@ -92,7 +93,7 @@ services:
|
||||
- _APP_FUNCTIONS_CPUS
|
||||
- _APP_FUNCTIONS_MEMORY
|
||||
- _APP_FUNCTIONS_RUNTIMES
|
||||
- _APP_EXECUTOR_SECRET
|
||||
- _APP_EXECUTOR_SECRET=$SERVICE_PASSWORD_64_APPWRITE
|
||||
- _APP_EXECUTOR_HOST
|
||||
- _APP_LOGGING_PROVIDER
|
||||
- _APP_LOGGING_CONFIG
|
||||
@ -500,7 +501,7 @@ services:
|
||||
- _APP_MAINTENANCE_RETENTION_SCHEDULES
|
||||
|
||||
appwrite-worker-usage:
|
||||
image: appwrite/appwrite:1.5.1
|
||||
image: appwrite/appwrite:1.5
|
||||
entrypoint: worker-usage
|
||||
container_name: appwrite-worker-usage
|
||||
<<: *x-logging
|
||||
@ -527,7 +528,7 @@ services:
|
||||
- _APP_USAGE_AGGREGATION_INTERVAL
|
||||
|
||||
appwrite-worker-usage-dump:
|
||||
image: appwrite/appwrite:1.5.1
|
||||
image: appwrite/appwrite:1.5
|
||||
entrypoint: worker-usage-dump
|
||||
<<: *x-logging
|
||||
container_name: appwrite-worker-usage-dump
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationPreview;
|
||||
use App\Models\GithubApp;
|
||||
use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
@ -9,74 +10,66 @@
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
ray()->clearAll();
|
||||
beforeEach(function () {
|
||||
$this->applicationComposeFile = [
|
||||
'version' => '3.8',
|
||||
'services' => [
|
||||
'app' => [
|
||||
'image' => 'nginx',
|
||||
'environment' => [
|
||||
'SERVICE_FQDN_APP' => '/app',
|
||||
'APP_KEY' => 'base64',
|
||||
'APP_DEBUG' => '${APP_DEBUG:-false}',
|
||||
'APP_URL' => '$SERVICE_FQDN_APP',
|
||||
],
|
||||
'volumes' => [
|
||||
'./nginx:/etc/nginx',
|
||||
'data:/var/www/html',
|
||||
],
|
||||
'depends_on' => [
|
||||
'db',
|
||||
],
|
||||
],
|
||||
'db' => [
|
||||
'image' => 'postgres',
|
||||
'environment' => [
|
||||
'POSTGRES_USER' => '${POSTGRES_USER:-postgres}',
|
||||
'POSTGRES_PASSWORD' => '${POSTGRES_PASSWORD:-postgres}',
|
||||
],
|
||||
'volumes' => [
|
||||
'dbdata:/var/lib/postgresql/data',
|
||||
],
|
||||
'healthcheck' => [
|
||||
'test' => ['CMD', 'pg_isready', '-U', 'postgres'],
|
||||
'interval' => '2s',
|
||||
'timeout' => '10s',
|
||||
'retries' => 10,
|
||||
],
|
||||
'depends_on' => [
|
||||
'app' => [
|
||||
'condition' => 'service_healthy',
|
||||
],
|
||||
],
|
||||
$this->applicationYaml = '
|
||||
version: "3.8"
|
||||
services:
|
||||
app:
|
||||
image: nginx
|
||||
environment:
|
||||
SERVICE_FQDN_APP: /app
|
||||
APP_KEY: base64
|
||||
APP_DEBUG: "${APP_DEBUG:-false}"
|
||||
APP_URL: $SERVICE_FQDN_APP
|
||||
volumes:
|
||||
- "./nginx:/etc/nginx"
|
||||
- "data:/var/www/html"
|
||||
depends_on:
|
||||
- db
|
||||
db:
|
||||
image: postgres
|
||||
environment:
|
||||
POSTGRES_USER: "${POSTGRES_USER:-postgres}"
|
||||
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-postgres}"
|
||||
volumes:
|
||||
- "dbdata:/var/lib/postgresql/data"
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- pg_isready
|
||||
- "-U"
|
||||
- "postgres"
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
depends_on:
|
||||
app:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
default:
|
||||
name: something
|
||||
external: true
|
||||
noinet:
|
||||
driver: bridge
|
||||
internal: true';
|
||||
|
||||
],
|
||||
|
||||
],
|
||||
'networks' => [
|
||||
'default' => [
|
||||
'name' => 'something',
|
||||
'external' => true,
|
||||
],
|
||||
'noinet' => [
|
||||
'driver' => 'bridge',
|
||||
'internal' => true,
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->applicationComposeFileString = Yaml::dump($this->applicationComposeFile, 10, 2);
|
||||
$this->jsonapplicationComposeFile = json_encode($this->applicationComposeFile, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR);
|
||||
$this->applicationComposeFileString = Yaml::parse($this->applicationYaml);
|
||||
|
||||
$this->application = Application::create([
|
||||
'name' => 'Application for tests',
|
||||
'uuid' => 'bcoowoookw0co4cok4sgc4k8',
|
||||
'docker_compose_domains' => json_encode([
|
||||
'app' => [
|
||||
'domain' => 'http://bcoowoookw0co4cok4sgc4k8.127.0.0.1.sslip.io',
|
||||
],
|
||||
]),
|
||||
'preview_url_template' => '{{pr_id}}.{{domain}}',
|
||||
'uuid' => 'bcoowoookw0co4cok4sgc4k8s',
|
||||
'repository_project_id' => 603035348,
|
||||
'git_repository' => 'coollabsio/coolify-examples',
|
||||
'git_branch' => 'main',
|
||||
'base_directory' => '/docker-compose-test',
|
||||
'docker_compose_location' => 'docker-compose.yml',
|
||||
'docker_compose_raw' => $this->applicationComposeFileString,
|
||||
'docker_compose_raw' => $this->applicationYaml,
|
||||
'build_pack' => 'dockercompose',
|
||||
'ports_exposes' => '3000',
|
||||
'environment_id' => 1,
|
||||
@ -85,100 +78,84 @@
|
||||
'source_id' => 1,
|
||||
'source_type' => GithubApp::class,
|
||||
]);
|
||||
$this->application->environment_variables_preview()->where('key', 'APP_DEBUG')->update(['value' => 'true']);
|
||||
$this->applicationPreview = ApplicationPreview::create([
|
||||
'git_type' => 'github',
|
||||
'application_id' => $this->application->id,
|
||||
'pull_request_id' => 1,
|
||||
'pull_request_html_url' => 'https://github.com/coollabsio/coolify-examples/pull/1',
|
||||
]);
|
||||
$this->serviceYaml = '
|
||||
version: "3.8"
|
||||
services:
|
||||
activepieces:
|
||||
image: ghcr.io/activepieces/activepieces:latest
|
||||
environment:
|
||||
- SERVICE_FQDN_ACTIVEPIECES
|
||||
- AP_ENCRYPTION_KEY=$SERVICE_PASSWORD_ENCRYPTIONKEY
|
||||
- AP_EXECUTION_MODE=UNSANDBOXED
|
||||
- AP_FRONTEND_URL=$SERVICE_FQDN_ACTIVEPIECES
|
||||
- AP_TEST=${AP_TEST:-test}
|
||||
volumes:
|
||||
- "dbdata:/var/lib/postgresql/data"
|
||||
depends_on:
|
||||
- postgres
|
||||
- redis
|
||||
activepieces2:
|
||||
image: ghcr.io/activepieces/activepieces:latest
|
||||
environment:
|
||||
TEST: $SERVICE_FQDN_ACTIVEPIECES
|
||||
volumes:
|
||||
- "dbdata:/var/lib/postgresql/data"
|
||||
depends_on:
|
||||
- postgres
|
||||
- redis
|
||||
postgres:
|
||||
image: postgres:latest
|
||||
environment:
|
||||
POSTGRES_DB: activepieces
|
||||
POSTGRES_USER: $SERVICE_USER_POSTGRES
|
||||
POSTGRES_PASSWORD: $SERVICE_PASSWORD_POSTGRES
|
||||
volumes:
|
||||
- "dbdata:/var/lib/postgresql/data"
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- pg_isready
|
||||
- "-U"
|
||||
- "postgres"
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
redis:
|
||||
image: redis:latest
|
||||
volumes:
|
||||
- "redis_data:/data"
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- redis-cli
|
||||
- ping
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
volumes:
|
||||
dbdata:
|
||||
redis_data:
|
||||
networks:
|
||||
default:
|
||||
name: something
|
||||
external: true
|
||||
noinet:
|
||||
driver: bridge
|
||||
internal: true';
|
||||
|
||||
$this->serviceComposeFile = [
|
||||
'services' => [
|
||||
'activepieces' => [
|
||||
'image' => 'ghcr.io/activepieces/activepieces:latest',
|
||||
'environment' => [
|
||||
'SERVICE_FQDN_ACTIVEPIECES_80' => '/app',
|
||||
'AP_API_KEY' => '$SERVICE_PASSWORD_64_APIKEY',
|
||||
'AP_ENCRYPTION_KEY' => '$SERVICE_PASSWORD_ENCRYPTIONKEY',
|
||||
'AP_ENGINE_EXECUTABLE_PATH' => 'dist/packages/engine/main.js',
|
||||
'AP_ENVIRONMENT' => 'prod',
|
||||
'AP_EXECUTION_MODE' => 'UNSANDBOXED',
|
||||
'AP_FRONTEND_URL' => '$SERVICE_FQDN_ACTIVEPIECES',
|
||||
'AP_JWT_SECRET' => '$SERVICE_PASSWORD_64_JWT',
|
||||
'AP_POSTGRES_DATABASE' => 'activepieces',
|
||||
'AP_POSTGRES_HOST' => 'postgres',
|
||||
'AP_POSTGRES_PASSWORD' => '$SERVICE_PASSWORD_POSTGRES',
|
||||
'AP_POSTGRES_PORT' => '5432',
|
||||
'AP_POSTGRES_USERNAME' => '$SERVICE_USER_POSTGRES',
|
||||
'AP_REDIS_HOST' => 'redis',
|
||||
'AP_REDIS_PORT' => '6379',
|
||||
'AP_SANDBOX_RUN_TIME_SECONDS' => '600',
|
||||
'AP_TELEMETRY_ENABLED' => 'true',
|
||||
'AP_TEMPLATES_SOURCE_URL' => 'https://cloud.activepieces.com/api/v1/flow-templates',
|
||||
'AP_TRIGGER_DEFAULT_POLL_INTERVAL' => '5',
|
||||
'AP_WEBHOOK_TIMEOUT_SECONDS' => '30',
|
||||
'AP_TEST' => '${AP_TEST:-test}',
|
||||
|
||||
],
|
||||
'depends_on' => [
|
||||
'postgres' => [
|
||||
'condition' => 'service_healthy',
|
||||
],
|
||||
'redis' => [
|
||||
'condition' => 'service_started',
|
||||
],
|
||||
],
|
||||
'healthcheck' => [
|
||||
'test' => [
|
||||
'CMD',
|
||||
'curl',
|
||||
'-f',
|
||||
'http://127.0.0.1:80',
|
||||
],
|
||||
'interval' => '5s',
|
||||
'timeout' => '20s',
|
||||
'retries' => 10,
|
||||
],
|
||||
],
|
||||
'postgres' => [
|
||||
'image' => 'postgres:latest',
|
||||
'environment' => [
|
||||
'POSTGRES_DB' => 'activepieces',
|
||||
'POSTGRES_PASSWORD' => '$SERVICE_PASSWORD_POSTGRES',
|
||||
'POSTGRES_USER' => '$SERVICE_USER_POSTGRES',
|
||||
],
|
||||
'volumes' => [
|
||||
'dbdata:/var/lib/postgresql/data',
|
||||
],
|
||||
'healthcheck' => [
|
||||
'test' => [
|
||||
'CMD-SHELL',
|
||||
'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}',
|
||||
],
|
||||
'interval' => '5s',
|
||||
'timeout' => '20s',
|
||||
'retries' => 10,
|
||||
],
|
||||
],
|
||||
'redis' => [
|
||||
'image' => 'redis:latest',
|
||||
'volumes' => [
|
||||
'redis_data:/data',
|
||||
],
|
||||
'healthcheck' => [
|
||||
'test' => [
|
||||
'CMD',
|
||||
'redis-cli',
|
||||
'ping',
|
||||
],
|
||||
'interval' => '5s',
|
||||
'timeout' => '20s',
|
||||
'retries' => 10,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
$this->serviceComposeFileString = Yaml::dump($this->serviceComposeFile, 10, 2);
|
||||
$this->jsonServiceComposeFile = json_encode($this->serviceComposeFile, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR);
|
||||
$this->serviceComposeFileString = Yaml::parse($this->serviceYaml);
|
||||
|
||||
$this->service = Service::create([
|
||||
'name' => 'Service for tests',
|
||||
'uuid' => (string) new Cuid2(),
|
||||
'docker_compose_raw' => $this->serviceComposeFileString,
|
||||
'docker_compose_raw' => $this->serviceYaml,
|
||||
'environment_id' => 1,
|
||||
'server_id' => 0,
|
||||
'destination_id' => 0,
|
||||
@ -187,6 +164,7 @@
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
// $this->applicationPreview->forceDelete();
|
||||
$this->application->forceDelete();
|
||||
$this->service->forceDelete();
|
||||
});
|
||||
@ -195,7 +173,7 @@
|
||||
// expect($this->jsonapplicationComposeFile)->toBeJson()->ray();
|
||||
|
||||
// $output = $this->application->newParser();
|
||||
// $outputOld = $this->application->oldParser();
|
||||
// $outputOld = $this->application->parse();
|
||||
// expect($output)->toBeInstanceOf(Collection::class);
|
||||
// expect($outputOld)->toBeInstanceOf(Collection::class);
|
||||
|
||||
@ -274,7 +252,7 @@
|
||||
// expect($this->jsonapplicationComposeFile)->toBeJson()->ray();
|
||||
|
||||
// $output = $this->application->newParser(pull_request_id: $pullRequestId, preview_id: $previewId);
|
||||
// $outputOld = $this->application->oldParser();
|
||||
// $outputOld = $this->application->parse();
|
||||
// expect($output)->toBeInstanceOf(Collection::class);
|
||||
// expect($outputOld)->toBeInstanceOf(Collection::class);
|
||||
|
||||
@ -354,32 +332,25 @@
|
||||
|
||||
// });
|
||||
|
||||
test('ServiceComposeParseNew', function () {
|
||||
ray()->clearAll();
|
||||
$output = $this->service->newParser();
|
||||
// ray('New parser');
|
||||
// ray(data_get($output, 'services.activepieces.environment')->toArray());
|
||||
ray($this->service->environment_variables->pluck('value', 'key')->toArray());
|
||||
// foreach ($this->service->applications as $application) {
|
||||
// ray($application->persistentStorages->pluck('mount_path', 'name')->toArray());
|
||||
// }
|
||||
// foreach ($this->service->databases as $database) {
|
||||
// ray($database->persistentStorages->pluck('mount_path', 'name')->toArray());
|
||||
// }
|
||||
expect($output)->toBeInstanceOf(Collection::class);
|
||||
});
|
||||
// test('ServiceComposeParseNew', function () {
|
||||
// $output = newParser($this->application, pull_request_id: 1, preview_id: $this->applicationPreview->id);
|
||||
// // ray('New parser');
|
||||
// // ray($output->toArray());
|
||||
// ray($this->service->environment_variables_preview->pluck('value', 'key')->toArray());
|
||||
// expect($output)->toBeInstanceOf(Collection::class);
|
||||
// });
|
||||
|
||||
// test('ServiceComposeParseOld', function () {
|
||||
// $output = parseDockerComposeFile($this->service);
|
||||
// ray('Old parser');
|
||||
// ray($output->toArray());
|
||||
// ray($this->service->environment_variables->pluck('value', 'key')->toArray());
|
||||
// foreach ($this->service->applications as $application) {
|
||||
// ray($application->persistentStorages->pluck('mount_path', 'name')->toArray());
|
||||
// }
|
||||
// foreach ($this->service->databases as $database) {
|
||||
// ray($database->persistentStorages->pluck('mount_path', 'name')->toArray());
|
||||
// }
|
||||
// // ray($output->toArray());
|
||||
// // ray($this->service->environment_variables->pluck('value', 'key')->toArray());
|
||||
// // foreach ($this->service->applications as $application) {
|
||||
// // ray($application->persistentStorages->pluck('mount_path', 'name')->toArray());
|
||||
// // }
|
||||
// // foreach ($this->service->databases as $database) {
|
||||
// // ray($database->persistentStorages->pluck('mount_path', 'name')->toArray());
|
||||
// // }
|
||||
// expect($output)->toBeInstanceOf(Collection::class);
|
||||
// });
|
||||
|
||||
@ -388,3 +359,30 @@
|
||||
// $output = instant_remote_process(['docker --version'], $server);
|
||||
// expect($output)->toContain('Docker version');
|
||||
// });
|
||||
|
||||
// test('ConvertComposeEnvironmentToArray', function () {
|
||||
// ray()->clearAll();
|
||||
// $yaml = '
|
||||
// services:
|
||||
// activepieces:
|
||||
// environment:
|
||||
// - SERVICE_FQDN_ACTIVEPIECES=/app
|
||||
// - AP_API_KEY=$SERVICE_PASSWORD_64_APIKEY
|
||||
// activepieces2:
|
||||
// environment:
|
||||
// - SERVICE_FQDN_ACTIVEPIECES=/v1/realtime
|
||||
// postgres:
|
||||
// environment:
|
||||
// - POSTGRES_DB: activepieces
|
||||
// ';
|
||||
// $parsedYaml = Yaml::parse($yaml);
|
||||
// $output = convertComposeEnvironmentToArray($parsedYaml['services']['activepieces']['environment']);
|
||||
// $output2 = convertComposeEnvironmentToArray($parsedYaml['services']['activepieces2']['environment']);
|
||||
// $dboutput = convertComposeEnvironmentToArray($parsedYaml['services']['postgres']['environment']);
|
||||
// ray($output);
|
||||
// ray($output2);
|
||||
// ray($dboutput);
|
||||
// expect($output)->toBeInstanceOf(Collection::class);
|
||||
// expect($output2)->toBeInstanceOf(Collection::class);
|
||||
// expect($dboutput)->toBeInstanceOf(Collection::class);
|
||||
// });
|
||||
|
Loading…
Reference in New Issue
Block a user