@@ -13,16 +13,16 @@ jobs:
|
||||
id: stale
|
||||
with:
|
||||
stale-issue-message: 'This issue will be automatically closed in a few days if no response is received. Please provide an update with the requested information.'
|
||||
stale-pr-message: 'This pull request will be automatically closed in a few days if no response is received. Please update your PR or comment if you would like to continue working on it.'
|
||||
stale-pr-message: 'This pull request requires attention. If no changes or response is received within the next few days, it will be automatically closed. Please update your PR or leave a comment with the requested information.'
|
||||
close-issue-message: 'This issue has been automatically closed due to inactivity.'
|
||||
close-pr-message: 'This pull request has been automatically closed due to inactivity.'
|
||||
close-pr-message: 'Thank you for your contribution. Due to inactivity, this PR was automatically closed. If you would like to continue working on this change in the future, feel free to reopen this PR or submit a new one.'
|
||||
days-before-stale: 14
|
||||
days-before-close: 7
|
||||
stale-issue-label: '⏱︎ Stale'
|
||||
stale-pr-label: '⏱︎ Stale'
|
||||
only-labels: '💤 Waiting for feedback'
|
||||
only-labels: '💤 Waiting for feedback, 💤 Waiting for changes'
|
||||
remove-stale-when-updated: true
|
||||
operations-per-run: 100
|
||||
labels-to-remove-when-unstale: '⏱︎ Stale, 💤 Waiting for feedback'
|
||||
labels-to-remove-when-unstale: '⏱︎ Stale, 💤 Waiting for feedback, 💤 Waiting for changes'
|
||||
close-issue-reason: 'not_planned'
|
||||
exempt-all-milestones: false
|
||||
|
@@ -19,8 +19,12 @@ jobs:
|
||||
script: |
|
||||
const { owner, repo } = context.repo;
|
||||
|
||||
async function processIssue(issueNumber) {
|
||||
async function processIssue(issueNumber, isFromPR = false, prBaseBranch = null) {
|
||||
try {
|
||||
if (isFromPR && prBaseBranch !== 'main') {
|
||||
return;
|
||||
}
|
||||
|
||||
const { data: currentLabels } = await github.rest.issues.listLabelsOnIssue({
|
||||
owner,
|
||||
repo,
|
||||
@@ -59,19 +63,19 @@ jobs:
|
||||
}
|
||||
}
|
||||
|
||||
if (context.eventName === 'issues' || context.eventName === 'pull_request' || context.eventName === 'pull_request_target') {
|
||||
const issue = context.payload.issue || context.payload.pull_request;
|
||||
await processIssue(issue.number);
|
||||
if (context.eventName === 'issues') {
|
||||
await processIssue(context.payload.issue.number);
|
||||
}
|
||||
|
||||
if (context.eventName === 'pull_request' || context.eventName === 'pull_request_target') {
|
||||
const pr = context.payload.pull_request;
|
||||
if (pr.body) {
|
||||
await processIssue(pr.number);
|
||||
if (pr.merged && pr.base.ref === 'main' && pr.body) {
|
||||
const issueReferences = pr.body.match(/#(\d+)/g);
|
||||
if (issueReferences) {
|
||||
for (const reference of issueReferences) {
|
||||
const issueNumber = parseInt(reference.substring(1));
|
||||
await processIssue(issueNumber);
|
||||
await processIssue(issueNumber, true, pr.base.ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -4,6 +4,8 @@
|
||||
|
||||
You can ask for guidance anytime on our [Discord server](https://coollabs.io/discord) in the `#contribute` channel.
|
||||
|
||||
To understand the tech stack, please refer to the [Tech Stack](TECH_STACK.md) document.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Setup Development Environment](#1-setup-development-environment)
|
||||
|
29
TECH_STACK.md
Normal file
29
TECH_STACK.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Coolify Technology Stack
|
||||
|
||||
## Frontend
|
||||
|
||||
- Livewire and Alpine.js
|
||||
- Blade (PHP templating engine)
|
||||
- Tailwind CSS
|
||||
- Monaco Editor (Code editor component)
|
||||
- XTerm.js (Terminal component)
|
||||
|
||||
## Backend
|
||||
|
||||
- Laravel 11 (PHP Framework)
|
||||
- PostgreSQL 15 (Database)
|
||||
- Redis 7 (Caching & Real-time features)
|
||||
- Soketi (WebSocket Server)
|
||||
|
||||
## DevOps & Infrastructure
|
||||
|
||||
- Docker & Docker Compose
|
||||
- Nginx (Web Server)
|
||||
- S6 Overlay (Process Supervisor)
|
||||
- GitHub Actions (CI/CD)
|
||||
|
||||
## Languages
|
||||
|
||||
- PHP 8.4
|
||||
- JavaScript
|
||||
- Shell/Bash scripts
|
@@ -294,7 +294,7 @@ class General extends Component
|
||||
public function resetDefaultLabels($manualReset = false)
|
||||
{
|
||||
try {
|
||||
if ($this->application->settings->is_container_label_readonly_enabled && ! $manualReset) {
|
||||
if (! $this->application->settings->is_container_label_readonly_enabled && ! $manualReset) {
|
||||
return;
|
||||
}
|
||||
$this->customLabels = str(implode('|coolify|', generateLabelsApplication($this->application)))->replace('|coolify|', "\n");
|
||||
@@ -324,6 +324,7 @@ class General extends Component
|
||||
}
|
||||
check_domain_usage(resource: $this->application);
|
||||
$this->application->fqdn = $domains->implode(',');
|
||||
$this->resetDefaultLabels(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -146,11 +146,16 @@ class ExecuteContainerCommand extends Component
|
||||
private function checkShellAvailability(Server $server, string $container): bool
|
||||
{
|
||||
$escapedContainer = escapeshellarg($container);
|
||||
$result = instant_remote_process([
|
||||
"docker exec {$escapedContainer} which bash || docker exec {$escapedContainer} which sh",
|
||||
], $server, false);
|
||||
try {
|
||||
instant_remote_process([
|
||||
"docker exec {$escapedContainer} bash -c 'exit 0' 2>/dev/null || ".
|
||||
"docker exec {$escapedContainer} sh -c 'exit 0' 2>/dev/null",
|
||||
], $server);
|
||||
|
||||
return ! empty($result);
|
||||
return true;
|
||||
} catch (\Throwable) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#[On('connectToServer')]
|
||||
|
@@ -9,6 +9,8 @@ use Livewire\Component;
|
||||
|
||||
class Terminal extends Component
|
||||
{
|
||||
public bool $hasShell = true;
|
||||
|
||||
public function getListeners()
|
||||
{
|
||||
$teamId = auth()->user()->currentTeam()->id;
|
||||
@@ -23,6 +25,21 @@ class Terminal extends Component
|
||||
$this->dispatch('reloadWindow');
|
||||
}
|
||||
|
||||
private function checkShellAvailability(Server $server, string $container): bool
|
||||
{
|
||||
$escapedContainer = escapeshellarg($container);
|
||||
try {
|
||||
instant_remote_process([
|
||||
"docker exec {$escapedContainer} bash -c 'exit 0' 2>/dev/null || ".
|
||||
"docker exec {$escapedContainer} sh -c 'exit 0' 2>/dev/null",
|
||||
], $server);
|
||||
|
||||
return true;
|
||||
} catch (\Throwable) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#[On('send-terminal-command')]
|
||||
public function sendTerminalCommand($isContainer, $identifier, $serverUuid)
|
||||
{
|
||||
@@ -40,6 +57,12 @@ class Terminal extends Component
|
||||
return;
|
||||
}
|
||||
|
||||
// Check shell availability
|
||||
$this->hasShell = $this->checkShellAvailability($server, $identifier);
|
||||
if (! $this->hasShell) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Escape the identifier for shell usage
|
||||
$escapedIdentifier = escapeshellarg($identifier);
|
||||
$command = SshMultiplexingHelper::generateSshCommand($server, "docker exec -it {$escapedIdentifier} sh -c 'PATH=\$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && if [ -f ~/.profile ]; then . ~/.profile; fi && if [ -n \"\$SHELL\" ]; then exec \$SHELL; else sh; fi'");
|
||||
|
@@ -4,9 +4,7 @@ namespace App\Models;
|
||||
|
||||
use App\Models\EnvironmentVariable as ModelsEnvironmentVariable;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use OpenApi\Attributes as OA;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
|
||||
#[OA\Schema(
|
||||
description: 'Environment Variable model',
|
||||
@@ -30,7 +28,7 @@ use Visus\Cuid2\Cuid2;
|
||||
'updated_at' => ['type' => 'string'],
|
||||
]
|
||||
)]
|
||||
class EnvironmentVariable extends Model
|
||||
class EnvironmentVariable extends BaseModel
|
||||
{
|
||||
protected $guarded = [];
|
||||
|
||||
@@ -49,12 +47,6 @@ class EnvironmentVariable extends Model
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::creating(function (Model $model) {
|
||||
if (! $model->uuid) {
|
||||
$model->uuid = (string) new Cuid2;
|
||||
}
|
||||
});
|
||||
|
||||
static::created(function (EnvironmentVariable $environment_variable) {
|
||||
if ($environment_variable->resourceable_type === Application::class && ! $environment_variable->is_preview) {
|
||||
$found = ModelsEnvironmentVariable::where('key', $environment_variable->key)
|
||||
|
@@ -2,8 +2,8 @@
|
||||
|
||||
return [
|
||||
'coolify' => [
|
||||
'version' => '4.0.0-beta.388',
|
||||
'helper_version' => '1.0.5',
|
||||
'version' => '4.0.0-beta.389',
|
||||
'helper_version' => '1.0.6',
|
||||
'realtime_version' => '1.0.5',
|
||||
'self_hosted' => env('SELF_HOSTED', true),
|
||||
'autoupdate' => env('AUTOUPDATE'),
|
||||
|
@@ -12,7 +12,7 @@ ARG DOCKER_BUILDX_VERSION=0.19.3
|
||||
# https://github.com/buildpacks/pack/releases
|
||||
ARG PACK_VERSION=0.36.2
|
||||
# https://github.com/railwayapp/nixpacks/releases
|
||||
ARG NIXPACKS_VERSION=1.32.0
|
||||
ARG NIXPACKS_VERSION=1.29.0
|
||||
# https://github.com/minio/mc/releases
|
||||
ARG MINIO_VERSION=RELEASE.2024-11-21T17-21-54Z
|
||||
|
||||
|
21
public/svgs/flipt.svg
Normal file
21
public/svgs/flipt.svg
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="logosandtypes_com" data-name="logosandtypes com" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150 150">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #7c3aed;
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: none;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g id="Layer_3" data-name="Layer 3">
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<path id="Layer_3-2" data-name="Layer 3-2" class="cls-2" d="M0,0H150V150H0V0Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<path class="cls-1" d="M140.19,76.45c-3.24-14.68-11.77-43.35-15.1-56.52-6.68,.83-18.4,2.17-24.91,2.97,0,0-11.73,1.41-11.73,1.41-1.91,.27-3.79-.53-4.19-2.11-.33-1.03-1.18-5.12-1.95-5.88-.9-.94-2.35-.97-3.55-.69-15.06,1.87-49.7,6.21-49.7,6.21l-3.47-12.55-15.25,1.87L45.82,141.9l14.93-1.99L34.32,41.31s33.41-4.16,48.14-6.1c3.63-.62,5.03,.57,7.18,4.1,.84,1.41,1.68,1.76,3.47,1.52,.07,0,20.89-2.41,20.96-2.38,1.01,3.73,3.08,11.11,4.07,14.81l-18.09,2.35c-2.75,.35-4.55-.47-6.1-2.7-.47-.68-1.17-2.1-1.89-2.5-.7-.52-1.48-.57-2.66-.43-9.03,1.12-35.96,4.46-35.96,4.46l3.23,11.84s24.42-2.93,33.09-4.1c3.05-.61,5.65,.3,6.98,4.42,.31,.96,1.37,1.47,2.38,1.54l22.33-2.92,4.14,14.7s-18.11,2.66-18.11,2.66c-2.87,.35-4.09,.2-5.64-2.03-.17-.28-1.1-1.45-1.63-1.98-1.28-1.28-3.09-1.9-4.88-1.66l-34.58,4.5,4.55,16.65s25.11-3.45,35.45-4.45c3.31-.44,2.75,2.76,3.9,4.64,.61,.94,1.73,1.56,4.12,1.22l14.75-1.85c5.81-.71,11.8-1.61,15.16-6.4,1.51-2.16,2.52-6.64,2.34-9.02-.09-1.22-.56-4.54-.82-5.74Z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
@@ -22,7 +22,7 @@
|
||||
<a wire:navigate class="menu-item {{ $activeMenu === 'log-drains' ? 'menu-item-active' : '' }}"
|
||||
href="{{ route('server.log-drains', ['server_uuid' => $server->uuid]) }}">Log
|
||||
Drains</a>
|
||||
<a wire:navigate class="menu-item {{ $activeMenu === 'metrics' ? 'menu-item-active' : '' }}"
|
||||
<a class="menu-item {{ $activeMenu === 'metrics' ? 'menu-item-active' : '' }}"
|
||||
href="{{ route('server.charts', ['server_uuid' => $server->uuid]) }}">Metrics</a>
|
||||
@endif
|
||||
@if (!$server->isLocalhost())
|
||||
|
@@ -74,8 +74,7 @@
|
||||
href="{{ route('project.application.resource-operations', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
|
||||
wire:navigate>Resource Operations</a>
|
||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.application.metrics', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
|
||||
wire:navigate>Metrics</a>
|
||||
href="{{ route('project.application.metrics', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}" >Metrics</a>
|
||||
<a class="menu-item" wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.application.tags', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}"
|
||||
wire:navigate>Tags</a>
|
||||
|
@@ -60,9 +60,10 @@
|
||||
@if(data_get($deployment, 'status') !== 'queued')
|
||||
<div class="text-gray-600 dark:text-gray-400 text-sm">
|
||||
Started: {{ formatDateInServerTimezone(data_get($deployment, 'created_at'), data_get($application, 'destination.server')) }}
|
||||
@if($deployment->status !== 'in_progress' && $deployment->status !== 'cancelled-by-user' && $deployment->status !== 'failed')
|
||||
@if($deployment->status !== 'in_progress' && $deployment->status !== 'cancelled-by-user')
|
||||
<br>Ended: {{ formatDateInServerTimezone(data_get($deployment, 'finished_at'), data_get($application, 'destination.server')) }}
|
||||
<br>Duration: {{ calculateDuration(data_get($deployment, 'created_at'), data_get($deployment, 'finished_at')) }}
|
||||
<br>Finished {{ \Carbon\Carbon::parse(data_get($deployment, 'finished_at'))->diffForHumans() }}
|
||||
@elseif($deployment->status === 'in_progress')
|
||||
<br>Running for: {{ calculateDuration(data_get($deployment, 'created_at'), now()) }}
|
||||
@endif
|
||||
|
@@ -4,7 +4,7 @@
|
||||
<h3 class="py-4">Executions</h3>
|
||||
<x-forms.button wire:click='cleanupFailed'>Cleanup Failed Backups</x-forms.button>
|
||||
</div>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div wire:poll.5000ms="refreshBackupExecutions" class="flex flex-col gap-4">
|
||||
@forelse($executions as $execution)
|
||||
<div wire:key="{{ data_get($execution, 'id') }}" @class([
|
||||
'flex flex-col border-l-2 transition-colors p-4 bg-white dark:bg-coolgray-100 text-black dark:text-white',
|
||||
@@ -40,6 +40,7 @@
|
||||
@if(data_get($execution, 'status') !== 'running')
|
||||
<br>Ended: {{ formatDateInServerTimezone(data_get($execution, 'finished_at'), $this->server()) }}
|
||||
<br>Duration: {{ calculateDuration(data_get($execution, 'created_at'), data_get($execution, 'finished_at')) }}
|
||||
<br>Finished {{ \Carbon\Carbon::parse(data_get($execution, 'finished_at'))->diffForHumans() }}
|
||||
@endif
|
||||
</div>
|
||||
<div class="text-gray-600 dark:text-gray-400 text-sm">
|
||||
|
@@ -32,8 +32,7 @@
|
||||
href="{{ route('project.database.resource-operations', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}"
|
||||
wire:navigate>Resource Operations</a>
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.database.metrics', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}"
|
||||
wire:navigate>Metrics</a>
|
||||
href="{{ route('project.database.metrics', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}">Metrics</a>
|
||||
<a class='menu-item' wire:current.exact="menu-item-active"
|
||||
href="{{ route('project.database.tags', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'database_uuid' => $database->uuid]) }}"
|
||||
wire:navigate>Tags</a>
|
||||
|
@@ -8,7 +8,7 @@
|
||||
@elseif(!$resource->destination->server->isMetricsEnabled())
|
||||
<div class="alert alert-warning">Metrics are only available for servers with Sentinel & Metrics enabled!</div>
|
||||
<div> Go to <a class="underline dark:text-white"
|
||||
href="{{ route('server.show', $resource->destination->server->uuid) }}">Server settings</a> to
|
||||
wire:navigate href="{{ route('server.show', $resource->destination->server->uuid) }}">Server settings</a> to
|
||||
enable
|
||||
it.</div>
|
||||
@else
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<div class="flex flex-col gap-2" x-data="{
|
||||
<div class="flex flex-col gap-2" wire:poll.5000ms="refreshExecutions" x-data="{
|
||||
init() {
|
||||
let interval;
|
||||
$wire.$watch('isPollingActive', value => {
|
||||
@@ -48,6 +48,7 @@
|
||||
@if(data_get($execution, 'status') !== 'running')
|
||||
<br>Ended: {{ formatDateInServerTimezone(data_get($execution, 'finished_at'), data_get($task, 'application.destination.server') ?? data_get($task, 'service.destination.server')) }}
|
||||
<br>Duration: {{ calculateDuration(data_get($execution, 'created_at'), data_get($execution, 'finished_at')) }}
|
||||
<br>Finished {{ \Carbon\Carbon::parse(data_get($execution, 'finished_at'))->diffForHumans() }}
|
||||
@endif
|
||||
</div>
|
||||
</a>
|
||||
|
@@ -1,4 +1,19 @@
|
||||
<div id="terminal-container" x-data="terminalData()">
|
||||
@if(!$hasShell)
|
||||
<div class="flex pt-4 items-center justify-center w-full py-4 mx-auto">
|
||||
<div class="p-4 w-full rounded border dark:bg-coolgray-100 dark:border-coolgray-300">
|
||||
<div class="flex flex-col items-center justify-center space-y-4">
|
||||
<svg class="w-12 h-12 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
|
||||
</svg>
|
||||
<div class="text-center">
|
||||
<h3 class="text-lg font-medium">Terminal Not Available</h3>
|
||||
<p class="mt-2 text-sm text-gray-500">No shell (bash/sh) is available in this container. Please ensure either bash or sh is installed to use the terminal.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div x-ref="terminalWrapper"
|
||||
:class="fullscreen ? 'fullscreen' : 'relative w-full h-full py-4 mx-auto max-h-[510px]'">
|
||||
<div id="terminal" wire:ignore></div>
|
||||
@@ -18,6 +33,7 @@
|
||||
</g>
|
||||
</svg></button>
|
||||
</div>
|
||||
@endif
|
||||
@script
|
||||
<script>
|
||||
// expose terminal config to the terminal.js file
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<div class="flex flex-col gap-2" x-data="{
|
||||
<div class="flex flex-col gap-2" wire:poll.5000ms="refreshExecutions" x-data="{
|
||||
init() {
|
||||
let interval;
|
||||
$wire.$watch('isPollingActive', value => {
|
||||
@@ -48,6 +48,7 @@
|
||||
@if(data_get($execution, 'status') !== 'running')
|
||||
<br>Ended: {{ formatDateInServerTimezone(data_get($execution, 'finished_at'), $server) }}
|
||||
<br>Duration: {{ calculateDuration(data_get($execution, 'created_at'), data_get($execution, 'finished_at')) }}
|
||||
<br>Finished {{ \Carbon\Carbon::parse(data_get($execution, 'finished_at'))->diffForHumans() }}
|
||||
@endif
|
||||
</div>
|
||||
</a>
|
||||
|
@@ -37,6 +37,9 @@ services:
|
||||
- MAILER_USER=${MAILER_USER}
|
||||
- MAILER_PASSWORD=${MAILER_PASSWORD}
|
||||
- MAILER_SENDER=${MAILER_SENDER}
|
||||
- COPILOT_FAL_API_KEY=${COPILOT_FAL_API_KEY}
|
||||
- COPILOT_PERPLEXITY_API_KEY=${COPILOT_PERPLEXITY_API_KEY}
|
||||
- COPILOT_OPENAI_API_KEY=${COPILOT_OPENAI_API_KEY}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "bash -c ':> /dev/tcp/127.0.0.1/3010' || exit 1"]
|
||||
interval: 5s
|
||||
|
23
templates/compose/flipt.yaml
Normal file
23
templates/compose/flipt.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
# documentation: https://docs.flipt.io/cloud/overview
|
||||
# slogan: Flipt is a fully managed feature flag solution that enables you to keep your feature flags and remote config next to your code in Git.
|
||||
# tags: feature flags,devops, CI, CD
|
||||
# logo: svgs/flipt.svg
|
||||
# port: 8080
|
||||
|
||||
services:
|
||||
flipt:
|
||||
image: 'docker.flipt.io/flipt/flipt:latest'
|
||||
volumes:
|
||||
- 'flipt-data:/var/opt/flipt'
|
||||
environment:
|
||||
- SERVICE_FQDN_FLIPT_8080
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- wget
|
||||
- '--spider'
|
||||
- '--quiet'
|
||||
- 'http://127.0.0.1:8080'
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
@@ -9,24 +9,49 @@ services:
|
||||
image: invoiceninja/invoiceninja:5
|
||||
environment:
|
||||
- SERVICE_FQDN_INVOICENINJA
|
||||
- APP_NAME=${APP_NAME:-"Invoice Ninja"}
|
||||
- APP_ENV=${APP_ENV:-production}
|
||||
- APP_URL=${SERVICE_FQDN_INVOICENINJA}
|
||||
- APP_KEY=base64:${SERVICE_REALBASE64_INVOICENINJA}
|
||||
- APP_DEBUG=${APP_DEBUG:-false}
|
||||
- REQUIRE_HTTPS=${REQUIRE_HTTPS:-false}
|
||||
- PHANTOMJS_PDF_GENERATION=${PHANTOMJS_PDF_GENERATION:-false}
|
||||
- PDF_GENERATOR=${PDF_GENERATOR:-snappdf}
|
||||
- PDF_GENERATOR=${PDF_GENERATOR:-hosted_ninja}
|
||||
- TRUSTED_PROXIES=${TRUSTED_PROXIES:-*}
|
||||
- QUEUE_CONNECTION=${QUEUE_CONNECTION:-database}
|
||||
- IN_USER_EMAIL=${IN_USER_EMAIL:-admin@example.com}
|
||||
- IN_PASSWORD=${SERVICE_PASSWORD_INVOICENINJAUSER}
|
||||
- CACHE_DRIVER=redis
|
||||
- QUEUE_CONNECTION=${QUEUE_CONNECTION:-redis}
|
||||
- SESSION_DRIVER=redis
|
||||
- REDIS_HOST=${REDIS_HOST:-redis}
|
||||
- REDIS_PASSWORD=${SERVICE_PASSWORD_REDIS}
|
||||
- REDIS_PORT=${REDIS_PORT:-6379}
|
||||
- DB_HOST=${DB_HOST:-mariadb}
|
||||
- DB_PORT=${DB_PORT:-3306}
|
||||
- DB_DATABASE=${DB_DATABASE:-invoiceninja}
|
||||
- DB_USERNAME=$SERVICE_USER_MARIADB
|
||||
- DB_PASSWORD=$SERVICE_PASSWORD_MARIADB
|
||||
- DB_USERNAME=${SERVICE_USER_MARIADB}
|
||||
- DB_PASSWORD=${SERVICE_PASSWORD_MARIADB}
|
||||
- IN_USER_EMAIL=${IN_USER_EMAIL:-admin@example.com}
|
||||
- IN_PASSWORD=${SERVICE_PASSWORD_INVOICENINJAUSER}
|
||||
- MAIL_MAILER=${MAIL_MAILER:-log}
|
||||
- MAIL_HOST=${MAIL_HOST}
|
||||
- MAIL_PORT=${MAIL_PORT}
|
||||
- MAIL_USERNAME=${MAIL_USERNAME}
|
||||
- MAIL_PASSWORD=${MAIL_PASSWORD}
|
||||
- MAIL_ENCRYPTION=${MAIL_ENCRYPTION}
|
||||
- MAIL_FROM_ADDRESS=${MAIL_FROM_ADDRESS}
|
||||
- MAIL_FROM_NAME=${MAIL_FROM_NAME}
|
||||
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
|
||||
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
|
||||
- AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION}
|
||||
- AWS_BUCKET=${AWS_BUCKET}
|
||||
- AWS_URL=${AWS_URL}
|
||||
- AWS_ENDPOINT=${AWS_ENDPOINT}
|
||||
- NORDIGEN_SECRET_ID=${NORDIGEN_SECRET_ID}
|
||||
- NORDIGEN_SECRET_KEY=${NORDIGEN_SECRET_KEY}
|
||||
- IS_DOCKER=true
|
||||
- SCOUT_DRIVER=${SCOUT_DRIVER}
|
||||
- LICENSE_KEY=${LICENSE_KEY}
|
||||
healthcheck:
|
||||
test: ['CMD', 'echo', 'ok']
|
||||
test: ["CMD", "echo", "ok"]
|
||||
interval: 5s
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
@@ -133,12 +158,25 @@ services:
|
||||
volumes:
|
||||
- mariadb-data:/var/lib/mysql
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=$SERVICE_PASSWORD_MARIADBROOT
|
||||
- MYSQL_ROOT_PASSWORD=${SERVICE_PASSWORD_MARIADBROOT}
|
||||
- MYSQL_DATABASE=${DB_DATABASE:-invoiceninja}
|
||||
- MYSQL_USER=$SERVICE_USER_MARIADB
|
||||
- MYSQL_PASSWORD=$SERVICE_PASSWORD_MARIADB
|
||||
- MYSQL_USER=${SERVICE_USER_MARIADB}
|
||||
- MYSQL_PASSWORD=${SERVICE_PASSWORD_MARIADB}
|
||||
healthcheck:
|
||||
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
||||
interval: 5s
|
||||
timeout: 20s
|
||||
retries: 10
|
||||
|
||||
redis:
|
||||
image: "redis:7.4-alpine"
|
||||
command: redis-server --requirepass ${SERVICE_PASSWORD_REDIS}
|
||||
environment:
|
||||
- REDIS_PASSWORD=${SERVICE_PASSWORD_REDIS}
|
||||
volumes:
|
||||
- "invoice-ninja-redis-data:/data"
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "-a", "${SERVICE_PASSWORD_REDIS}", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
@@ -25,9 +25,10 @@ services:
|
||||
- APP_URI=${SERVICE_FQDN_PLUNK}
|
||||
- API_URI=${SERVICE_FQDN_PLUNK}/api
|
||||
- DISABLE_SIGNUPS=${DISABLE_SIGNUPS:-False}
|
||||
- NODE_OPTIONS=--no-network-family-autoselection
|
||||
entrypoint: [ "/app/entry.sh" ]
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:3000"]
|
||||
test: ["CMD-SHELL", "(wget -S --spider http://127.0.0.1:3000/api/health 2>&1 | grep -q \"HTTP/1.1 [1-3]\")"]
|
||||
interval: 2s
|
||||
timeout: 10s
|
||||
retries: 15
|
||||
|
File diff suppressed because one or more lines are too long
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"coolify": {
|
||||
"v4": {
|
||||
"version": "4.0.0-beta.388"
|
||||
},
|
||||
"nightly": {
|
||||
"version": "4.0.0-beta.389"
|
||||
},
|
||||
"nightly": {
|
||||
"version": "4.0.0-beta.390"
|
||||
},
|
||||
"helper": {
|
||||
"version": "1.0.5"
|
||||
"version": "1.0.6"
|
||||
},
|
||||
"realtime": {
|
||||
"version": "1.0.5"
|
||||
|
Reference in New Issue
Block a user