diff --git a/.github/workflows/coolify-production-build.yml b/.github/workflows/coolify-production-build.yml index d7244fc84..ad0878205 100644 --- a/.github/workflows/coolify-production-build.yml +++ b/.github/workflows/coolify-production-build.yml @@ -12,6 +12,7 @@ on: - docker/coolify-realtime/Dockerfile - docker/testing-host/Dockerfile - templates/** + - CHANGELOG.md env: GITHUB_REGISTRY: ghcr.io diff --git a/.github/workflows/coolify-staging-build.yml b/.github/workflows/coolify-staging-build.yml index bcb65ecbf..ff6b553df 100644 --- a/.github/workflows/coolify-staging-build.yml +++ b/.github/workflows/coolify-staging-build.yml @@ -12,6 +12,7 @@ on: - docker/coolify-realtime/Dockerfile - docker/testing-host/Dockerfile - templates/** + - CHANGELOG.md env: GITHUB_REGISTRY: ghcr.io diff --git a/.github/workflows/generate-changelog.yml b/.github/workflows/generate-changelog.yml new file mode 100644 index 000000000..ee5af6b3d --- /dev/null +++ b/.github/workflows/generate-changelog.yml @@ -0,0 +1,36 @@ +name: Generate Changelog + +on: + push: + branches: [ main ] + workflow_dispatch: + +permissions: + contents: write + +jobs: + changelog: + name: Generate changelog + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Generate changelog + uses: orhun/git-cliff-action@v4 + with: + config: cliff.toml + args: --verbose + env: + OUTPUT: CHANGELOG.md + GITHUB_REPO: ${{ github.repository }} + + - name: Commit + run: | + git config user.name 'github-actions[bot]' + git config user.email 'github-actions[bot]@users.noreply.github.com' + git add CHANGELOG.md + git commit -m "docs: update changelog" + git push https://${{ secrets.GITHUB_TOKEN }}@github.com/${GITHUB_REPOSITORY}.git main diff --git a/.gitignore b/.gitignore index d7ee7e96c..541c8e920 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ scripts/load-test/* .env.dusk.local docker/coolify-realtime/node_modules .DS_Store +Changelog.md diff --git a/README.md b/README.md index b7a5fc3eb..cafff116f 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ Special thanks to our biggest sponsors! * [SupaGuide](https://supa.guide/?ref=coolify.io) - A comprehensive resource hub offering guides and tutorials for web development using Supabase. * [GoldenVM](https://billing.goldenvm.com/?ref=coolify.io) - A cloud hosting provider offering scalable infrastructure solutions for businesses of all sizes. * [Tigris](https://tigrisdata.com/?ref=coolify.io) - A fully managed serverless object storage service compatible with Amazon S3 API. Offers high performance, scalability, and built-in search capabilities for efficient data management. +* [Convex](https://convex.link/coolify.io) - Convex is the open-source reactive database for web app developers. * [Cloudify.ro](https://cloudify.ro/?ref=coolify.io) - A cloud hosting provider offering scalable infrastructure solutions for businesses of all sizes. * [Syntaxfm](https://syntax.fm/?ref=coolify.io) - Podcast for web developers. * [PFGlabs](https://pfglabs.com/?ref=coolify.io) - Build real project with Golang. diff --git a/app/Actions/Service/StartService.php b/app/Actions/Service/StartService.php index 1dfaf6c49..d48594e62 100644 --- a/app/Actions/Service/StartService.php +++ b/app/Actions/Service/StartService.php @@ -12,19 +12,24 @@ class StartService public string $jobQueue = 'high'; - public function handle(Service $service) + public function handle(Service $service, bool $pullLatestImages = false, bool $stopBeforeStart = false) { + $service->parse(); + if ($stopBeforeStart) { + StopService::run(service: $service, dockerCleanup: false); + } $service->saveComposeConfigs(); $commands[] = 'cd '.$service->workdir(); $commands[] = "echo 'Saved configuration files to {$service->workdir()}.'"; + if ($pullLatestImages) { + $commands[] = "echo 'Pulling images.'"; + $commands[] = 'docker compose pull'; + } if ($service->networks()->count() > 0) { $commands[] = "echo 'Creating Docker network.'"; $commands[] = "docker network inspect $service->uuid >/dev/null 2>&1 || docker network create --attachable $service->uuid"; } $commands[] = 'echo Starting service.'; - $commands[] = "echo 'Pulling images.'"; - $commands[] = 'docker compose pull'; - $commands[] = "echo 'Starting containers.'"; $commands[] = 'docker compose up -d --remove-orphans --force-recreate --build'; $commands[] = "docker network connect $service->uuid coolify-proxy >/dev/null 2>&1 || true"; if (data_get($service, 'connect_to_docker_network')) { diff --git a/app/Actions/Shared/PullImage.php b/app/Actions/Shared/PullImage.php deleted file mode 100644 index 4bd1cf453..000000000 --- a/app/Actions/Shared/PullImage.php +++ /dev/null @@ -1,28 +0,0 @@ -saveComposeConfigs(); - - $commands[] = 'cd '.$resource->workdir(); - $commands[] = "echo 'Saved configuration files to {$resource->workdir()}.'"; - $commands[] = 'docker compose pull'; - - $server = data_get($resource, 'server'); - - if (! $server) { - return; - } - - instant_remote_process($commands, $resource->server); - } -} diff --git a/app/Http/Controllers/Api/ApplicationsController.php b/app/Http/Controllers/Api/ApplicationsController.php index 485563818..ff926bf70 100644 --- a/app/Http/Controllers/Api/ApplicationsController.php +++ b/app/Http/Controllers/Api/ApplicationsController.php @@ -1388,6 +1388,108 @@ class ApplicationsController extends Controller return response()->json($this->removeSensitiveData($application)); } + #[OA\Get( + summary: 'Get application logs.', + description: 'Get application logs by UUID.', + path: '/applications/{uuid}/logs', + operationId: 'get-application-logs-by-uuid', + security: [ + ['bearerAuth' => []], + ], + tags: ['Applications'], + parameters: [ + new OA\Parameter( + name: 'uuid', + in: 'path', + description: 'UUID of the application.', + required: true, + schema: new OA\Schema( + type: 'string', + format: 'uuid', + ) + ), + new OA\Parameter( + name: 'lines', + in: 'query', + description: 'Number of lines to show from the end of the logs.', + required: false, + schema: new OA\Schema( + type: 'integer', + format: 'int32', + default: 100, + ) + ), + ], + responses: [ + new OA\Response( + response: 200, + description: 'Get application logs by UUID.', + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'logs' => ['type' => 'string'], + ] + ) + ), + ] + ), + new OA\Response( + response: 401, + ref: '#/components/responses/401', + ), + new OA\Response( + response: 400, + ref: '#/components/responses/400', + ), + new OA\Response( + response: 404, + ref: '#/components/responses/404', + ), + ] + )] + public function logs_by_uuid(Request $request) + { + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + $uuid = $request->route('uuid'); + if (! $uuid) { + return response()->json(['message' => 'UUID is required.'], 400); + } + $application = Application::ownedByCurrentTeamAPI($teamId)->where('uuid', $request->uuid)->first(); + if (! $application) { + return response()->json(['message' => 'Application not found.'], 404); + } + + $containers = getCurrentApplicationContainerStatus($application->destination->server, $application->id); + + if ($containers->count() == 0) { + return response()->json([ + 'message' => 'Application is not running.', + ], 400); + } + + $container = $containers->first(); + + $status = getContainerStatus($application->destination->server, $container['Names']); + if ($status !== 'running') { + return response()->json([ + 'message' => 'Application is not running.', + ], 400); + } + + $lines = $request->query->get('lines', 100) ?: 100; + $logs = getContainerLogs($application->destination->server, $container['ID'], $lines); + + return response()->json([ + 'logs' => $logs, + ]); + } + #[OA\Delete( summary: 'Delete', description: 'Delete application by UUID.', diff --git a/app/Http/Controllers/Webhook/Bitbucket.php b/app/Http/Controllers/Webhook/Bitbucket.php index 8c74f95e5..b629daf54 100644 --- a/app/Http/Controllers/Webhook/Bitbucket.php +++ b/app/Http/Controllers/Webhook/Bitbucket.php @@ -37,7 +37,7 @@ class Bitbucket extends Controller $headers = $request->headers->all(); $x_bitbucket_token = data_get($headers, 'x-hub-signature.0', ''); $x_bitbucket_event = data_get($headers, 'x-event-key.0', ''); - $handled_events = collect(['repo:push', 'pullrequest:created', 'pullrequest:rejected', 'pullrequest:fulfilled']); + $handled_events = collect(['repo:push', 'pullrequest:updated', 'pullrequest:created', 'pullrequest:rejected', 'pullrequest:fulfilled']); if (! $handled_events->contains($x_bitbucket_event)) { return response([ 'status' => 'failed', @@ -48,14 +48,15 @@ class Bitbucket extends Controller $branch = data_get($payload, 'push.changes.0.new.name'); $full_name = data_get($payload, 'repository.full_name'); $commit = data_get($payload, 'push.changes.0.new.target.hash'); - if (! $branch) { + + if (!$branch) { return response([ 'status' => 'failed', 'message' => 'Nothing to do. No branch found in the request.', ]); } } - if ($x_bitbucket_event === 'pullrequest:created' || $x_bitbucket_event === 'pullrequest:rejected' || $x_bitbucket_event === 'pullrequest:fulfilled') { + if ($x_bitbucket_event === 'pullrequest:updated' || $x_bitbucket_event === 'pullrequest:created' || $x_bitbucket_event === 'pullrequest:rejected' || $x_bitbucket_event === 'pullrequest:fulfilled') { $branch = data_get($payload, 'pullrequest.destination.branch.name'); $base_branch = data_get($payload, 'pullrequest.source.branch.name'); $full_name = data_get($payload, 'repository.full_name'); @@ -119,7 +120,7 @@ class Bitbucket extends Controller ]); } } - if ($x_bitbucket_event === 'pullrequest:created') { + if ($x_bitbucket_event === 'pullrequest:created' || $x_bitbucket_event === 'pullrequest:updated') { if ($application->isPRDeployable()) { $deployment_uuid = new Cuid2; $found = ApplicationPreview::where('application_id', $application->id)->where('pull_request_id', $pull_request_id)->first(); diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 3be186b66..67708bd32 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -253,6 +253,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue return; } try { + // Make sure the private key is stored in the filesystem + $this->server->privateKey->storeInFileSystem(); + // Generate custom host<->ip mapping $allContainers = instant_remote_process(["docker network inspect {$this->destination->network} -f '{{json .Containers}}' "], $this->server); diff --git a/app/Livewire/Project/New/Select.php b/app/Livewire/Project/New/Select.php index b645a8915..396930c40 100644 --- a/app/Livewire/Project/New/Select.php +++ b/app/Livewire/Project/New/Select.php @@ -81,7 +81,7 @@ class Select extends Component public function loadServices() { - $services = get_service_templates(true); + $services = get_service_templates(); $services = collect($services)->map(function ($service, $key) { $default_logo = 'images/default.webp'; $logo = data_get($service, 'logo', $default_logo); diff --git a/app/Livewire/Project/Service/Navbar.php b/app/Livewire/Project/Service/Navbar.php index 915fb54c6..e6a1271e1 100644 --- a/app/Livewire/Project/Service/Navbar.php +++ b/app/Livewire/Project/Service/Navbar.php @@ -4,7 +4,6 @@ namespace App\Livewire\Project\Service; use App\Actions\Service\StartService; use App\Actions\Service\StopService; -use App\Actions\Shared\PullImage; use App\Enums\ProcessStatus; use App\Events\ServiceStatusChanged; use App\Models\Service; @@ -85,8 +84,7 @@ class Navbar extends Component public function start() { - $this->service->parse(); - $activity = StartService::run($this->service); + $activity = StartService::run($this->service, pullLatestImages: true); $this->dispatch('activityMonitor', $activity->id); } @@ -98,8 +96,7 @@ class Navbar extends Component $activity->properties->status = ProcessStatus::ERROR->value; $activity->save(); } - $this->service->parse(); - $activity = StartService::run($this->service); + $activity = StartService::run($this->service, pullLatestImages: true, stopBeforeStart: true); $this->dispatch('activityMonitor', $activity->id); } catch (\Exception $e) { $this->dispatch('error', $e->getMessage()); @@ -129,10 +126,7 @@ class Navbar extends Component return; } - StopService::run(service: $this->service, dockerCleanup: false); - $this->service->parse(); - $this->dispatch('imagePulled'); - $activity = StartService::run($this->service); + $activity = StartService::run($this->service, stopBeforeStart: true); $this->dispatch('activityMonitor', $activity->id); } @@ -144,11 +138,7 @@ class Navbar extends Component return; } - PullImage::run($this->service); - StopService::run(service: $this->service, dockerCleanup: false); - $this->service->parse(); - $this->dispatch('imagePulled'); - $activity = StartService::run($this->service); + $activity = StartService::run($this->service, pullLatestImages: true, stopBeforeStart: true); $this->dispatch('activityMonitor', $activity->id); } diff --git a/app/Notifications/Application/DeploymentFailed.php b/app/Notifications/Application/DeploymentFailed.php index 0c09b1dbd..dec361e78 100644 --- a/app/Notifications/Application/DeploymentFailed.php +++ b/app/Notifications/Application/DeploymentFailed.php @@ -44,7 +44,7 @@ class DeploymentFailed extends CustomEmailNotification if (str($this->fqdn)->explode(',')->count() > 1) { $this->fqdn = str($this->fqdn)->explode(',')->first(); } - $this->deployment_url = base_url()."/project/{$this->project_uuid}/environments/{$this->environment_uuid}/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}"; + $this->deployment_url = base_url()."/project/{$this->project_uuid}/environment/{$this->environment_uuid}/application/{$this->application->uuid}/deployment/{$this->deployment_uuid}"; } public function via(object $notifiable): array @@ -175,9 +175,9 @@ class DeploymentFailed extends CustomEmailNotification } } - $description .= "\n\n**Project:** ".data_get($this->application, 'environment.project.name'); - $description .= "\n**Environment:** {$this->environment_name}"; - $description .= "\n**Deployment Logs:** {$this->deployment_url}"; + $description .= "\n\n*Project:* ".data_get($this->application, 'environment.project.name'); + $description .= "\n*Environment:* {$this->environment_name}"; + $description .= "\n*<{$this->deployment_url}|Deployment Logs>*"; return new SlackMessage( title: $title, diff --git a/app/Notifications/Application/DeploymentSuccess.php b/app/Notifications/Application/DeploymentSuccess.php index e1067e9bc..c6bbf43ac 100644 --- a/app/Notifications/Application/DeploymentSuccess.php +++ b/app/Notifications/Application/DeploymentSuccess.php @@ -195,9 +195,9 @@ class DeploymentSuccess extends CustomEmailNotification } } - $description .= "\n\n**Project:** ".data_get($this->application, 'environment.project.name'); - $description .= "\n**Environment:** {$this->environment_name}"; - $description .= "\n**Deployment Logs:** {$this->deployment_url}"; + $description .= "\n\n*Project:* ".data_get($this->application, 'environment.project.name'); + $description .= "\n*Environment:* {$this->environment_name}"; + $description .= "\n*<{$this->deployment_url}|Deployment Logs>*"; return new SlackMessage( title: $title, diff --git a/app/Notifications/Application/StatusChanged.php b/app/Notifications/Application/StatusChanged.php index 669f6e584..34c5ed747 100644 --- a/app/Notifications/Application/StatusChanged.php +++ b/app/Notifications/Application/StatusChanged.php @@ -103,9 +103,9 @@ class StatusChanged extends CustomEmailNotification $title = 'Application stopped'; $description = "{$this->resource_name} has been stopped"; - $description .= "\n\n**Project:** ".data_get($this->resource, 'environment.project.name'); - $description .= "\n**Environment:** {$this->environment_name}"; - $description .= "\n**Application URL:** {$this->resource_url}"; + $description .= "\n\n*Project:* ".data_get($this->resource, 'environment.project.name'); + $description .= "\n*Environment:* {$this->environment_name}"; + $description .= "\n*Application URL:* {$this->resource_url}"; return new SlackMessage( title: $title, diff --git a/app/Notifications/Container/ContainerRestarted.php b/app/Notifications/Container/ContainerRestarted.php index 68fc6b019..f6ae69481 100644 --- a/app/Notifications/Container/ContainerRestarted.php +++ b/app/Notifications/Container/ContainerRestarted.php @@ -93,7 +93,7 @@ class ContainerRestarted extends CustomEmailNotification $description = "A resource ({$this->name}) has been restarted automatically on {$this->server->name}"; if ($this->url) { - $description .= "\n**Resource URL:** {$this->url}"; + $description .= "\n*Resource URL:* {$this->url}"; } return new SlackMessage( diff --git a/app/Notifications/Container/ContainerStopped.php b/app/Notifications/Container/ContainerStopped.php index 59ad7ae4e..fc9410a85 100644 --- a/app/Notifications/Container/ContainerStopped.php +++ b/app/Notifications/Container/ContainerStopped.php @@ -93,7 +93,7 @@ class ContainerStopped extends CustomEmailNotification $description = "A resource ({$this->name}) has been stopped unexpectedly on {$this->server->name}"; if ($this->url) { - $description .= "\n**Resource URL:** {$this->url}"; + $description .= "\n*Resource URL:* {$this->url}"; } return new SlackMessage( diff --git a/app/Notifications/Database/BackupFailed.php b/app/Notifications/Database/BackupFailed.php index 6dcb70583..a19fb0431 100644 --- a/app/Notifications/Database/BackupFailed.php +++ b/app/Notifications/Database/BackupFailed.php @@ -79,8 +79,8 @@ class BackupFailed extends CustomEmailNotification $title = 'Database backup failed'; $description = "Database backup for {$this->name} (db:{$this->database_name}) has FAILED."; - $description .= "\n\n**Frequency:** {$this->frequency}"; - $description .= "\n\n**Error Output:**\n{$this->output}"; + $description .= "\n\n*Frequency:* {$this->frequency}"; + $description .= "\n\n*Error Output:* {$this->output}"; return new SlackMessage( title: $title, diff --git a/app/Notifications/Database/BackupSuccess.php b/app/Notifications/Database/BackupSuccess.php index 585f7cce1..78bcfafe3 100644 --- a/app/Notifications/Database/BackupSuccess.php +++ b/app/Notifications/Database/BackupSuccess.php @@ -77,7 +77,7 @@ class BackupSuccess extends CustomEmailNotification $title = 'Database backup successful'; $description = "Database backup for {$this->name} (db:{$this->database_name}) was successful."; - $description .= "\n\n**Frequency:** {$this->frequency}"; + $description .= "\n\n*Frequency:* {$this->frequency}"; return new SlackMessage( title: $title, diff --git a/app/Notifications/ScheduledTask/TaskFailed.php b/app/Notifications/ScheduledTask/TaskFailed.php index c4d92f213..eb4fc7e79 100644 --- a/app/Notifications/ScheduledTask/TaskFailed.php +++ b/app/Notifications/ScheduledTask/TaskFailed.php @@ -101,11 +101,11 @@ class TaskFailed extends CustomEmailNotification $description = "Scheduled task ({$this->task->name}) failed."; if ($this->output) { - $description .= "\n\n**Error Output:**\n{$this->output}"; + $description .= "\n\n*Error Output:* {$this->output}"; } if ($this->url) { - $description .= "\n\n**Task URL:** {$this->url}"; + $description .= "\n\n*Task URL:* {$this->url}"; } return new SlackMessage( diff --git a/app/Notifications/ScheduledTask/TaskSuccess.php b/app/Notifications/ScheduledTask/TaskSuccess.php index 5d4154e7a..c45784db2 100644 --- a/app/Notifications/ScheduledTask/TaskSuccess.php +++ b/app/Notifications/ScheduledTask/TaskSuccess.php @@ -96,7 +96,7 @@ class TaskSuccess extends CustomEmailNotification $description = "Scheduled task ({$this->task->name}) succeeded."; if ($this->url) { - $description .= "\n\n**Task URL:** {$this->url}"; + $description .= "\n\n*Task URL:* {$this->url}"; } return new SlackMessage( diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php index 74d26e2f5..696f6a8c4 100644 --- a/bootstrap/helpers/docker.php +++ b/bootstrap/helpers/docker.php @@ -852,6 +852,23 @@ function validateComposeFile(string $compose, int $server_id): string|Throwable } } +function getContainerLogs(Server $server, string $container_id, int $lines = 100): string +{ + if ($server->isSwarm()) { + $output = instant_remote_process([ + "docker service logs -n {$lines} {$container_id}", + ], $server); + } else { + $output = instant_remote_process([ + "docker logs -n {$lines} {$container_id}", + ], $server); + } + + $output .= removeAnsiColors($output); + + return $output; +} + function escapeEnvVariables($value) { $search = ['\\', "\r", "\t", "\x0", '"', "'"]; diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 0f3b07cfe..3fba7edb7 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -440,11 +440,7 @@ function sslip(Server $server) function get_service_templates(bool $force = false): Collection { - if (isDev()) { - $services = File::get(base_path('templates/service-templates.json')); - return collect(json_decode($services))->sortKeys(); - } if ($force) { try { $response = Http::retry(3, 1000)->get(config('constants.services.official')); diff --git a/cliff.toml b/cliff.toml new file mode 100644 index 000000000..8656d82b2 --- /dev/null +++ b/cliff.toml @@ -0,0 +1,84 @@ +# git-cliff ~ default configuration file +# https://git-cliff.org/docs/configuration +# +# Lines starting with "#" are comments. +# Configuration options are organized into tables and keys. +# See documentation for more information on available options. + +[changelog] +# template for the changelog header +header = """ +# Changelog\n +All notable changes to this project will be documented in this file.\n +""" +# template for the changelog body +# https://keats.github.io/tera/docs/#introduction +body = """ +{% if version %}\ + ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} +{% else %}\ + ## [unreleased] +{% endif %}\ +{% for group, commits in commits | group_by(attribute="group") %} + ### {{ group | striptags | trim | upper_first }} + {% for commit in commits %} + - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\ + {% if commit.breaking %}[**breaking**] {% endif %}\ + {{ commit.message | upper_first }}\ + {% endfor %} +{% endfor %}\n +""" +# template for the changelog footer +footer = """ + +""" +# remove the leading and trailing s +trim = true +# postprocessors +postprocessors = [ + # { pattern = '', replace = "https://github.com/orhun/git-cliff" }, # replace repository URL +] +# render body even when there are no releases to process +# render_always = true +# output file path +# output = "test.md" + +[git] +# parse the commits based on https://www.conventionalcommits.org +conventional_commits = true +# filter out the commits that are not conventional +filter_unconventional = true +# process each line of a commit as an individual commit +split_commits = false +# regex for preprocessing the commit messages +commit_preprocessors = [ + # Replace issue numbers + #{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/issues/${2}))"}, + # Check spelling of the commit with https://github.com/crate-ci/typos + # If the spelling is incorrect, it will be automatically fixed. + #{ pattern = '.*', replace_command = 'typos --write-changes -' }, +] +# regex for parsing and grouping commits +commit_parsers = [ + { message = "^feat", group = "๐Ÿš€ Features" }, + { message = "^fix", group = "๐Ÿ› Bug Fixes" }, + { message = "^doc", group = "๐Ÿ“š Documentation" }, + { message = "^perf", group = "โšก Performance" }, + { message = "^refactor", group = "๐Ÿšœ Refactor" }, + { message = "^style", group = "๐ŸŽจ Styling" }, + { message = "^test", group = "๐Ÿงช Testing" }, + { message = "^chore\\(release\\): prepare for", skip = true }, + { message = "^chore\\(deps.*\\)", skip = true }, + { message = "^chore\\(pr\\)", skip = true }, + { message = "^chore\\(pull\\)", skip = true }, + { message = "^chore|^ci", group = "โš™๏ธ Miscellaneous Tasks" }, + { body = ".*security", group = "๐Ÿ›ก๏ธ Security" }, + { message = "^revert", group = "โ—€๏ธ Revert" }, + { message = ".*", group = "๐Ÿ’ผ Other" }, +] +# filter out the commits that are not matched by commit parsers +filter_commits = false +# sort the tags topologically +topo_order = false +# sort the commits inside sections by oldest/newest order +sort_commits = "oldest" diff --git a/config/constants.php b/config/constants.php index 981c0e331..2cf0123c0 100644 --- a/config/constants.php +++ b/config/constants.php @@ -2,7 +2,7 @@ return [ 'coolify' => [ - 'version' => '4.0.0-beta.391', + 'version' => '4.0.0-beta.392', 'helper_version' => '1.0.6', 'realtime_version' => '1.0.5', 'self_hosted' => env('SELF_HOSTED', true), diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index e651b4add..2e46438bd 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -14,7 +14,7 @@ services: - /data/coolify/webhooks-during-maintenance:/var/www/html/storage/app/webhooks-during-maintenance environment: - APP_ENV=${APP_ENV:-production} - - PHP_MEMORY_LIMIT=${PHP_MEMORY_LIMIT:-128M} + - PHP_MEMORY_LIMIT=${PHP_MEMORY_LIMIT:-256M} - PHP_FPM_PM_CONTROL=${PHP_FPM_PM_CONTROL:-dynamic} - PHP_FPM_PM_START_SERVERS=${PHP_FPM_PM_START_SERVERS:-1} - PHP_FPM_PM_MIN_SPARE_SERVERS=${PHP_FPM_PM_MIN_SPARE_SERVERS:-1} diff --git a/other/nightly/docker-compose.prod.yml b/other/nightly/docker-compose.prod.yml index e651b4add..2e46438bd 100644 --- a/other/nightly/docker-compose.prod.yml +++ b/other/nightly/docker-compose.prod.yml @@ -14,7 +14,7 @@ services: - /data/coolify/webhooks-during-maintenance:/var/www/html/storage/app/webhooks-during-maintenance environment: - APP_ENV=${APP_ENV:-production} - - PHP_MEMORY_LIMIT=${PHP_MEMORY_LIMIT:-128M} + - PHP_MEMORY_LIMIT=${PHP_MEMORY_LIMIT:-256M} - PHP_FPM_PM_CONTROL=${PHP_FPM_PM_CONTROL:-dynamic} - PHP_FPM_PM_START_SERVERS=${PHP_FPM_PM_START_SERVERS:-1} - PHP_FPM_PM_MIN_SPARE_SERVERS=${PHP_FPM_PM_MIN_SPARE_SERVERS:-1} diff --git a/public/svgs/bugsink.svg b/public/svgs/bugsink.svg new file mode 100644 index 000000000..9818b1081 --- /dev/null +++ b/public/svgs/bugsink.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/views/livewire/destination/index.blade.php b/resources/views/livewire/destination/index.blade.php index 2faef1037..0cb588e38 100644 --- a/resources/views/livewire/destination/index.blade.php +++ b/resources/views/livewire/destination/index.blade.php @@ -2,7 +2,7 @@ Destinations | Coolify -
+

Destinations

@if ($servers->count() > 0) diff --git a/resources/views/livewire/project/service/configuration.blade.php b/resources/views/livewire/project/service/configuration.blade.php index 68caa3e64..72f275895 100644 --- a/resources/views/livewire/project/service/configuration.blade.php +++ b/resources/views/livewire/project/service/configuration.blade.php @@ -39,7 +39,7 @@ @if ($currentRoute === 'project.service.configuration')

Services

-
+
@foreach ($applications as $application)
str( diff --git a/resources/views/livewire/project/service/navbar.blade.php b/resources/views/livewire/project/service/navbar.blade.php index 4629f1625..7424f0940 100644 --- a/resources/views/livewire/project/service/navbar.blade.php +++ b/resources/views/livewire/project/service/navbar.blade.php @@ -165,10 +165,12 @@ return; } $wire.$dispatch('info', 'Service restart in progress.'); + window.dispatchEvent(new CustomEvent('startservice')); $wire.$call('restart'); }); $wire.$on('pullAndRestartEvent', () => { - $wire.$dispatch('info', 'Pulling new images.'); + $wire.$dispatch('info', 'Pulling new images and restarting service.'); + window.dispatchEvent(new CustomEvent('startservice')); $wire.$call('pullAndRestartEvent'); }); $wire.on('imagePulled', () => { diff --git a/resources/views/livewire/server/index.blade.php b/resources/views/livewire/server/index.blade.php index 3ab776400..72f18cce0 100644 --- a/resources/views/livewire/server/index.blade.php +++ b/resources/views/livewire/server/index.blade.php @@ -2,7 +2,7 @@ Servers | Coolify -
+

Servers

diff --git a/resources/views/livewire/shared-variables/project/show.blade.php b/resources/views/livewire/shared-variables/project/show.blade.php index 8b7274419..2d3824dc9 100644 --- a/resources/views/livewire/shared-variables/project/show.blade.php +++ b/resources/views/livewire/shared-variables/project/show.blade.php @@ -2,7 +2,7 @@ Project Variable | Coolify -
+

Shared Variables for {{data_get($project,'name')}}

diff --git a/resources/views/livewire/shared-variables/team/index.blade.php b/resources/views/livewire/shared-variables/team/index.blade.php index 4ba1c7d99..708618f89 100644 --- a/resources/views/livewire/shared-variables/team/index.blade.php +++ b/resources/views/livewire/shared-variables/team/index.blade.php @@ -2,7 +2,7 @@ Team Variables | Coolify -
+

Team Shared Variables

diff --git a/resources/views/livewire/storage/index.blade.php b/resources/views/livewire/storage/index.blade.php index 3bc4144e2..7c28054a9 100644 --- a/resources/views/livewire/storage/index.blade.php +++ b/resources/views/livewire/storage/index.blade.php @@ -2,7 +2,7 @@ Storages | Coolify -
+

S3 Storages

diff --git a/resources/views/source/all.blade.php b/resources/views/source/all.blade.php index 35c3e1222..41b1e705c 100644 --- a/resources/views/source/all.blade.php +++ b/resources/views/source/all.blade.php @@ -2,7 +2,7 @@ Sources | Coolify -
+

Sources

diff --git a/routes/api.php b/routes/api.php index b884f4007..eaf155b3c 100644 --- a/routes/api.php +++ b/routes/api.php @@ -88,6 +88,7 @@ Route::group([ Route::patch('/applications/{uuid}/envs', [ApplicationsController::class, 'update_env_by_uuid'])->middleware(['api.ability:write']); Route::delete('/applications/{uuid}/envs/{env_uuid}', [ApplicationsController::class, 'delete_env_by_uuid'])->middleware(['api.ability:write']); // Route::post('/applications/{uuid}/execute', [ApplicationsController::class, 'execute_command_by_uuid'])->middleware(['ability:write']); + Route::get('/applications/{uuid}/logs', [ApplicationsController::class, 'logs_by_uuid'])->middleware(['api.ability:read']); Route::match(['get', 'post'], '/applications/{uuid}/start', [ApplicationsController::class, 'action_deploy'])->middleware(['api.ability:write']); Route::match(['get', 'post'], '/applications/{uuid}/restart', [ApplicationsController::class, 'action_restart'])->middleware(['api.ability:write']); diff --git a/templates/compose/bugsink.yaml b/templates/compose/bugsink.yaml new file mode 100644 index 000000000..bf015d727 --- /dev/null +++ b/templates/compose/bugsink.yaml @@ -0,0 +1,41 @@ +# documentation: https://www.bugsink.com/docs/ +# slogan: Self-hosted Error Tracking +# tags: python, error-tracking, django, mysql +# logo: svgs/bugsink.svg +# port: 8000 +services: + mysql: + image: 'mysql:latest' + restart: unless-stopped + environment: + - 'MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_ROOT}' + - 'MYSQL_DATABASE=${MYSQL_DATABASE:-bugsink}' + - 'MYSQL_USER=${SERVICE_USER_BUGSINK}' + - 'MYSQL_PASSWORD=${SERVICE_PASSWORD_BUGSINK}' + volumes: + - 'my-datavolume:/var/lib/mysql' + healthcheck: + test: + - CMD + - mysqladmin + - ping + - '-h' + - 127.0.0.1 + interval: 5s + timeout: 20s + retries: 10 + web: + image: bugsink/bugsink + restart: unless-stopped + environment: + - SECRET_KEY=$SERVICE_PASSWORD_64_BUGSINK + - 'CREATE_SUPERUSER=admin:${SERVICE_PASSWORD_BUGSINK}' + - SERVICE_FQDN_BUGSINK_8000 + - 'BASE_URL=${SERVICE_FQDN_BUGSINK_8000}' + - 'DATABASE_URL=mysql://${SERVICE_USER_BUGSINK}:$SERVICE_PASSWORD_BUGSINK@mysql:3306/${MYSQL_DATABASE:-bugsink}' + depends_on: + mysql: + condition: service_healthy + +volumes: + my-datavolume: \ No newline at end of file diff --git a/templates/compose/nitropage-with-postgresql.yaml b/templates/compose/nitropage-with-postgresql.yaml index 70aebb19b..4cea4d875 100644 --- a/templates/compose/nitropage-with-postgresql.yaml +++ b/templates/compose/nitropage-with-postgresql.yaml @@ -1,5 +1,5 @@ -# documentation: https://nitropage.com -# slogan: Nitropage is an extensible visual website builder, offering a growing collection of versatile building blocks, focal-point image cropping and sovereign font management. +# documentation: https://nitropage.org +# slogan: Nitropage is an extensible, visual website builder, offering a growing library of versatile building blocks, focal-point image cropping and sovereign font management. # tags: nitropage, builder, editor, wysiwyg, cms, content, management # logo: svgs/nitropage.svg # port: 3000 diff --git a/templates/compose/nitropage.yaml b/templates/compose/nitropage.yaml index f267a38cd..bcc58de5f 100644 --- a/templates/compose/nitropage.yaml +++ b/templates/compose/nitropage.yaml @@ -1,5 +1,5 @@ -# documentation: https://nitropage.com -# slogan: Nitropage is an extensible visual website builder, offering a growing collection of versatile building blocks, focal-point image cropping and sovereign font management. +# documentation: https://nitropage.org +# slogan: Nitropage is an extensible, visual website builder, offering a growing library of versatile building blocks, focal-point image cropping and sovereign font management. # tags: nitropage, builder, editor, wysiwyg, cms, content, management # logo: svgs/nitropage.svg # port: 3000 diff --git a/templates/service-templates.json b/templates/service-templates.json index 8c28a509b..3f11afb36 100644 --- a/templates/service-templates.json +++ b/templates/service-templates.json @@ -268,6 +268,20 @@ "minversion": "0.0.0", "port": "10000" }, + "bugsink": { + "documentation": "https://www.bugsink.com/docs/?utm_source=coolify.io", + "slogan": "Self-hosted Error Tracking", + "compose": "c2VydmljZXM6CiAgbXlzcWw6CiAgICBpbWFnZTogJ215c3FsOmxhdGVzdCcKICAgIHJlc3RhcnQ6IHVubGVzcy1zdG9wcGVkCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnTVlTUUxfUk9PVF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfUk9PVH0nCiAgICAgIC0gJ01ZU1FMX0RBVEFCQVNFPSR7TVlTUUxfREFUQUJBU0U6LWJ1Z3Npbmt9JwogICAgICAtICdNWVNRTF9VU0VSPSR7U0VSVklDRV9VU0VSX0JVR1NJTkt9JwogICAgICAtICdNWVNRTF9QQVNTV09SRD0ke1NFUlZJQ0VfUEFTU1dPUkRfQlVHU0lOS30nCiAgICB2b2x1bWVzOgogICAgICAtICdteS1kYXRhdm9sdW1lOi92YXIvbGliL215c3FsJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIG15c3FsYWRtaW4KICAgICAgICAtIHBpbmcKICAgICAgICAtICctaCcKICAgICAgICAtIDEyNy4wLjAuMQogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCiAgd2ViOgogICAgaW1hZ2U6IGJ1Z3NpbmsvYnVnc2luawogICAgcmVzdGFydDogdW5sZXNzLXN0b3BwZWQKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFQ1JFVF9LRVk9JFNFUlZJQ0VfUEFTU1dPUkRfNjRfQlVHU0lOSwogICAgICAtICdDUkVBVEVfU1VQRVJVU0VSPWFkbWluOiR7U0VSVklDRV9QQVNTV09SRF9CVUdTSU5LfScKICAgICAgLSBTRVJWSUNFX0ZRRE5fQlVHU0lOS184MDAwCiAgICAgIC0gJ0JBU0VfVVJMPSR7U0VSVklDRV9GUUROX0JVR1NJTktfODAwMH0nCiAgICAgIC0gJ0RBVEFCQVNFX1VSTD1teXNxbDovLyR7U0VSVklDRV9VU0VSX0JVR1NJTkt9OiRTRVJWSUNFX1BBU1NXT1JEX0JVR1NJTktAbXlzcWw6MzMwNi8ke01ZU1FMX0RBVEFCQVNFOi1idWdzaW5rfScKICAgIGRlcGVuZHNfb246CiAgICAgIG15c3FsOgogICAgICAgIGNvbmRpdGlvbjogc2VydmljZV9oZWFsdGh5CnZvbHVtZXM6CiAgbXktZGF0YXZvbHVtZTogbnVsbAo=", + "tags": [ + "python", + "error-tracking", + "django", + "mysql" + ], + "logo": "svgs/bugsink.svg", + "minversion": "0.0.0", + "port": "8000" + }, "calcom": { "documentation": "https://cal.com/docs?utm_source=coolify.io", "slogan": "Scheduling infrastructure for everyone.", @@ -2002,7 +2016,7 @@ "transformation", "service" ], - "logo": "svgs/coolify.png", + "logo": "svgs/coolify-transparent.png", "minversion": "0.0.0", "port": "3000" }, @@ -2101,8 +2115,8 @@ "port": "8081" }, "nitropage-with-postgresql": { - "documentation": "https://nitropage.com?utm_source=coolify.io", - "slogan": "Nitropage is an extensible visual website builder, offering a growing collection of versatile building blocks, focal-point image cropping and sovereign font management.", + "documentation": "https://nitropage.org?utm_source=coolify.io", + "slogan": "Nitropage is an extensible, visual website builder, offering a growing library of versatile building blocks, focal-point image cropping and sovereign font management.", "compose": "c2VydmljZXM6CiAgbml0cm9wYWdlOgogICAgaW1hZ2U6IGNvZGViZXJnLm9yZy9uaXRyb3BhZ2Uvbml0cm9wYWdlCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTklUUk9QQUdFXzMwMDAKICAgICAgLSAnTlBfQVVUSF9TQUxUPSR7U0VSVklDRV9CQVNFNjRfU0FMVH0nCiAgICAgIC0gJ05QX0FVVEhfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEXzY0X1NFU1NJT059JwogICAgICAtICdEQVRBQkFTRV9VUkw9cG9zdGdyZXNxbDovLyR7U0VSVklDRV9VU0VSX1BPU1RHUkVTUUx9OiR7U0VSVklDRV9QQVNTV09SRF9QT1NUR1JFU1FMfUBwb3N0Z3Jlc3FsOjU0MzIvJHtQT1NUR1JFU1FMX0RBVEFCQVNFOi1uaXRyb3BhZ2V9JwogICAgdm9sdW1lczoKICAgICAgLSAnbml0cm9wYWdlLWRhdGE6L2FwcC8uZGF0YScKICAgIGRlcGVuZHNfb246CiAgICAgIHBvc3RncmVzcWw6CiAgICAgICAgY29uZGl0aW9uOiBzZXJ2aWNlX2hlYWx0aHkKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovLzEyNy4wLjAuMTozMDAwL2FkbWluJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1CiAgcG9zdGdyZXNxbDoKICAgIGltYWdlOiAncG9zdGdyZXM6MTYtYWxwaW5lJwogICAgdm9sdW1lczoKICAgICAgLSAnbml0cm9wYWdlLXBvc3RncmVzcWwtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGEnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSAnUE9TVEdSRVNfVVNFUj0ke1NFUlZJQ0VfVVNFUl9QT1NUR1JFU1FMfScKICAgICAgLSAnUE9TVEdSRVNfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEX1BPU1RHUkVTUUx9JwogICAgICAtICdQT1NUR1JFU19EQj0ke1BPU1RHUkVTUUxfREFUQUJBU0U6LW5pdHJvcGFnZX0nCiAgICBoZWFsdGhjaGVjazoKICAgICAgdGVzdDoKICAgICAgICAtIENNRC1TSEVMTAogICAgICAgIC0gJ3BnX2lzcmVhZHkgLVUgJCR7UE9TVEdSRVNfVVNFUn0gLWQgJCR7UE9TVEdSRVNfREJ9JwogICAgICBpbnRlcnZhbDogNXMKICAgICAgdGltZW91dDogMjBzCiAgICAgIHJldHJpZXM6IDEwCg==", "tags": [ "nitropage", @@ -2118,8 +2132,8 @@ "port": "3000" }, "nitropage": { - "documentation": "https://nitropage.com?utm_source=coolify.io", - "slogan": "Nitropage is an extensible visual website builder, offering a growing collection of versatile building blocks, focal-point image cropping and sovereign font management.", + "documentation": "https://nitropage.org?utm_source=coolify.io", + "slogan": "Nitropage is an extensible, visual website builder, offering a growing library of versatile building blocks, focal-point image cropping and sovereign font management.", "compose": "c2VydmljZXM6CiAgbml0cm9wYWdlOgogICAgaW1hZ2U6ICdjb2RlYmVyZy5vcmcvbml0cm9wYWdlL25pdHJvcGFnZTpzcWxpdGUnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fTklUUk9QQUdFXzMwMDAKICAgICAgLSAnTlBfQVVUSF9TQUxUPSR7U0VSVklDRV9CQVNFNjRfU0FMVH0nCiAgICAgIC0gJ05QX0FVVEhfUEFTU1dPUkQ9JHtTRVJWSUNFX1BBU1NXT1JEXzY0X1NFU1NJT059JwogICAgICAtICdEQVRBQkFTRV9VUkw9ZmlsZTouLi8uLi8uZGF0YS9kZXYuZGInCiAgICB2b2x1bWVzOgogICAgICAtICduaXRyb3BhZ2UtZGF0YTovYXBwLy5kYXRhJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vMTI3LjAuMC4xOjMwMDAvYWRtaW4nCiAgICAgIGludGVydmFsOiAycwogICAgICB0aW1lb3V0OiAxMHMKICAgICAgcmV0cmllczogMTUK", "tags": [ "nitropage", diff --git a/versions.json b/versions.json index 8b37d727d..e43c4736c 100644 --- a/versions.json +++ b/versions.json @@ -1,10 +1,10 @@ { "coolify": { "v4": { - "version": "4.0.0-beta.391" + "version": "4.0.0-beta.392" }, "nightly": { - "version": "4.0.0-beta.392" + "version": "4.0.0-beta.393" }, "helper": { "version": "1.0.6"