commit
2fb674ae85
@ -2,7 +2,9 @@
|
||||
|
||||
namespace App\Actions\Database;
|
||||
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\StandaloneRedis;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
@ -12,7 +14,7 @@ class StartDatabaseProxy
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb $database)
|
||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $database)
|
||||
{
|
||||
$internalPort = null;
|
||||
if ($database->getMorphClass() === 'App\Models\StandaloneRedis') {
|
||||
@ -21,6 +23,10 @@ class StartDatabaseProxy
|
||||
$internalPort = 5432;
|
||||
} else if ($database->getMorphClass() === 'App\Models\StandaloneMongodb') {
|
||||
$internalPort = 27017;
|
||||
} else if ($database->getMorphClass() === 'App\Models\StandaloneMysql') {
|
||||
$internalPort = 3306;
|
||||
} else if ($database->getMorphClass() === 'App\Models\StandaloneMariadb') {
|
||||
$internalPort = 3306;
|
||||
}
|
||||
$containerName = "{$database->uuid}-proxy";
|
||||
$configuration_dir = database_proxy_dir($database->uuid);
|
||||
|
158
app/Actions/Database/StartMariadb.php
Normal file
158
app/Actions/Database/StartMariadb.php
Normal file
@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Database;
|
||||
|
||||
use App\Models\StandaloneMariadb;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class StartMariadb
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public StandaloneMariadb $database;
|
||||
public array $commands = [];
|
||||
public string $configuration_dir;
|
||||
|
||||
public function handle(StandaloneMariadb $database)
|
||||
{
|
||||
$this->database = $database;
|
||||
|
||||
$container_name = $this->database->uuid;
|
||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||
|
||||
$this->commands = [
|
||||
"echo '####### Starting {$database->name}.'",
|
||||
"mkdir -p $this->configuration_dir",
|
||||
];
|
||||
|
||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||
$environment_variables = $this->generate_environment_variables();
|
||||
$this->add_custom_mysql();
|
||||
$docker_compose = [
|
||||
'version' => '3.8',
|
||||
'services' => [
|
||||
$container_name => [
|
||||
'image' => $this->database->image,
|
||||
'container_name' => $container_name,
|
||||
'environment' => $environment_variables,
|
||||
'restart' => RESTART_MODE,
|
||||
'networks' => [
|
||||
$this->database->destination->network,
|
||||
],
|
||||
'labels' => [
|
||||
'coolify.managed' => 'true',
|
||||
],
|
||||
'healthcheck' => [
|
||||
'test' => ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"],
|
||||
'interval' => '5s',
|
||||
'timeout' => '5s',
|
||||
'retries' => 10,
|
||||
'start_period' => '5s'
|
||||
],
|
||||
'mem_limit' => $this->database->limits_memory,
|
||||
'memswap_limit' => $this->database->limits_memory_swap,
|
||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||
'cpus' => $this->database->limits_cpus,
|
||||
'cpuset' => $this->database->limits_cpuset,
|
||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||
]
|
||||
],
|
||||
'networks' => [
|
||||
$this->database->destination->network => [
|
||||
'external' => true,
|
||||
'name' => $this->database->destination->network,
|
||||
'attachable' => true,
|
||||
]
|
||||
]
|
||||
];
|
||||
if (count($this->database->ports_mappings_array) > 0) {
|
||||
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||
}
|
||||
if (count($persistent_storages) > 0) {
|
||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||
}
|
||||
if (count($volume_names) > 0) {
|
||||
$docker_compose['volumes'] = $volume_names;
|
||||
}
|
||||
if (!is_null($this->database->mariadb_conf)) {
|
||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||
'type' => 'bind',
|
||||
'source' => $this->configuration_dir . '/custom-config.cnf',
|
||||
'target' => '/etc/mysql/conf.d/custom-config.cnf',
|
||||
'read_only' => true,
|
||||
];
|
||||
}
|
||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$docker_compose_base64 = base64_encode($docker_compose);
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||
$readme = generate_readme_file($this->database->name, now());
|
||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||
$this->commands[] = "echo '####### {$database->name} started.'";
|
||||
return remote_process($this->commands, $database->destination->server);
|
||||
}
|
||||
|
||||
private function generate_local_persistent_volumes()
|
||||
{
|
||||
$local_persistent_volumes = [];
|
||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||
}
|
||||
return $local_persistent_volumes;
|
||||
}
|
||||
|
||||
private function generate_local_persistent_volumes_only_volume_names()
|
||||
{
|
||||
$local_persistent_volumes_names = [];
|
||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||
if ($persistentStorage->host_path) {
|
||||
continue;
|
||||
}
|
||||
$name = $persistentStorage->name;
|
||||
$local_persistent_volumes_names[$name] = [
|
||||
'name' => $name,
|
||||
'external' => false,
|
||||
];
|
||||
}
|
||||
return $local_persistent_volumes_names;
|
||||
}
|
||||
|
||||
private function generate_environment_variables()
|
||||
{
|
||||
$environment_variables = collect();
|
||||
foreach ($this->database->runtime_environment_variables as $env) {
|
||||
$environment_variables->push("$env->key=$env->value");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_ROOT_PASSWORD'))->isEmpty()) {
|
||||
$environment_variables->push("MARIADB_ROOT_PASSWORD={$this->database->mariadb_root_password}");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_DATABASE'))->isEmpty()) {
|
||||
$environment_variables->push("MARIADB_DATABASE={$this->database->mariadb_database}");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_USER'))->isEmpty()) {
|
||||
$environment_variables->push("MARIADB_USER={$this->database->mariadb_user}");
|
||||
}
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MARIADB_PASSWORD'))->isEmpty()) {
|
||||
$environment_variables->push("MARIADB_PASSWORD={$this->database->mariadb_password}");
|
||||
}
|
||||
return $environment_variables->all();
|
||||
}
|
||||
private function add_custom_mysql()
|
||||
{
|
||||
if (is_null($this->database->mariadb_conf)) {
|
||||
return;
|
||||
}
|
||||
$filename = 'custom-config.cnf';
|
||||
$content = $this->database->mariadb_conf;
|
||||
$content_base64 = base64_encode($content);
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
||||
}
|
||||
}
|
@ -52,7 +52,7 @@ class StartMongodb
|
||||
'healthcheck' => [
|
||||
'test' => [
|
||||
'CMD-SHELL',
|
||||
'mongo --eval "printjson(db.serverStatus())" | grep uptime | grep -v grep'
|
||||
'mongosh --eval "printjson(db.runCommand(\"ping\"))"'
|
||||
],
|
||||
'interval' => '5s',
|
||||
'timeout' => '5s',
|
||||
@ -94,6 +94,14 @@ class StartMongodb
|
||||
];
|
||||
$docker_compose['services'][$container_name]['command'] = $startCommand . ' --config /etc/mongo/mongod.conf';
|
||||
}
|
||||
$this->add_default_database();
|
||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||
'type' => 'bind',
|
||||
'source' => $this->configuration_dir . '/docker-entrypoint-initdb.d',
|
||||
'target' => '/docker-entrypoint-initdb.d',
|
||||
'read_only' => true,
|
||||
];
|
||||
|
||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$docker_compose_base64 = base64_encode($docker_compose);
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||
@ -160,4 +168,11 @@ class StartMongodb
|
||||
$content_base64 = base64_encode($content);
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
||||
}
|
||||
private function add_default_database()
|
||||
{
|
||||
$content = "db = db.getSiblingDB(\"{$this->database->mongo_initdb_database}\");db.createCollection('init_collection');db.createUser({user: \"{$this->database->mongo_initdb_root_username}\", pwd: \"{$this->database->mongo_initdb_root_password}\",roles: [{role:\"readWrite\",db:\"{$this->database->mongo_initdb_database}\"}]});";
|
||||
$content_base64 = base64_encode($content);
|
||||
$this->commands[] = "mkdir -p $this->configuration_dir/docker-entrypoint-initdb.d";
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/docker-entrypoint-initdb.d/01-default-database.js";
|
||||
}
|
||||
}
|
||||
|
158
app/Actions/Database/StartMysql.php
Normal file
158
app/Actions/Database/StartMysql.php
Normal file
@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Database;
|
||||
|
||||
use App\Models\StandaloneMysql;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class StartMysql
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public StandaloneMysql $database;
|
||||
public array $commands = [];
|
||||
public string $configuration_dir;
|
||||
|
||||
public function handle(StandaloneMysql $database)
|
||||
{
|
||||
$this->database = $database;
|
||||
|
||||
$container_name = $this->database->uuid;
|
||||
$this->configuration_dir = database_configuration_dir() . '/' . $container_name;
|
||||
|
||||
$this->commands = [
|
||||
"echo '####### Starting {$database->name}.'",
|
||||
"mkdir -p $this->configuration_dir",
|
||||
];
|
||||
|
||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||
$environment_variables = $this->generate_environment_variables();
|
||||
$this->add_custom_mysql();
|
||||
$docker_compose = [
|
||||
'version' => '3.8',
|
||||
'services' => [
|
||||
$container_name => [
|
||||
'image' => $this->database->image,
|
||||
'container_name' => $container_name,
|
||||
'environment' => $environment_variables,
|
||||
'restart' => RESTART_MODE,
|
||||
'networks' => [
|
||||
$this->database->destination->network,
|
||||
],
|
||||
'labels' => [
|
||||
'coolify.managed' => 'true',
|
||||
],
|
||||
'healthcheck' => [
|
||||
'test' => ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p{$this->database->mysql_root_password}"],
|
||||
'interval' => '5s',
|
||||
'timeout' => '5s',
|
||||
'retries' => 10,
|
||||
'start_period' => '5s'
|
||||
],
|
||||
'mem_limit' => $this->database->limits_memory,
|
||||
'memswap_limit' => $this->database->limits_memory_swap,
|
||||
'mem_swappiness' => $this->database->limits_memory_swappiness,
|
||||
'mem_reservation' => $this->database->limits_memory_reservation,
|
||||
'cpus' => $this->database->limits_cpus,
|
||||
'cpuset' => $this->database->limits_cpuset,
|
||||
'cpu_shares' => $this->database->limits_cpu_shares,
|
||||
]
|
||||
],
|
||||
'networks' => [
|
||||
$this->database->destination->network => [
|
||||
'external' => true,
|
||||
'name' => $this->database->destination->network,
|
||||
'attachable' => true,
|
||||
]
|
||||
]
|
||||
];
|
||||
if (count($this->database->ports_mappings_array) > 0) {
|
||||
$docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
|
||||
}
|
||||
if (count($persistent_storages) > 0) {
|
||||
$docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
|
||||
}
|
||||
if (count($volume_names) > 0) {
|
||||
$docker_compose['volumes'] = $volume_names;
|
||||
}
|
||||
if (!is_null($this->database->mysql_conf)) {
|
||||
$docker_compose['services'][$container_name]['volumes'][] = [
|
||||
'type' => 'bind',
|
||||
'source' => $this->configuration_dir . '/custom-config.cnf',
|
||||
'target' => '/etc/mysql/conf.d/custom-config.cnf',
|
||||
'read_only' => true,
|
||||
];
|
||||
}
|
||||
$docker_compose = Yaml::dump($docker_compose, 10);
|
||||
$docker_compose_base64 = base64_encode($docker_compose);
|
||||
$this->commands[] = "echo '{$docker_compose_base64}' | base64 -d > $this->configuration_dir/docker-compose.yml";
|
||||
$readme = generate_readme_file($this->database->name, now());
|
||||
$this->commands[] = "echo '{$readme}' > $this->configuration_dir/README.md";
|
||||
$this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
|
||||
$this->commands[] = "echo '####### {$database->name} started.'";
|
||||
return remote_process($this->commands, $database->destination->server);
|
||||
}
|
||||
|
||||
private function generate_local_persistent_volumes()
|
||||
{
|
||||
$local_persistent_volumes = [];
|
||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||
$volume_name = $persistentStorage->host_path ?? $persistentStorage->name;
|
||||
$local_persistent_volumes[] = $volume_name . ':' . $persistentStorage->mount_path;
|
||||
}
|
||||
return $local_persistent_volumes;
|
||||
}
|
||||
|
||||
private function generate_local_persistent_volumes_only_volume_names()
|
||||
{
|
||||
$local_persistent_volumes_names = [];
|
||||
foreach ($this->database->persistentStorages as $persistentStorage) {
|
||||
if ($persistentStorage->host_path) {
|
||||
continue;
|
||||
}
|
||||
$name = $persistentStorage->name;
|
||||
$local_persistent_volumes_names[$name] = [
|
||||
'name' => $name,
|
||||
'external' => false,
|
||||
];
|
||||
}
|
||||
return $local_persistent_volumes_names;
|
||||
}
|
||||
|
||||
private function generate_environment_variables()
|
||||
{
|
||||
$environment_variables = collect();
|
||||
foreach ($this->database->runtime_environment_variables as $env) {
|
||||
$environment_variables->push("$env->key=$env->value");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_ROOT_PASSWORD'))->isEmpty()) {
|
||||
$environment_variables->push("MYSQL_ROOT_PASSWORD={$this->database->mysql_root_password}");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_DATABASE'))->isEmpty()) {
|
||||
$environment_variables->push("MYSQL_DATABASE={$this->database->mysql_database}");
|
||||
}
|
||||
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_USER'))->isEmpty()) {
|
||||
$environment_variables->push("MYSQL_USER={$this->database->mysql_user}");
|
||||
}
|
||||
if ($environment_variables->filter(fn ($env) => Str::of($env)->contains('MYSQL_PASSWORD'))->isEmpty()) {
|
||||
$environment_variables->push("MYSQL_PASSWORD={$this->database->mysql_password}");
|
||||
}
|
||||
return $environment_variables->all();
|
||||
}
|
||||
private function add_custom_mysql()
|
||||
{
|
||||
if (is_null($this->database->mysql_conf)) {
|
||||
return;
|
||||
}
|
||||
$filename = 'custom-config.cnf';
|
||||
$content = $this->database->mysql_conf;
|
||||
$content_base64 = base64_encode($content);
|
||||
$this->commands[] = "echo '{$content_base64}' | base64 -d > $this->configuration_dir/{$filename}";
|
||||
}
|
||||
}
|
@ -2,7 +2,9 @@
|
||||
|
||||
namespace App\Actions\Database;
|
||||
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\StandaloneRedis;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
@ -11,7 +13,7 @@ class StopDatabase
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb $database)
|
||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $database)
|
||||
{
|
||||
$server = $database->destination->server;
|
||||
instant_remote_process(
|
||||
|
@ -2,7 +2,9 @@
|
||||
|
||||
namespace App\Actions\Database;
|
||||
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\StandaloneRedis;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
@ -11,7 +13,7 @@ class StopDatabaseProxy
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb $database)
|
||||
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $database)
|
||||
{
|
||||
instant_remote_process(["docker rm -f {$database->uuid}-proxy"], $database->destination->server);
|
||||
$database->is_public = false;
|
||||
|
@ -3,8 +3,15 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Enums\ApplicationDeploymentStatus;
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationDeploymentQueue;
|
||||
use App\Models\Service;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\StandaloneRedis;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class Init extends Command
|
||||
{
|
||||
@ -13,9 +20,27 @@ class Init extends Command
|
||||
|
||||
public function handle()
|
||||
{
|
||||
ray()->clearAll();
|
||||
$this->cleanup_in_progress_application_deployments();
|
||||
$this->cleanup_stucked_resources();
|
||||
// $this->cleanup_ssh();
|
||||
}
|
||||
|
||||
private function cleanup_ssh()
|
||||
{
|
||||
try {
|
||||
$files = Storage::allFiles('ssh/keys');
|
||||
foreach ($files as $file) {
|
||||
Storage::delete($file);
|
||||
}
|
||||
$files = Storage::allFiles('ssh/mux');
|
||||
foreach ($files as $file) {
|
||||
Storage::delete($file);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error: {$e->getMessage()}\n";
|
||||
}
|
||||
}
|
||||
private function cleanup_in_progress_application_deployments()
|
||||
{
|
||||
// Cleanup any failed deployments
|
||||
@ -30,4 +55,93 @@ class Init extends Command
|
||||
echo "Error: {$e->getMessage()}\n";
|
||||
}
|
||||
}
|
||||
private function cleanup_stucked_resources()
|
||||
{
|
||||
// Cleanup any resources that are not attached to any environment or destination or server
|
||||
try {
|
||||
$applications = Application::all();
|
||||
foreach ($applications as $application) {
|
||||
if (!$application->environment) {
|
||||
ray('Application without environment', $application->name);
|
||||
$application->delete();
|
||||
}
|
||||
if (!$application->destination()) {
|
||||
ray('Application without destination', $application->name);
|
||||
$application->delete();
|
||||
}
|
||||
}
|
||||
$postgresqls = StandalonePostgresql::all();
|
||||
foreach ($postgresqls as $postgresql) {
|
||||
if (!$postgresql->environment) {
|
||||
ray('Postgresql without environment', $postgresql->name);
|
||||
$postgresql->delete();
|
||||
}
|
||||
if (!$postgresql->destination()) {
|
||||
ray('Postgresql without destination', $postgresql->name);
|
||||
$postgresql->delete();
|
||||
}
|
||||
}
|
||||
$redis = StandaloneRedis::all();
|
||||
foreach ($redis as $redis) {
|
||||
if (!$redis->environment) {
|
||||
ray('Redis without environment', $redis->name);
|
||||
$redis->delete();
|
||||
}
|
||||
if (!$redis->destination()) {
|
||||
ray('Redis without destination', $redis->name);
|
||||
$redis->delete();
|
||||
}
|
||||
}
|
||||
$mongodbs = StandaloneMongodb::all();
|
||||
foreach ($mongodbs as $mongodb) {
|
||||
if (!$mongodb->environment) {
|
||||
ray('Mongodb without environment', $mongodb->name);
|
||||
$mongodb->delete();
|
||||
}
|
||||
if (!$mongodb->destination()) {
|
||||
ray('Mongodb without destination', $mongodb->name);
|
||||
$mongodb->delete();
|
||||
}
|
||||
}
|
||||
$mysqls = StandaloneMysql::all();
|
||||
foreach ($mysqls as $mysql) {
|
||||
if (!$mysql->environment) {
|
||||
ray('Mysql without environment', $mysql->name);
|
||||
$mysql->delete();
|
||||
}
|
||||
if (!$mysql->destination()) {
|
||||
ray('Mysql without destination', $mysql->name);
|
||||
$mysql->delete();
|
||||
}
|
||||
}
|
||||
$mariadbs = StandaloneMysql::all();
|
||||
foreach ($mariadbs as $mariadb) {
|
||||
if (!$mariadb->environment) {
|
||||
ray('Mariadb without environment', $mariadb->name);
|
||||
$mariadb->delete();
|
||||
}
|
||||
if (!$mariadb->destination()) {
|
||||
ray('Mariadb without destination', $mariadb->name);
|
||||
$mariadb->delete();
|
||||
}
|
||||
}
|
||||
$services = Service::all();
|
||||
foreach ($services as $service) {
|
||||
if (!$service->environment) {
|
||||
ray('Service without environment', $service->name);
|
||||
$service->delete();
|
||||
}
|
||||
if (!$service->server) {
|
||||
ray('Service without server', $service->name);
|
||||
$service->delete();
|
||||
}
|
||||
if (!$service->destination()) {
|
||||
ray('Service without destination', $service->name);
|
||||
$service->delete();
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error: {$e->getMessage()}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ namespace App\Console\Commands;
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class GenerateServiceTemplates extends Command
|
||||
class ServicesGenerate extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
@ -80,6 +80,14 @@ class GenerateServiceTemplates extends Command
|
||||
$env_file = null;
|
||||
}
|
||||
|
||||
$tags = collect(preg_grep('/^# tags:/', explode("\n", $content)))->values();
|
||||
if ($tags->count() > 0) {
|
||||
$tags = str($tags[0])->after('# tags:')->trim()->explode(',')->map(function ($tag) {
|
||||
return str($tag)->trim()->lower()->value();
|
||||
})->values();
|
||||
} else {
|
||||
$tags = null;
|
||||
}
|
||||
$json = Yaml::parse($content);
|
||||
$yaml = base64_encode(Yaml::dump($json, 10, 2));
|
||||
$payload = [
|
||||
@ -87,9 +95,12 @@ class GenerateServiceTemplates extends Command
|
||||
'documentation' => $documentation,
|
||||
'slogan' => $slogan,
|
||||
'compose' => $yaml,
|
||||
'tags' => $tags,
|
||||
];
|
||||
if ($env_file) {
|
||||
$payload['envs'] = $env_file;
|
||||
$env_file_content = file_get_contents(base_path("templates/compose/$env_file"));
|
||||
$env_file_base64 = base64_encode($env_file_content);
|
||||
$payload['envs'] = $env_file_base64;
|
||||
}
|
||||
return $payload;
|
||||
}
|
@ -16,7 +16,7 @@ class SyncBunny extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'sync:bunny {--only-template} {--only-version}';
|
||||
protected $signature = 'sync:bunny {--templates} {--release}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
@ -31,8 +31,8 @@ class SyncBunny extends Command
|
||||
public function handle()
|
||||
{
|
||||
$that = $this;
|
||||
$only_template = $this->option('only-template');
|
||||
$only_version = $this->option('only-version');
|
||||
$only_template = $this->option('templates');
|
||||
$only_version = $this->option('release');
|
||||
$bunny_cdn = "https://cdn.coollabs.io";
|
||||
$bunny_cdn_path = "coolify";
|
||||
$bunny_cdn_storage_name = "coolcdn";
|
||||
|
@ -63,8 +63,12 @@ class ProjectController extends Controller
|
||||
$database = create_standalone_postgresql($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'redis') {
|
||||
$database = create_standalone_redis($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'mongodb') {
|
||||
} else if ($type->value() === 'mongodb') {
|
||||
$database = create_standalone_mongodb($environment->id, $destination_uuid);
|
||||
} else if ($type->value() === 'mysql') {
|
||||
$database = create_standalone_mysql($environment->id, $destination_uuid);
|
||||
}else if ($type->value() === 'mariadb') {
|
||||
$database = create_standalone_mariadb($environment->id, $destination_uuid);
|
||||
}
|
||||
return redirect()->route('project.database.configuration', [
|
||||
'project_uuid' => $project->uuid,
|
||||
@ -72,7 +76,7 @@ class ProjectController extends Controller
|
||||
'database_uuid' => $database->uuid,
|
||||
]);
|
||||
}
|
||||
if ($type->startsWith('one-click-service-') && !is_null( (int)$server_id)) {
|
||||
if ($type->startsWith('one-click-service-') && !is_null((int)$server_id)) {
|
||||
$oneClickServiceName = $type->after('one-click-service-')->value();
|
||||
$oneClickService = data_get($services, "$oneClickServiceName.compose");
|
||||
$oneClickDotEnvs = data_get($services, "$oneClickServiceName.envs", null);
|
||||
|
@ -55,18 +55,21 @@ class CloneProject extends Component
|
||||
'selectedServer' => 'required',
|
||||
'newProjectName' => 'required',
|
||||
]);
|
||||
$foundProject = Project::where('name', $this->newProjectName)->first();
|
||||
if ($foundProject) {
|
||||
throw new \Exception('Project with the same name already exists.');
|
||||
}
|
||||
$newProject = Project::create([
|
||||
'name' => $this->newProjectName,
|
||||
'team_id' => currentTeam()->id,
|
||||
'description' => $this->project->description . ' (clone)',
|
||||
]);
|
||||
if ($this->environment->id !== 1) {
|
||||
if ($this->environment->name !== 'production') {
|
||||
$newProject->environments()->create([
|
||||
'name' => $this->environment->name,
|
||||
]);
|
||||
$newProject->environments()->find(1)->delete();
|
||||
}
|
||||
$newEnvironment = $newProject->environments->first();
|
||||
$newEnvironment = $newProject->environments->where('name', $this->environment->name)->first();
|
||||
// Clone Applications
|
||||
$applications = $this->environment->applications;
|
||||
$databases = $this->environment->databases();
|
||||
@ -80,7 +83,6 @@ class CloneProject extends Component
|
||||
'environment_id' => $newEnvironment->id,
|
||||
'destination_id' => $this->selectedServer,
|
||||
]);
|
||||
$newApplication->environment_id = $newProject->environments->first()->id;
|
||||
$newApplication->save();
|
||||
$environmentVaribles = $application->environment_variables()->get();
|
||||
foreach ($environmentVaribles as $environmentVarible) {
|
||||
@ -105,7 +107,6 @@ class CloneProject extends Component
|
||||
'environment_id' => $newEnvironment->id,
|
||||
'destination_id' => $this->selectedServer,
|
||||
]);
|
||||
$newDatabase->environment_id = $newProject->environments->first()->id;
|
||||
$newDatabase->save();
|
||||
$environmentVaribles = $database->environment_variables()->get();
|
||||
foreach ($environmentVaribles as $environmentVarible) {
|
||||
@ -116,6 +117,10 @@ class CloneProject extends Component
|
||||
$payload['standalone_redis_id'] = $newDatabase->id;
|
||||
} else if ($database->type() === 'standalone_mongodb') {
|
||||
$payload['standalone_mongodb_id'] = $newDatabase->id;
|
||||
} else if ($database->type() === 'standalone_mysql') {
|
||||
$payload['standalone_mysql_id'] = $newDatabase->id;
|
||||
}else if ($database->type() === 'standalone_mariadb') {
|
||||
$payload['standalone_mariadb_id'] = $newDatabase->id;
|
||||
}
|
||||
$newEnvironmentVariable = $environmentVarible->replicate()->fill($payload);
|
||||
$newEnvironmentVariable->save();
|
||||
@ -128,7 +133,6 @@ class CloneProject extends Component
|
||||
'environment_id' => $newEnvironment->id,
|
||||
'destination_id' => $this->selectedServer,
|
||||
]);
|
||||
$newService->environment_id = $newProject->environments->first()->id;
|
||||
$newService->save();
|
||||
$newService->parse();
|
||||
}
|
||||
|
@ -1,23 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire\Project\Database;
|
||||
|
||||
use App\Models\ScheduledDatabaseBackupExecution;
|
||||
use Livewire\Component;
|
||||
|
||||
class BackupExecution extends Component
|
||||
{
|
||||
public ScheduledDatabaseBackupExecution $execution;
|
||||
|
||||
public function download()
|
||||
{
|
||||
}
|
||||
|
||||
public function delete(): void
|
||||
{
|
||||
delete_backup_locally($this->execution->filename, $this->execution->scheduledDatabaseBackup->database->destination->server);
|
||||
$this->execution->delete();
|
||||
$this->emit('success', 'Backup deleted successfully.');
|
||||
$this->emit('refreshBackupExecutions');
|
||||
}
|
||||
}
|
@ -2,14 +2,51 @@
|
||||
|
||||
namespace App\Http\Livewire\Project\Database;
|
||||
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Livewire\Component;
|
||||
|
||||
class BackupExecutions extends Component
|
||||
{
|
||||
public $backup;
|
||||
public $executions;
|
||||
protected $listeners = ['refreshBackupExecutions'];
|
||||
public $setDeletableBackup;
|
||||
protected $listeners = ['refreshBackupExecutions', 'deleteBackup'];
|
||||
|
||||
public function deleteBackup($exeuctionId)
|
||||
{
|
||||
$execution = $this->backup->executions()->where('id', $exeuctionId)->first();
|
||||
if (is_null($execution)) {
|
||||
$this->emit('error', 'Backup execution not found.');
|
||||
return;
|
||||
}
|
||||
delete_backup_locally($execution->filename, $execution->scheduledDatabaseBackup->database->destination->server);
|
||||
$execution->delete();
|
||||
$this->emit('success', 'Backup deleted successfully.');
|
||||
$this->emit('refreshBackupExecutions');
|
||||
}
|
||||
public function download($exeuctionId)
|
||||
{
|
||||
try {
|
||||
$execution = $this->backup->executions()->where('id', $exeuctionId)->first();
|
||||
if (is_null($execution)) {
|
||||
$this->emit('error', 'Backup execution not found.');
|
||||
return;
|
||||
}
|
||||
$filename = data_get($execution, 'filename');
|
||||
$server = $execution->scheduledDatabaseBackup->database->destination->server;
|
||||
$privateKeyLocation = savePrivateKeyToFs($server);
|
||||
$disk = Storage::build([
|
||||
'driver' => 'sftp',
|
||||
'host' => $server->ip,
|
||||
'port' => $server->port,
|
||||
'username' => $server->user,
|
||||
'privateKey' => $privateKeyLocation,
|
||||
]);
|
||||
return $disk->download($filename);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function refreshBackupExecutions(): void
|
||||
{
|
||||
$this->executions = $this->backup->executions;
|
||||
|
@ -48,6 +48,10 @@ class CreateScheduledBackup extends Component
|
||||
];
|
||||
if ($this->database->type() === 'standalone-postgresql') {
|
||||
$payload['databases_to_backup'] = $this->database->postgres_db;
|
||||
} else if ($this->database->type() === 'standalone-mysql') {
|
||||
$payload['databases_to_backup'] = $this->database->mysql_database;
|
||||
}else if ($this->database->type() === 'standalone-mariadb') {
|
||||
$payload['databases_to_backup'] = $this->database->mariadb_database;
|
||||
}
|
||||
ScheduledDatabaseBackup::create($payload);
|
||||
$this->emit('refreshScheduledBackups');
|
||||
|
@ -2,7 +2,9 @@
|
||||
|
||||
namespace App\Http\Livewire\Project\Database;
|
||||
|
||||
use App\Actions\Database\StartMariadb;
|
||||
use App\Actions\Database\StartMongodb;
|
||||
use App\Actions\Database\StartMysql;
|
||||
use App\Actions\Database\StartPostgresql;
|
||||
use App\Actions\Database\StartRedis;
|
||||
use App\Actions\Database\StopDatabase;
|
||||
@ -49,14 +51,18 @@ class Heading extends Component
|
||||
if ($this->database->type() === 'standalone-postgresql') {
|
||||
$activity = StartPostgresql::run($this->database);
|
||||
$this->emit('newMonitorActivity', $activity->id);
|
||||
}
|
||||
if ($this->database->type() === 'standalone-redis') {
|
||||
} else if ($this->database->type() === 'standalone-redis') {
|
||||
$activity = StartRedis::run($this->database);
|
||||
$this->emit('newMonitorActivity', $activity->id);
|
||||
}
|
||||
if ($this->database->type() === 'standalone-mongodb') {
|
||||
} else if ($this->database->type() === 'standalone-mongodb') {
|
||||
$activity = StartMongodb::run($this->database);
|
||||
$this->emit('newMonitorActivity', $activity->id);
|
||||
} else if ($this->database->type() === 'standalone-mysql') {
|
||||
$activity = StartMysql::run($this->database);
|
||||
$this->emit('newMonitorActivity', $activity->id);
|
||||
} else if ($this->database->type() === 'standalone-mariadb') {
|
||||
$activity = StartMariadb::run($this->database);
|
||||
$this->emit('newMonitorActivity', $activity->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
95
app/Http/Livewire/Project/Database/Mariadb/General.php
Normal file
95
app/Http/Livewire/Project/Database/Mariadb/General.php
Normal file
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire\Project\Database\Mariadb;
|
||||
|
||||
use App\Actions\Database\StartDatabaseProxy;
|
||||
use App\Actions\Database\StopDatabaseProxy;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use Exception;
|
||||
use Livewire\Component;
|
||||
|
||||
class General extends Component
|
||||
{
|
||||
protected $listeners = ['refresh'];
|
||||
|
||||
public StandaloneMariadb $database;
|
||||
public string $db_url;
|
||||
|
||||
protected $rules = [
|
||||
'database.name' => 'required',
|
||||
'database.description' => 'nullable',
|
||||
'database.mariadb_root_password' => 'required',
|
||||
'database.mariadb_user' => 'required',
|
||||
'database.mariadb_password' => 'required',
|
||||
'database.mariadb_database' => 'required',
|
||||
'database.mariadb_conf' => 'nullable',
|
||||
'database.image' => 'required',
|
||||
'database.ports_mappings' => 'nullable',
|
||||
'database.is_public' => 'nullable|boolean',
|
||||
'database.public_port' => 'nullable|integer',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'database.name' => 'Name',
|
||||
'database.description' => 'Description',
|
||||
'database.mariadb_root_password' => 'Root Password',
|
||||
'database.mariadb_user' => 'User',
|
||||
'database.mariadb_password' => 'Password',
|
||||
'database.mariadb_database' => 'Database',
|
||||
'database.mariadb_conf' => 'MariaDB Configuration',
|
||||
'database.image' => 'Image',
|
||||
'database.ports_mappings' => 'Port Mapping',
|
||||
'database.is_public' => 'Is Public',
|
||||
'database.public_port' => 'Public Port',
|
||||
];
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
$this->database->save();
|
||||
$this->emit('success', 'Database updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
if ($this->database->is_public && !$this->database->public_port) {
|
||||
$this->emit('error', 'Public port is required.');
|
||||
$this->database->is_public = false;
|
||||
return;
|
||||
}
|
||||
if ($this->database->is_public) {
|
||||
if (!str($this->database->status)->startsWith('running')) {
|
||||
$this->emit('error', 'Database must be started to be publicly accessible.');
|
||||
$this->database->is_public = false;
|
||||
return;
|
||||
}
|
||||
StartDatabaseProxy::run($this->database);
|
||||
$this->emit('success', 'Database is now publicly accessible.');
|
||||
} else {
|
||||
StopDatabaseProxy::run($this->database);
|
||||
$this->emit('success', 'Database is no longer publicly accessible.');
|
||||
}
|
||||
$this->db_url = $this->database->getDbUrl();
|
||||
$this->database->save();
|
||||
} catch (\Throwable $e) {
|
||||
$this->database->is_public = !$this->database->is_public;
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function refresh(): void
|
||||
{
|
||||
$this->database->refresh();
|
||||
}
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->db_url = $this->database->getDbUrl();
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.database.mariadb.general');
|
||||
}
|
||||
}
|
@ -39,7 +39,8 @@ class General extends Component
|
||||
'database.is_public' => 'Is Public',
|
||||
'database.public_port' => 'Public Port',
|
||||
];
|
||||
public function submit() {
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
if ($this->database->mongo_conf === "") {
|
||||
@ -60,7 +61,11 @@ class General extends Component
|
||||
return;
|
||||
}
|
||||
if ($this->database->is_public) {
|
||||
$this->emit('success', 'Starting TCP proxy...');
|
||||
if (!str($this->database->status)->startsWith('running')) {
|
||||
$this->emit('error', 'Database must be started to be publicly accessible.');
|
||||
$this->database->is_public = false;
|
||||
return;
|
||||
}
|
||||
StartDatabaseProxy::run($this->database);
|
||||
$this->emit('success', 'Database is now publicly accessible.');
|
||||
} else {
|
||||
@ -69,7 +74,7 @@ class General extends Component
|
||||
}
|
||||
$this->db_url = $this->database->getDbUrl();
|
||||
$this->database->save();
|
||||
} catch(\Throwable $e) {
|
||||
} catch (\Throwable $e) {
|
||||
$this->database->is_public = !$this->database->is_public;
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
95
app/Http/Livewire/Project/Database/Mysql/General.php
Normal file
95
app/Http/Livewire/Project/Database/Mysql/General.php
Normal file
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire\Project\Database\Mysql;
|
||||
|
||||
use App\Actions\Database\StartDatabaseProxy;
|
||||
use App\Actions\Database\StopDatabaseProxy;
|
||||
use App\Models\StandaloneMysql;
|
||||
use Exception;
|
||||
use Livewire\Component;
|
||||
|
||||
class General extends Component
|
||||
{
|
||||
protected $listeners = ['refresh'];
|
||||
|
||||
public StandaloneMysql $database;
|
||||
public string $db_url;
|
||||
|
||||
protected $rules = [
|
||||
'database.name' => 'required',
|
||||
'database.description' => 'nullable',
|
||||
'database.mysql_root_password' => 'required',
|
||||
'database.mysql_user' => 'required',
|
||||
'database.mysql_password' => 'required',
|
||||
'database.mysql_database' => 'required',
|
||||
'database.mysql_conf' => 'nullable',
|
||||
'database.image' => 'required',
|
||||
'database.ports_mappings' => 'nullable',
|
||||
'database.is_public' => 'nullable|boolean',
|
||||
'database.public_port' => 'nullable|integer',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'database.name' => 'Name',
|
||||
'database.description' => 'Description',
|
||||
'database.mysql_root_password' => 'Root Password',
|
||||
'database.mysql_user' => 'User',
|
||||
'database.mysql_password' => 'Password',
|
||||
'database.mysql_database' => 'Database',
|
||||
'database.mysql_conf' => 'MySQL Configuration',
|
||||
'database.image' => 'Image',
|
||||
'database.ports_mappings' => 'Port Mapping',
|
||||
'database.is_public' => 'Is Public',
|
||||
'database.public_port' => 'Public Port',
|
||||
];
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
$this->database->save();
|
||||
$this->emit('success', 'Database updated successfully.');
|
||||
} catch (Exception $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
if ($this->database->is_public && !$this->database->public_port) {
|
||||
$this->emit('error', 'Public port is required.');
|
||||
$this->database->is_public = false;
|
||||
return;
|
||||
}
|
||||
if ($this->database->is_public) {
|
||||
if (!str($this->database->status)->startsWith('running')) {
|
||||
$this->emit('error', 'Database must be started to be publicly accessible.');
|
||||
$this->database->is_public = false;
|
||||
return;
|
||||
}
|
||||
StartDatabaseProxy::run($this->database);
|
||||
$this->emit('success', 'Database is now publicly accessible.');
|
||||
} else {
|
||||
StopDatabaseProxy::run($this->database);
|
||||
$this->emit('success', 'Database is no longer publicly accessible.');
|
||||
}
|
||||
$this->db_url = $this->database->getDbUrl();
|
||||
$this->database->save();
|
||||
} catch (\Throwable $e) {
|
||||
$this->database->is_public = !$this->database->is_public;
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
public function refresh(): void
|
||||
{
|
||||
$this->database->refresh();
|
||||
}
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->db_url = $this->database->getDbUrl();
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.database.mysql.general');
|
||||
}
|
||||
}
|
@ -60,7 +60,11 @@ class General extends Component
|
||||
return;
|
||||
}
|
||||
if ($this->database->is_public) {
|
||||
$this->emit('success', 'Starting TCP proxy...');
|
||||
if (!str($this->database->status)->startsWith('running')) {
|
||||
$this->emit('error', 'Database must be started to be publicly accessible.');
|
||||
$this->database->is_public = false;
|
||||
return;
|
||||
}
|
||||
StartDatabaseProxy::run($this->database);
|
||||
$this->emit('success', 'Database is now publicly accessible.');
|
||||
} else {
|
||||
@ -69,11 +73,10 @@ class General extends Component
|
||||
}
|
||||
$this->db_url = $this->database->getDbUrl();
|
||||
$this->database->save();
|
||||
} catch(\Throwable $e) {
|
||||
} catch (\Throwable $e) {
|
||||
$this->database->is_public = !$this->database->is_public;
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
||||
}
|
||||
public function save_init_script($script)
|
||||
{
|
||||
|
@ -35,7 +35,8 @@ class General extends Component
|
||||
'database.is_public' => 'Is Public',
|
||||
'database.public_port' => 'Public Port',
|
||||
];
|
||||
public function submit() {
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->validate();
|
||||
if ($this->database->redis_conf === "") {
|
||||
@ -56,7 +57,11 @@ class General extends Component
|
||||
return;
|
||||
}
|
||||
if ($this->database->is_public) {
|
||||
$this->emit('success', 'Starting TCP proxy...');
|
||||
if (!str($this->database->status)->startsWith('running')) {
|
||||
$this->emit('error', 'Database must be started to be publicly accessible.');
|
||||
$this->database->is_public = false;
|
||||
return;
|
||||
}
|
||||
StartDatabaseProxy::run($this->database);
|
||||
$this->emit('success', 'Database is now publicly accessible.');
|
||||
} else {
|
||||
@ -65,7 +70,7 @@ class General extends Component
|
||||
}
|
||||
$this->db_url = $this->database->getDbUrl();
|
||||
$this->database->save();
|
||||
} catch(\Throwable $e) {
|
||||
} catch (\Throwable $e) {
|
||||
$this->database->is_public = !$this->database->is_public;
|
||||
return handleError($e, $this);
|
||||
}
|
||||
|
@ -21,10 +21,10 @@ class DeleteEnvironment extends Component
|
||||
'environment_id' => 'required|int',
|
||||
]);
|
||||
$environment = Environment::findOrFail($this->environment_id);
|
||||
if ($environment->applications->count() > 0) {
|
||||
return $this->emit('error', 'Environment has resources defined, please delete them first.');
|
||||
if ($environment->isEmpty()) {
|
||||
$environment->delete();
|
||||
return redirect()->route('project.show', ['project_uuid' => $this->parameters['project_uuid']]);
|
||||
}
|
||||
$environment->delete();
|
||||
return redirect()->route('project.show', ['project_uuid' => $this->parameters['project_uuid']]);
|
||||
return $this->emit('error', 'Environment has defined resources, please delete them first.');
|
||||
}
|
||||
}
|
||||
|
@ -21,14 +21,18 @@ class Select extends Component
|
||||
public Collection|array $swarmDockers = [];
|
||||
public array $parameters;
|
||||
public Collection|array $services = [];
|
||||
public Collection|array $allServices = [];
|
||||
|
||||
public bool $loadingServices = true;
|
||||
public bool $loading = false;
|
||||
public $environments = [];
|
||||
public ?string $selectedEnvironment = null;
|
||||
public ?string $existingPostgresqlUrl = null;
|
||||
|
||||
public ?string $search = null;
|
||||
protected $queryString = [
|
||||
'server',
|
||||
'search'
|
||||
];
|
||||
|
||||
public function mount()
|
||||
@ -41,6 +45,11 @@ class Select extends Component
|
||||
$this->environments = Project::whereUuid($projectUuid)->first()->environments;
|
||||
$this->selectedEnvironment = data_get($this->parameters, 'environment_name');
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
$this->loadServices();
|
||||
return view('livewire.project.new.select');
|
||||
}
|
||||
|
||||
public function updatedSelectedEnvironment()
|
||||
{
|
||||
@ -49,6 +58,7 @@ class Select extends Component
|
||||
'environment_name' => $this->selectedEnvironment,
|
||||
]);
|
||||
}
|
||||
|
||||
// public function addExistingPostgresql()
|
||||
// {
|
||||
// try {
|
||||
@ -59,19 +69,28 @@ class Select extends Component
|
||||
// }
|
||||
// }
|
||||
|
||||
public function loadThings()
|
||||
{
|
||||
$this->loadServices();
|
||||
$this->loadServers();
|
||||
}
|
||||
public function loadServices(bool $forceReload = false)
|
||||
public function loadServices(bool $force = false)
|
||||
{
|
||||
try {
|
||||
if ($forceReload) {
|
||||
Cache::forget('services');
|
||||
if (count($this->allServices) > 0 && !$force) {
|
||||
if (!$this->search) {
|
||||
$this->services = $this->allServices;
|
||||
return;
|
||||
}
|
||||
$this->services = $this->allServices->filter(function ($service, $key) {
|
||||
$tags = collect(data_get($service, 'tags', []));
|
||||
return str_contains(strtolower($key), strtolower($this->search)) || $tags->contains(function ($tag) {
|
||||
return str_contains(strtolower($tag), strtolower($this->search));
|
||||
});
|
||||
});
|
||||
} else {
|
||||
$this->search = null;
|
||||
$this->allServices = getServiceTemplates();
|
||||
$this->services = $this->allServices->filter(function ($service, $key) {
|
||||
return str_contains(strtolower($key), strtolower($this->search));
|
||||
});;
|
||||
$this->emit('success', 'Successfully loaded services.');
|
||||
}
|
||||
$this->services = getServiceTemplates();
|
||||
$this->emit('success', 'Successfully loaded services.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
} finally {
|
||||
|
@ -6,8 +6,8 @@ use Livewire\Component;
|
||||
|
||||
class ComposeModal extends Component
|
||||
{
|
||||
public string $raw;
|
||||
public string $actual;
|
||||
public ?string $raw = null;
|
||||
public ?string $actual = null;
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.service.compose-modal');
|
||||
|
@ -31,11 +31,17 @@ class All extends Component
|
||||
public function getDevView()
|
||||
{
|
||||
$this->variables = $this->resource->environment_variables->map(function ($item) {
|
||||
if ($item->is_shown_once) {
|
||||
return "$item->key=(locked secret)";
|
||||
}
|
||||
return "$item->key=$item->value";
|
||||
})->sort()->join('
|
||||
');
|
||||
if ($this->showPreview) {
|
||||
$this->variablesPreview = $this->resource->environment_variables_preview->map(function ($item) {
|
||||
if ($item->is_shown_once) {
|
||||
return "$item->key=(locked secret)";
|
||||
}
|
||||
return "$item->key=$item->value";
|
||||
})->sort()->join('
|
||||
');
|
||||
@ -49,19 +55,27 @@ class All extends Component
|
||||
{
|
||||
if ($isPreview) {
|
||||
$variables = parseEnvFormatToArray($this->variablesPreview);
|
||||
$existingVariables = $this->resource->environment_variables_preview();
|
||||
$this->resource->environment_variables_preview()->delete();
|
||||
} else {
|
||||
$variables = parseEnvFormatToArray($this->variables);
|
||||
$existingVariables = $this->resource->environment_variables();
|
||||
$this->resource->environment_variables()->delete();
|
||||
}
|
||||
foreach ($variables as $key => $variable) {
|
||||
$found = $existingVariables->where('key', $key)->first();
|
||||
$found = $this->resource->environment_variables()->where('key', $key)->first();
|
||||
$foundPreview = $this->resource->environment_variables_preview()->where('key', $key)->first();
|
||||
if ($found) {
|
||||
if ($found->is_shown_once) {
|
||||
continue;
|
||||
}
|
||||
$found->value = $variable;
|
||||
$found->save();
|
||||
continue;
|
||||
}
|
||||
if ($foundPreview) {
|
||||
if ($foundPreview->is_shown_once) {
|
||||
continue;
|
||||
}
|
||||
$foundPreview->value = $variable;
|
||||
$foundPreview->save();
|
||||
continue;
|
||||
} else {
|
||||
$environment = new EnvironmentVariable();
|
||||
$environment->key = $key;
|
||||
@ -81,6 +95,12 @@ class All extends Component
|
||||
case 'standalone-mongodb':
|
||||
$environment->standalone_mongodb_id = $this->resource->id;
|
||||
break;
|
||||
case 'standalone-mysql':
|
||||
$environment->standalone_mysql_id = $this->resource->id;
|
||||
break;
|
||||
case 'standalone-mariadb':
|
||||
$environment->standalone_mariadb_id = $this->resource->id;
|
||||
break;
|
||||
case 'service':
|
||||
$environment->service_id = $this->resource->id;
|
||||
break;
|
||||
|
@ -5,7 +5,6 @@ namespace App\Http\Livewire\Project\Shared\EnvironmentVariable;
|
||||
use App\Models\EnvironmentVariable as ModelsEnvironmentVariable;
|
||||
use Livewire\Component;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class Show extends Component
|
||||
{
|
||||
@ -13,29 +12,45 @@ class Show extends Component
|
||||
public ModelsEnvironmentVariable $env;
|
||||
public ?string $modalId = null;
|
||||
public bool $isDisabled = false;
|
||||
public bool $isLocked = false;
|
||||
public string $type;
|
||||
|
||||
protected $rules = [
|
||||
'env.key' => 'required|string',
|
||||
'env.value' => 'nullable',
|
||||
'env.is_build_time' => 'required|boolean',
|
||||
'env.is_shown_once' => 'required|boolean',
|
||||
];
|
||||
protected $validationAttributes = [
|
||||
'key' => 'key',
|
||||
'value' => 'value',
|
||||
'is_build_time' => 'build',
|
||||
'key' => 'Key',
|
||||
'value' => 'Value',
|
||||
'is_build_time' => 'Build Time',
|
||||
'is_shown_once' => 'Shown Once',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->isDisabled = false;
|
||||
if (Str::of($this->env->key)->startsWith('SERVICE_FQDN') || Str::of($this->env->key)->startsWith('SERVICE_URL')) {
|
||||
$this->isDisabled = true;
|
||||
}
|
||||
$this->modalId = new Cuid2(7);
|
||||
$this->parameters = get_route_parameters();
|
||||
$this->checkEnvs();
|
||||
}
|
||||
public function checkEnvs()
|
||||
{
|
||||
$this->isDisabled = false;
|
||||
if (str($this->env->key)->startsWith('SERVICE_FQDN') || str($this->env->key)->startsWith('SERVICE_URL')) {
|
||||
$this->isDisabled = true;
|
||||
}
|
||||
if ($this->env->is_shown_once) {
|
||||
$this->isLocked = true;
|
||||
}
|
||||
}
|
||||
public function lock()
|
||||
{
|
||||
$this->env->is_shown_once = true;
|
||||
$this->env->save();
|
||||
$this->checkEnvs();
|
||||
$this->emit('refreshEnvs');
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
$this->submit();
|
||||
|
@ -5,7 +5,9 @@ namespace App\Http\Livewire\Project\Shared;
|
||||
use App\Models\Application;
|
||||
use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\StandaloneRedis;
|
||||
use Livewire\Component;
|
||||
@ -13,7 +15,7 @@ use Livewire\Component;
|
||||
class Logs extends Component
|
||||
{
|
||||
public ?string $type = null;
|
||||
public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb $resource;
|
||||
public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $resource;
|
||||
public Server $server;
|
||||
public ?string $container = null;
|
||||
public $parameters;
|
||||
@ -41,11 +43,16 @@ class Logs extends Component
|
||||
if (is_null($resource)) {
|
||||
$resource = StandaloneMongodb::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
if (is_null($resource)) {
|
||||
abort(404);
|
||||
$resource = StandaloneMysql::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
if (is_null($resource)) {
|
||||
$resource = StandaloneMariadb::where('uuid', $this->parameters['database_uuid'])->first();
|
||||
if (is_null($resource)) {
|
||||
abort(404);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->resource = $resource;
|
||||
$this->status = $this->resource->status;
|
||||
$this->server = $this->resource->destination->server;
|
||||
|
19
app/Http/Livewire/Project/Shared/Webhooks.php
Normal file
19
app/Http/Livewire/Project/Shared/Webhooks.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire\Project\Shared;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class Webhooks extends Component
|
||||
{
|
||||
public $resource;
|
||||
public ?string $deploywebhook = null;
|
||||
public function mount()
|
||||
{
|
||||
$this->deploywebhook = generateDeployWebhook($this->resource);
|
||||
}
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.project.shared.webhooks');
|
||||
}
|
||||
}
|
@ -885,14 +885,14 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
|
||||
private function generate_build_env_variables()
|
||||
{
|
||||
$this->build_args = collect(["--build-arg SOURCE_COMMIT={$this->commit}"]);
|
||||
$this->build_args = collect(["--build-arg SOURCE_COMMIT=\"{$this->commit}\""]);
|
||||
if ($this->pull_request_id === 0) {
|
||||
foreach ($this->application->build_environment_variables as $env) {
|
||||
$this->build_args->push("--build-arg {$env->key}={$env->value}");
|
||||
$this->build_args->push("--build-arg {$env->key}=\"{$env->value}\"");
|
||||
}
|
||||
} else {
|
||||
foreach ($this->application->build_environment_variables_preview as $env) {
|
||||
$this->build_args->push("--build-arg {$env->key}={$env->value}");
|
||||
$this->build_args->push("--build-arg {$env->key}=\"{$env->value}\"");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,9 @@ use App\Models\S3Storage;
|
||||
use App\Models\ScheduledDatabaseBackup;
|
||||
use App\Models\ScheduledDatabaseBackupExecution;
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\Team;
|
||||
use App\Notifications\Database\BackupFailed;
|
||||
@ -28,7 +30,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
public ?Team $team = null;
|
||||
public Server $server;
|
||||
public ScheduledDatabaseBackup $backup;
|
||||
public StandalonePostgresql|StandaloneMongodb $database;
|
||||
public StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $database;
|
||||
|
||||
public ?string $container_name = null;
|
||||
public ?ScheduledDatabaseBackupExecution $backup_log = null;
|
||||
@ -75,6 +77,10 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$databasesToBackup = [$this->database->postgres_db];
|
||||
} else if ($databaseType === 'standalone-mongodb') {
|
||||
$databasesToBackup = ['*'];
|
||||
} else if ($databaseType === 'standalone-mysql') {
|
||||
$databasesToBackup = [$this->database->mysql_database];
|
||||
} else if ($databaseType === 'standalone-mariadb') {
|
||||
$databasesToBackup = [$this->database->mariadb_database];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@ -88,6 +94,14 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$databasesToBackup = explode('|', $databasesToBackup);
|
||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||
ray($databasesToBackup);
|
||||
} else if ($databaseType === 'standalone-mysql') {
|
||||
// Format: db1,db2,db3
|
||||
$databasesToBackup = explode(',', $databasesToBackup);
|
||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||
} else if ($databaseType === 'standalone-mariadb') {
|
||||
// Format: db1,db2,db3
|
||||
$databasesToBackup = explode(',', $databasesToBackup);
|
||||
$databasesToBackup = array_map('trim', $databasesToBackup);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@ -124,7 +138,6 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
} else {
|
||||
$databaseName = $database;
|
||||
}
|
||||
ray($databaseName);
|
||||
}
|
||||
$this->backup_file = "/mongo-dump-$databaseName-" . Carbon::now()->timestamp . ".tar.gz";
|
||||
$this->backup_location = $this->backup_dir . $this->backup_file;
|
||||
@ -134,6 +147,24 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
'scheduled_database_backup_id' => $this->backup->id,
|
||||
]);
|
||||
$this->backup_standalone_mongodb($database);
|
||||
} else if ($databaseType === 'standalone-mysql') {
|
||||
$this->backup_file = "/mysql-dump-$database-" . Carbon::now()->timestamp . ".dmp";
|
||||
$this->backup_location = $this->backup_dir . $this->backup_file;
|
||||
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
||||
'database_name' => $database,
|
||||
'filename' => $this->backup_location,
|
||||
'scheduled_database_backup_id' => $this->backup->id,
|
||||
]);
|
||||
$this->backup_standalone_mysql($database);
|
||||
} else if ($databaseType === 'standalone-mariadb') {
|
||||
$this->backup_file = "/mariadb-dump-$database-" . Carbon::now()->timestamp . ".dmp";
|
||||
$this->backup_location = $this->backup_dir . $this->backup_file;
|
||||
$this->backup_log = ScheduledDatabaseBackupExecution::create([
|
||||
'database_name' => $database,
|
||||
'filename' => $this->backup_location,
|
||||
'scheduled_database_backup_id' => $this->backup->id,
|
||||
]);
|
||||
$this->backup_standalone_mariadb($database);
|
||||
} else {
|
||||
throw new \Exception('Unsupported database type');
|
||||
}
|
||||
@ -170,18 +201,25 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
private function backup_standalone_mongodb(string $databaseWithCollections): void
|
||||
{
|
||||
try {
|
||||
$url = $this->database->getDbUrl();
|
||||
$url = $this->database->getDbUrl(useInternal: true);
|
||||
if ($databaseWithCollections === 'all') {
|
||||
$commands[] = "mkdir -p " . $this->backup_dir;
|
||||
$commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --gzip --archive > $this->backup_location";
|
||||
} else {
|
||||
$collectionsToExclude = str($databaseWithCollections)->after(':')->explode(',');
|
||||
$databaseName = str($databaseWithCollections)->before(':');
|
||||
if (str($databaseWithCollections)->contains(':')) {
|
||||
$databaseName = str($databaseWithCollections)->before(':');
|
||||
$collectionsToExclude = str($databaseWithCollections)->after(':')->explode(',');
|
||||
} else {
|
||||
$databaseName = $databaseWithCollections;
|
||||
$collectionsToExclude = collect();
|
||||
}
|
||||
$commands[] = "mkdir -p " . $this->backup_dir;
|
||||
$commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --excludeCollection " . $collectionsToExclude->implode(' --excludeCollection ') . " --archive > $this->backup_location";
|
||||
if ($collectionsToExclude->count() === 0) {
|
||||
$commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --archive > $this->backup_location";
|
||||
} else {
|
||||
$commands[] = "docker exec $this->container_name mongodump --authenticationDatabase=admin --uri=$url --db $databaseName --gzip --excludeCollection " . $collectionsToExclude->implode(' --excludeCollection ') . " --archive > $this->backup_location";
|
||||
}
|
||||
}
|
||||
|
||||
ray($commands);
|
||||
$this->backup_output = instant_remote_process($commands, $this->server);
|
||||
$this->backup_output = trim($this->backup_output);
|
||||
if ($this->backup_output === '') {
|
||||
@ -211,7 +249,42 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
private function backup_standalone_mysql(string $database): void
|
||||
{
|
||||
try {
|
||||
$commands[] = "mkdir -p " . $this->backup_dir;
|
||||
$commands[] = "docker exec $this->container_name mysqldump -u root -p{$this->database->mysql_root_password} $database > $this->backup_location";
|
||||
ray($commands);
|
||||
$this->backup_output = instant_remote_process($commands, $this->server);
|
||||
$this->backup_output = trim($this->backup_output);
|
||||
if ($this->backup_output === '') {
|
||||
$this->backup_output = null;
|
||||
}
|
||||
ray('Backup done for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location);
|
||||
} catch (\Throwable $e) {
|
||||
$this->add_to_backup_output($e->getMessage());
|
||||
ray('Backup failed for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location . '\n\nError:' . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
private function backup_standalone_mariadb(string $database): void
|
||||
{
|
||||
try {
|
||||
$commands[] = "mkdir -p " . $this->backup_dir;
|
||||
$commands[] = "docker exec $this->container_name mariadb-dump -u root -p{$this->database->mariadb_root_password} $database > $this->backup_location";
|
||||
ray($commands);
|
||||
$this->backup_output = instant_remote_process($commands, $this->server);
|
||||
$this->backup_output = trim($this->backup_output);
|
||||
if ($this->backup_output === '') {
|
||||
$this->backup_output = null;
|
||||
}
|
||||
ray('Backup done for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location);
|
||||
} catch (\Throwable $e) {
|
||||
$this->add_to_backup_output($e->getMessage());
|
||||
ray('Backup failed for ' . $this->container_name . ' at ' . $this->server->name . ':' . $this->backup_location . '\n\nError:' . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
private function add_to_backup_output($output): void
|
||||
{
|
||||
if ($this->backup_output) {
|
||||
|
@ -7,7 +7,9 @@ use App\Actions\Database\StopDatabase;
|
||||
use App\Actions\Service\StopService;
|
||||
use App\Models\Application;
|
||||
use App\Models\Service;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\StandaloneRedis;
|
||||
use Illuminate\Bus\Queueable;
|
||||
@ -21,7 +23,7 @@ class StopResourceJob implements ShouldQueue, ShouldBeEncrypted
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public function __construct(public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb $resource)
|
||||
public function __construct(public Application|Service|StandalonePostgresql|StandaloneRedis|StandaloneMongodb|StandaloneMysql|StandaloneMariadb $resource)
|
||||
{
|
||||
}
|
||||
|
||||
@ -45,6 +47,12 @@ class StopResourceJob implements ShouldQueue, ShouldBeEncrypted
|
||||
case 'standalone-mongodb':
|
||||
StopDatabase::run($this->resource);
|
||||
break;
|
||||
case 'standalone-mysql':
|
||||
StopDatabase::run($this->resource);
|
||||
break;
|
||||
case 'standalone-mariadb':
|
||||
StopDatabase::run($this->resource);
|
||||
break;
|
||||
case 'service':
|
||||
StopService::run($this->resource);
|
||||
break;
|
||||
|
@ -7,12 +7,8 @@ use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Environment extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'project_id',
|
||||
];
|
||||
|
||||
public function can_delete_environment()
|
||||
protected $guarded = [];
|
||||
public function isEmpty()
|
||||
{
|
||||
return $this->applications()->count() == 0 &&
|
||||
$this->redis()->count() == 0 &&
|
||||
@ -38,13 +34,23 @@ class Environment extends Model
|
||||
{
|
||||
return $this->hasMany(StandaloneMongodb::class);
|
||||
}
|
||||
public function mysqls()
|
||||
{
|
||||
return $this->hasMany(StandaloneMysql::class);
|
||||
}
|
||||
public function mariadbs()
|
||||
{
|
||||
return $this->hasMany(StandaloneMariadb::class);
|
||||
}
|
||||
|
||||
public function databases()
|
||||
{
|
||||
$postgresqls = $this->postgresqls;
|
||||
$redis = $this->redis;
|
||||
$mongodbs = $this->mongodbs;
|
||||
return $postgresqls->concat($redis)->concat($mongodbs);
|
||||
$mysqls = $this->mysqls;
|
||||
$mariadbs = $this->mariadbs;
|
||||
return $postgresqls->concat($redis)->concat($mongodbs)->concat($mysqls)->concat($mariadbs);
|
||||
}
|
||||
|
||||
public function project()
|
||||
|
@ -11,7 +11,7 @@ class EnvironmentVariable extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
protected $casts = [
|
||||
"key" => 'string',
|
||||
'key' => 'string',
|
||||
'value' => 'encrypted',
|
||||
'is_build_time' => 'boolean',
|
||||
];
|
||||
@ -21,6 +21,10 @@ class EnvironmentVariable extends Model
|
||||
static::created(function ($environment_variable) {
|
||||
if ($environment_variable->application_id && !$environment_variable->is_preview) {
|
||||
$found = ModelsEnvironmentVariable::where('key', $environment_variable->key)->where('application_id', $environment_variable->application_id)->where('is_preview', true)->first();
|
||||
$application = Application::find($environment_variable->application_id);
|
||||
if ($application->build_pack === 'dockerfile') {
|
||||
return;
|
||||
}
|
||||
if (!$found) {
|
||||
ModelsEnvironmentVariable::create([
|
||||
'key' => $environment_variable->key,
|
||||
@ -33,7 +37,8 @@ class EnvironmentVariable extends Model
|
||||
}
|
||||
});
|
||||
}
|
||||
public function service() {
|
||||
public function service()
|
||||
{
|
||||
return $this->belongsTo(Service::class);
|
||||
}
|
||||
protected function value(): Attribute
|
||||
@ -55,9 +60,9 @@ class EnvironmentVariable extends Model
|
||||
$variable = Str::after($environment_variable, 'global.');
|
||||
$variable = Str::before($variable, '}}');
|
||||
$variable = Str::of($variable)->trim()->value;
|
||||
// $environment_variable = GlobalEnvironmentVariable::where('name', $environment_variable)->where('team_id', $team_id)->first()?->value;
|
||||
ray('global env variable');
|
||||
return $environment_variable;
|
||||
// $environment_variable = GlobalEnvironmentVariable::where('name', $environment_variable)->where('team_id', $team_id)->first()?->value;
|
||||
ray('global env variable');
|
||||
return $environment_variable;
|
||||
}
|
||||
return $environment_variable;
|
||||
}
|
||||
@ -77,5 +82,4 @@ class EnvironmentVariable extends Model
|
||||
set: fn (string $value) => Str::of($value)->trim(),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ class Project extends BaseModel
|
||||
'project_id' => $project->id,
|
||||
]);
|
||||
Environment::create([
|
||||
'name' => 'Production',
|
||||
'name' => 'production',
|
||||
'project_id' => $project->id,
|
||||
]);
|
||||
});
|
||||
@ -56,4 +56,16 @@ class Project extends BaseModel
|
||||
{
|
||||
return $this->hasManyThrough(StandaloneRedis::class, Environment::class);
|
||||
}
|
||||
public function mongodbs()
|
||||
{
|
||||
return $this->hasManyThrough(StandaloneMongodb::class, Environment::class);
|
||||
}
|
||||
public function mysqls()
|
||||
{
|
||||
return $this->hasMany(StandaloneMysql::class, Environment::class);
|
||||
}
|
||||
public function mariadbs()
|
||||
{
|
||||
return $this->hasMany(StandaloneMariadb::class, Environment::class);
|
||||
}
|
||||
}
|
||||
|
@ -122,10 +122,12 @@ class Server extends BaseModel
|
||||
public function databases()
|
||||
{
|
||||
return $this->destinations()->map(function ($standaloneDocker) {
|
||||
$postgresqls = data_get($standaloneDocker,'postgresqls',collect([]));
|
||||
$redis = data_get($standaloneDocker,'redis',collect([]));
|
||||
$mongodbs = data_get($standaloneDocker,'mongodbs',collect([]));
|
||||
return $postgresqls->concat($redis)->concat($mongodbs);
|
||||
$postgresqls = data_get($standaloneDocker, 'postgresqls', collect([]));
|
||||
$redis = data_get($standaloneDocker, 'redis', collect([]));
|
||||
$mongodbs = data_get($standaloneDocker, 'mongodbs', collect([]));
|
||||
$mysqls = data_get($standaloneDocker, 'mysqls', collect([]));
|
||||
$mariadbs = data_get($standaloneDocker, 'mariadbs', collect([]));
|
||||
return $postgresqls->concat($redis)->concat($mongodbs)->concat($mysqls)->concat($mariadbs);
|
||||
})->flatten();
|
||||
}
|
||||
public function applications()
|
||||
@ -258,7 +260,8 @@ class Server extends BaseModel
|
||||
$this->settings->save();
|
||||
return true;
|
||||
}
|
||||
public function validateCoolifyNetwork() {
|
||||
public function validateCoolifyNetwork()
|
||||
{
|
||||
return instant_remote_process(["docker network create coolify --attachable >/dev/null 2>&1 || true"], $this, false);
|
||||
}
|
||||
}
|
||||
|
@ -6,10 +6,7 @@ use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ServerSetting extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'server_id',
|
||||
'is_usable',
|
||||
];
|
||||
protected $guarded = [];
|
||||
|
||||
public function server()
|
||||
{
|
||||
|
@ -24,6 +24,14 @@ class StandaloneDocker extends BaseModel
|
||||
{
|
||||
return $this->morphMany(StandaloneMongodb::class, 'destination');
|
||||
}
|
||||
public function mysqls()
|
||||
{
|
||||
return $this->morphMany(StandaloneMysql::class, 'destination');
|
||||
}
|
||||
public function mariadbs()
|
||||
{
|
||||
return $this->morphMany(StandaloneMariadb::class, 'destination');
|
||||
}
|
||||
|
||||
public function server()
|
||||
{
|
||||
@ -35,6 +43,16 @@ class StandaloneDocker extends BaseModel
|
||||
return $this->morphMany(Service::class, 'destination');
|
||||
}
|
||||
|
||||
public function databases()
|
||||
{
|
||||
$postgresqls = $this->postgresqls;
|
||||
$redis = $this->redis;
|
||||
$mongodbs = $this->mongodbs;
|
||||
$mysqls = $this->mysqls;
|
||||
$mariadbs = $this->mariadbs;
|
||||
return $postgresqls->concat($redis)->concat($mongodbs)->concat($mysqls)->concat($mariadbs);
|
||||
}
|
||||
|
||||
public function attachedTo()
|
||||
{
|
||||
return $this->applications?->count() > 0 || $this->databases?->count() > 0;
|
||||
|
106
app/Models/StandaloneMariadb.php
Normal file
106
app/Models/StandaloneMariadb.php
Normal file
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class StandaloneMariadb extends BaseModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $casts = [
|
||||
'mariadb_password' => 'encrypted',
|
||||
];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function ($database) {
|
||||
LocalPersistentVolume::create([
|
||||
'name' => 'mariadb-data-' . $database->uuid,
|
||||
'mount_path' => '/var/lib/mysql',
|
||||
'host_path' => null,
|
||||
'resource_id' => $database->id,
|
||||
'resource_type' => $database->getMorphClass(),
|
||||
'is_readonly' => true
|
||||
]);
|
||||
});
|
||||
static::deleting(function ($database) {
|
||||
$storages = $database->persistentStorages()->get();
|
||||
foreach ($storages as $storage) {
|
||||
instant_remote_process(["docker volume rm -f $storage->name"], $database->destination->server, false);
|
||||
}
|
||||
$database->scheduledBackups()->delete();
|
||||
$database->persistentStorages()->delete();
|
||||
$database->environment_variables()->delete();
|
||||
});
|
||||
}
|
||||
public function type(): string
|
||||
{
|
||||
return 'standalone-mariadb';
|
||||
}
|
||||
|
||||
public function portsMappings(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
set: fn ($value) => $value === "" ? null : $value,
|
||||
);
|
||||
}
|
||||
|
||||
public function portsMappingsArray(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => is_null($this->ports_mappings)
|
||||
? []
|
||||
: explode(',', $this->ports_mappings),
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
public function getDbUrl(bool $useInternal = false): string
|
||||
{
|
||||
if ($this->is_public && !$useInternal) {
|
||||
return "mysql://{$this->mariadb_user}:{$this->mariadb_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mariadb_database}";
|
||||
} else {
|
||||
return "mysql://{$this->mariadb_user}:{$this->mariadb_password}@{$this->uuid}:3306/{$this->mariadb_database}";
|
||||
}
|
||||
}
|
||||
|
||||
public function environment()
|
||||
{
|
||||
return $this->belongsTo(Environment::class);
|
||||
}
|
||||
|
||||
public function fileStorages()
|
||||
{
|
||||
return $this->morphMany(LocalFileVolume::class, 'resource');
|
||||
}
|
||||
|
||||
public function destination()
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
|
||||
public function environment_variables(): HasMany
|
||||
{
|
||||
return $this->hasMany(EnvironmentVariable::class);
|
||||
}
|
||||
|
||||
public function runtime_environment_variables(): HasMany
|
||||
{
|
||||
return $this->hasMany(EnvironmentVariable::class);
|
||||
}
|
||||
|
||||
public function persistentStorages()
|
||||
{
|
||||
return $this->morphMany(LocalPersistentVolume::class, 'resource');
|
||||
}
|
||||
|
||||
public function scheduledBackups()
|
||||
{
|
||||
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||
}
|
||||
}
|
@ -15,8 +15,16 @@ class StandaloneMongodb extends BaseModel
|
||||
{
|
||||
static::created(function ($database) {
|
||||
LocalPersistentVolume::create([
|
||||
'name' => 'mongodb-data-' . $database->uuid,
|
||||
'mount_path' => '/data',
|
||||
'name' => 'mongodb-configdb-' . $database->uuid,
|
||||
'mount_path' => '/data/configdb',
|
||||
'host_path' => null,
|
||||
'resource_id' => $database->id,
|
||||
'resource_type' => $database->getMorphClass(),
|
||||
'is_readonly' => true
|
||||
]);
|
||||
LocalPersistentVolume::create([
|
||||
'name' => 'mongodb-db-' . $database->uuid,
|
||||
'mount_path' => '/data/db',
|
||||
'host_path' => null,
|
||||
'resource_id' => $database->id,
|
||||
'resource_type' => $database->getMorphClass(),
|
||||
@ -34,6 +42,20 @@ class StandaloneMongodb extends BaseModel
|
||||
});
|
||||
}
|
||||
|
||||
public function mongoInitdbRootPassword(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: function ($value) {
|
||||
try {
|
||||
return decrypt($value);
|
||||
} catch (\Throwable $th) {
|
||||
$this->mongo_initdb_root_password = encrypt($value);
|
||||
$this->save();
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
public function portsMappings(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
@ -55,8 +77,9 @@ class StandaloneMongodb extends BaseModel
|
||||
{
|
||||
return 'standalone-mongodb';
|
||||
}
|
||||
public function getDbUrl() {
|
||||
if ($this->is_public) {
|
||||
public function getDbUrl(bool $useInternal = false)
|
||||
{
|
||||
if ($this->is_public && !$useInternal) {
|
||||
return "mongodb://{$this->mongo_initdb_root_username}:{$this->mongo_initdb_root_password}@{$this->destination->server->getIp}:{$this->public_port}/?directConnection=true";
|
||||
} else {
|
||||
return "mongodb://{$this->mongo_initdb_root_username}:{$this->mongo_initdb_root_password}@{$this->uuid}:27017/?directConnection=true";
|
||||
|
106
app/Models/StandaloneMysql.php
Normal file
106
app/Models/StandaloneMysql.php
Normal file
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class StandaloneMysql extends BaseModel
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $casts = [
|
||||
'mysql_password' => 'encrypted',
|
||||
'mysql_root_password' => 'encrypted',
|
||||
];
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::created(function ($database) {
|
||||
LocalPersistentVolume::create([
|
||||
'name' => 'mysql-data-' . $database->uuid,
|
||||
'mount_path' => '/var/lib/mysql',
|
||||
'host_path' => null,
|
||||
'resource_id' => $database->id,
|
||||
'resource_type' => $database->getMorphClass(),
|
||||
'is_readonly' => true
|
||||
]);
|
||||
});
|
||||
static::deleting(function ($database) {
|
||||
$storages = $database->persistentStorages()->get();
|
||||
foreach ($storages as $storage) {
|
||||
instant_remote_process(["docker volume rm -f $storage->name"], $database->destination->server, false);
|
||||
}
|
||||
$database->scheduledBackups()->delete();
|
||||
$database->persistentStorages()->delete();
|
||||
$database->environment_variables()->delete();
|
||||
});
|
||||
}
|
||||
public function type(): string
|
||||
{
|
||||
return 'standalone-mysql';
|
||||
}
|
||||
|
||||
public function portsMappings(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
set: fn ($value) => $value === "" ? null : $value,
|
||||
);
|
||||
}
|
||||
|
||||
public function portsMappingsArray(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => is_null($this->ports_mappings)
|
||||
? []
|
||||
: explode(',', $this->ports_mappings),
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
public function getDbUrl(bool $useInternal = false): string
|
||||
{
|
||||
if ($this->is_public && !$useInternal) {
|
||||
return "mysql://{$this->mysql_user}:{$this->mysql_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mysql_database}";
|
||||
} else {
|
||||
return "mysql://{$this->mysql_user}:{$this->mysql_password}@{$this->uuid}:3306/{$this->mysql_database}";
|
||||
}
|
||||
}
|
||||
|
||||
public function environment()
|
||||
{
|
||||
return $this->belongsTo(Environment::class);
|
||||
}
|
||||
|
||||
public function fileStorages()
|
||||
{
|
||||
return $this->morphMany(LocalFileVolume::class, 'resource');
|
||||
}
|
||||
|
||||
public function destination()
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
|
||||
public function environment_variables(): HasMany
|
||||
{
|
||||
return $this->hasMany(EnvironmentVariable::class);
|
||||
}
|
||||
|
||||
public function runtime_environment_variables(): HasMany
|
||||
{
|
||||
return $this->hasMany(EnvironmentVariable::class);
|
||||
}
|
||||
|
||||
public function persistentStorages()
|
||||
{
|
||||
return $this->morphMany(LocalPersistentVolume::class, 'resource');
|
||||
}
|
||||
|
||||
public function scheduledBackups()
|
||||
{
|
||||
return $this->morphMany(ScheduledDatabaseBackup::class, 'database');
|
||||
}
|
||||
}
|
@ -46,8 +46,6 @@ class StandalonePostgresql extends BaseModel
|
||||
);
|
||||
}
|
||||
|
||||
// Normal Deployments
|
||||
|
||||
public function portsMappingsArray(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
@ -62,9 +60,9 @@ class StandalonePostgresql extends BaseModel
|
||||
{
|
||||
return 'standalone-postgresql';
|
||||
}
|
||||
public function getDbUrl(): string
|
||||
public function getDbUrl(bool $useInternal = false): string
|
||||
{
|
||||
if ($this->is_public) {
|
||||
if ($this->is_public && !$useInternal) {
|
||||
return "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->postgres_db}";
|
||||
} else {
|
||||
return "postgres://{$this->postgres_user}:{$this->postgres_password}@{$this->uuid}:5432/{$this->postgres_db}";
|
||||
|
@ -48,7 +48,7 @@ class RouteServiceProvider extends ServiceProvider
|
||||
if ($request->path() === 'api/health') {
|
||||
return Limit::perMinute(1000)->by($request->user()?->id ?: $request->ip());
|
||||
}
|
||||
return Limit::perMinute(30)->by($request->user()?->id ?: $request->ip());
|
||||
return Limit::perMinute(200)->by($request->user()?->id ?: $request->ip());
|
||||
});
|
||||
RateLimiter::for('5', function (Request $request) {
|
||||
return Limit::perMinute(5)->by($request->user()?->id ?: $request->ip());
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
const DATABASE_TYPES = ['postgresql','redis', 'mongodb'];
|
||||
const DATABASE_TYPES = ['postgresql', 'redis', 'mongodb', 'mysql', 'mariadb'];
|
||||
const VALID_CRON_STRINGS = [
|
||||
'every_minute' => '* * * * *',
|
||||
'hourly' => '0 * * * *',
|
||||
|
@ -2,7 +2,9 @@
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\StandaloneDocker;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\StandaloneRedis;
|
||||
use Visus\Cuid2\Cuid2;
|
||||
@ -58,6 +60,36 @@ function create_standalone_mongodb($environment_id, $destination_uuid): Standalo
|
||||
'destination_type' => $destination->getMorphClass(),
|
||||
]);
|
||||
}
|
||||
function create_standalone_mysql($environment_id, $destination_uuid): StandaloneMysql
|
||||
{
|
||||
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
|
||||
if (!$destination) {
|
||||
throw new Exception('Destination not found');
|
||||
}
|
||||
return StandaloneMysql::create([
|
||||
'name' => generate_database_name('mysql'),
|
||||
'mysql_root_password' => \Illuminate\Support\Str::password(symbols: false),
|
||||
'mysql_password' => \Illuminate\Support\Str::password(symbols: false),
|
||||
'environment_id' => $environment_id,
|
||||
'destination_id' => $destination->id,
|
||||
'destination_type' => $destination->getMorphClass(),
|
||||
]);
|
||||
}
|
||||
function create_standalone_mariadb($environment_id, $destination_uuid): StandaloneMariadb
|
||||
{
|
||||
$destination = StandaloneDocker::where('uuid', $destination_uuid)->first();
|
||||
if (!$destination) {
|
||||
throw new Exception('Destination not found');
|
||||
}
|
||||
return StandaloneMariadb::create([
|
||||
'name' => generate_database_name('mariadb'),
|
||||
'mariadb_root_password' => \Illuminate\Support\Str::password(symbols: false),
|
||||
'mariadb_password' => \Illuminate\Support\Str::password(symbols: false),
|
||||
'environment_id' => $environment_id,
|
||||
'destination_id' => $destination->id,
|
||||
'destination_type' => $destination->getMorphClass(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete file locally on the filesystem.
|
||||
|
@ -4,7 +4,9 @@ use App\Models\Application;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Server;
|
||||
use App\Models\Service;
|
||||
use App\Models\StandaloneMariadb;
|
||||
use App\Models\StandaloneMongodb;
|
||||
use App\Models\StandaloneMysql;
|
||||
use App\Models\StandalonePostgresql;
|
||||
use App\Models\StandaloneRedis;
|
||||
use App\Models\Team;
|
||||
@ -484,5 +486,18 @@ function queryResourcesByUuid(string $uuid)
|
||||
if ($redis) return $redis;
|
||||
$mongodb = StandaloneMongodb::whereUuid($uuid)->first();
|
||||
if ($mongodb) return $mongodb;
|
||||
$mysql = StandaloneMysql::whereUuid($uuid)->first();
|
||||
if ($mysql) return $mysql;
|
||||
$mariadb = StandaloneMariadb::whereUuid($uuid)->first();
|
||||
if ($mariadb) return $mariadb;
|
||||
return $resource;
|
||||
}
|
||||
|
||||
function generateDeployWebhook($resource) {
|
||||
$baseUrl = base_url();
|
||||
$api = Url::fromString($baseUrl) . '/api/v1';
|
||||
$endpoint = '/deploy';
|
||||
$uuid = data_get($resource, 'uuid');
|
||||
$url = $api . $endpoint . "?uuid=$uuid&force=false";
|
||||
return $url;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
"laravel/ui": "^4.2",
|
||||
"lcobucci/jwt": "^5.0.0",
|
||||
"league/flysystem-aws-s3-v3": "^3.0",
|
||||
"league/flysystem-sftp-v3": "^3.0",
|
||||
"livewire/livewire": "^v2.12.3",
|
||||
"lorisleiva/laravel-actions": "^2.7",
|
||||
"masmerise/livewire-toaster": "^1.2",
|
||||
|
62
composer.lock
generated
62
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "de2c45be3f03d43430549d963778dc4a",
|
||||
"content-hash": "21ed976753483557403be75318585442",
|
||||
"packages": [
|
||||
{
|
||||
"name": "aws/aws-crt-php",
|
||||
@ -2938,6 +2938,66 @@
|
||||
],
|
||||
"time": "2023-08-30T10:23:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/flysystem-sftp-v3",
|
||||
"version": "3.16.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/flysystem-sftp-v3.git",
|
||||
"reference": "1ba682def8e87fd7fa00883629553c0200d2e974"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/flysystem-sftp-v3/zipball/1ba682def8e87fd7fa00883629553c0200d2e974",
|
||||
"reference": "1ba682def8e87fd7fa00883629553c0200d2e974",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"league/flysystem": "^3.0.14",
|
||||
"league/mime-type-detection": "^1.0.0",
|
||||
"php": "^8.0.2",
|
||||
"phpseclib/phpseclib": "^3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\Flysystem\\PhpseclibV3\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Frank de Jonge",
|
||||
"email": "info@frankdejonge.nl"
|
||||
}
|
||||
],
|
||||
"description": "SFTP filesystem adapter for Flysystem.",
|
||||
"keywords": [
|
||||
"Flysystem",
|
||||
"file",
|
||||
"files",
|
||||
"filesystem",
|
||||
"sftp"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/thephpleague/flysystem-sftp-v3/issues",
|
||||
"source": "https://github.com/thephpleague/flysystem-sftp-v3/tree/3.16.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://ecologi.com/frankdejonge",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/frankdejonge",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-08-30T10:25:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/mime-type-detection",
|
||||
"version": "1.13.0",
|
||||
|
@ -3,11 +3,11 @@
|
||||
return [
|
||||
|
||||
// @see https://docs.sentry.io/product/sentry-basics/dsn-explainer/
|
||||
'dsn' => 'https://72f02655749d5d687297b6b9f078b8b9@o1082494.ingest.sentry.io/4505347448045568',
|
||||
'dsn' => 'https://c35fe90ee56e18b220bb55e8217d4839@o1082494.ingest.sentry.io/4505347448045568',
|
||||
|
||||
// The release version of your application
|
||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||
'release' => '4.0.0-beta.98',
|
||||
'release' => '4.0.0-beta.103',
|
||||
// When left empty or `null` the Laravel environment will be used
|
||||
'environment' => config('app.env'),
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
<?php
|
||||
|
||||
return '4.0.0-beta.98';
|
||||
return '4.0.0-beta.103';
|
||||
|
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('standalone_mysqls', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('uuid')->unique();
|
||||
$table->string('name');
|
||||
$table->string('description')->nullable();
|
||||
|
||||
$table->text('mysql_root_password');
|
||||
$table->string('mysql_user')->default('mysql');
|
||||
$table->text('mysql_password');
|
||||
$table->string('mysql_database')->default('default');
|
||||
$table->longText('mysql_conf')->nullable();
|
||||
|
||||
$table->string('status')->default('exited');
|
||||
|
||||
$table->string('image')->default('mysql:8');
|
||||
$table->boolean('is_public')->default(false);
|
||||
$table->integer('public_port')->nullable();
|
||||
$table->text('ports_mappings')->nullable();
|
||||
|
||||
$table->string('limits_memory')->default("0");
|
||||
$table->string('limits_memory_swap')->default("0");
|
||||
$table->integer('limits_memory_swappiness')->default(60);
|
||||
$table->string('limits_memory_reservation')->default("0");
|
||||
|
||||
$table->string('limits_cpus')->default("0");
|
||||
$table->string('limits_cpuset')->nullable()->default("0");
|
||||
$table->integer('limits_cpu_shares')->default(1024);
|
||||
|
||||
$table->timestamp('started_at')->nullable();
|
||||
$table->morphs('destination');
|
||||
|
||||
$table->foreignId('environment_id')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('standalone_mysqls');
|
||||
}
|
||||
};
|
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('standalone_mariadbs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('uuid')->unique();
|
||||
$table->string('name');
|
||||
$table->string('description')->nullable();
|
||||
|
||||
$table->text('mariadb_root_password');
|
||||
$table->string('mariadb_user')->default('mariadb');
|
||||
$table->text('mariadb_password');
|
||||
$table->string('mariadb_database')->default('default');
|
||||
$table->longText('mariadb_conf')->nullable();
|
||||
|
||||
$table->string('status')->default('exited');
|
||||
|
||||
$table->string('image')->default('mariadb:11');
|
||||
$table->boolean('is_public')->default(false);
|
||||
$table->integer('public_port')->nullable();
|
||||
$table->text('ports_mappings')->nullable();
|
||||
|
||||
$table->string('limits_memory')->default("0");
|
||||
$table->string('limits_memory_swap')->default("0");
|
||||
$table->integer('limits_memory_swappiness')->default(60);
|
||||
$table->string('limits_memory_reservation')->default("0");
|
||||
|
||||
$table->string('limits_cpus')->default("0");
|
||||
$table->string('limits_cpuset')->nullable()->default("0");
|
||||
$table->integer('limits_cpu_shares')->default(1024);
|
||||
|
||||
$table->timestamp('started_at')->nullable();
|
||||
$table->morphs('destination');
|
||||
|
||||
$table->foreignId('environment_id')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('standalone_mariadbs');
|
||||
}
|
||||
};
|
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('environment_variables', function (Blueprint $table) {
|
||||
$table->foreignId('standalone_mysql_id')->nullable();
|
||||
$table->foreignId('standalone_mariadb_id')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('environment_variables', function (Blueprint $table) {
|
||||
$table->dropColumn('standalone_mysql_id');
|
||||
$table->dropColumn('standalone_mariadb_id');
|
||||
});
|
||||
}
|
||||
};
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('environment_variables', function (Blueprint $table) {
|
||||
$table->boolean('is_shown_once')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('environment_variables', function (Blueprint $table) {
|
||||
$table->dropColumn('is_shown_once');
|
||||
});
|
||||
}
|
||||
};
|
@ -28,3 +28,4 @@ networks:
|
||||
coolify:
|
||||
name: coolify
|
||||
driver: bridge
|
||||
external: true
|
||||
|
@ -7,7 +7,11 @@
|
||||
href="{{ route('project.database.logs', $parameters) }}">
|
||||
<button>Logs</button>
|
||||
</a>
|
||||
@if ($database->getMorphClass() === 'App\Models\StandalonePostgresql' || $database->getMorphClass() === 'App\Models\StandaloneMongodb')
|
||||
@if (
|
||||
$database->getMorphClass() === 'App\Models\StandalonePostgresql' ||
|
||||
$database->getMorphClass() === 'App\Models\StandaloneMongodb' ||
|
||||
$database->getMorphClass() === 'App\Models\StandaloneMysql' ||
|
||||
$database->getMorphClass() === 'App\Models\StandaloneMariadb')
|
||||
<a class="{{ request()->routeIs('project.database.backups.all') ? 'text-white' : '' }}"
|
||||
href="{{ route('project.database.backups.all', $parameters) }}">
|
||||
<button>Backups</button>
|
||||
|
@ -96,8 +96,7 @@
|
||||
}
|
||||
|
||||
function copyToClipboard(text) {
|
||||
navigator.clipboard.writeText(text);
|
||||
Livewire.emit('success', 'Copied to clipboard.');
|
||||
navigator?.clipboard?.writeText(text) && Livewire.emit('success', 'Copied to clipboard.');
|
||||
}
|
||||
|
||||
Livewire.on('reloadWindow', (timeout) => {
|
||||
|
@ -35,6 +35,14 @@
|
||||
<x-forms.input label="Databases To Include"
|
||||
helper="A list of databases to backup. You can specify which collection(s) per database to exclude from the backup. Empty will include all databases and collections.<br><br>Example:<br><br>database1:collection1,collection2|database2:collection3,collection4<br><br> database1 will include all collections except collection1 and collection2. <br>database2 will include all collections except collection3 and collection4.<br><br>Another Example:<br><br>database1:collection1|database2<br><br> database1 will include all collections except collection1.<br>database2 will include ALL collections."
|
||||
id="backup.databases_to_backup" />
|
||||
@elseif($backup->database_type === 'App\Models\StandaloneMysql')
|
||||
<x-forms.input label="Databases To Backup"
|
||||
helper="Comma separated list of databases to backup. Empty will include the default one."
|
||||
id="backup.databases_to_backup" />
|
||||
@elseif($backup->database_type === 'App\Models\StandaloneMariadb')
|
||||
<x-forms.input label="Databases To Backup"
|
||||
helper="Comma separated list of databases to backup. Empty will include the default one."
|
||||
id="backup.databases_to_backup" />
|
||||
@endif
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
|
@ -1,8 +0,0 @@
|
||||
<div class="flex gap-2">
|
||||
<div class="flex-1"></div>
|
||||
|
||||
{{-- @if (data_get($execution, 'status') !== 'failed') --}}
|
||||
{{-- <x-forms.button class="bg-coollabs-100 hover:bg-coollabs" wire:click="download">Download</x-forms.button> --}}
|
||||
{{-- @endif --}}
|
||||
<x-forms.button isError wire:click="delete">Delete</x-forms.button>
|
||||
</div>
|
@ -1,9 +1,10 @@
|
||||
<div class="flex flex-col-reverse gap-2">
|
||||
@forelse($executions as $execution)
|
||||
<form class="flex flex-col p-2 border-dotted border-1 bg-coolgray-300" @class([
|
||||
'border-green-500' => data_get($execution, 'status') === 'success',
|
||||
'border-red-500' => data_get($execution, 'status') === 'failed',
|
||||
])>
|
||||
<form wire:key="{{ data_get($execution, 'id') }}" class="flex flex-col p-2 border-dotted border-1 bg-coolgray-300"
|
||||
@class([
|
||||
'border-green-500' => data_get($execution, 'status') === 'success',
|
||||
'border-red-500' => data_get($execution, 'status') === 'failed',
|
||||
])>
|
||||
<div>Database: {{ data_get($execution, 'database_name', 'N/A') }}</div>
|
||||
<div>Status: {{ data_get($execution, 'status') }}</div>
|
||||
<div>Started At: {{ data_get($execution, 'created_at') }}</div>
|
||||
@ -14,9 +15,24 @@
|
||||
kB / {{ round((int) data_get($execution, 'size') / 1024 / 1024, 3) }} MB
|
||||
</div>
|
||||
<div>Location: {{ data_get($execution, 'filename', 'N/A') }}</div>
|
||||
<livewire:project.database.backup-execution :execution="$execution" :wire:key="$execution->id" />
|
||||
<div class="flex gap-2">
|
||||
<div class="flex-1"></div>
|
||||
@if (data_get($execution, 'status') === 'success')
|
||||
<x-forms.button class=" hover:bg-coolgray-400"
|
||||
wire:click="download({{ data_get($execution, 'id') }})">Download</x-forms.button>
|
||||
@endif
|
||||
<x-forms.button isError onclick="sure({{ data_get($execution, 'id') }})">Delete</x-forms.button>
|
||||
</div>
|
||||
</form>
|
||||
@empty
|
||||
<div>No executions found.</div>
|
||||
@endforelse
|
||||
<script>
|
||||
function sure($id) {
|
||||
const sure = confirm('Are you sure you want to delete this backup?');
|
||||
if (sure) {
|
||||
Livewire.emit('deleteBackup', $id);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
|
@ -0,0 +1,58 @@
|
||||
<div>
|
||||
<form wire:submit.prevent="submit" class="flex flex-col gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<h2>General</h2>
|
||||
<x-forms.button type="submit">
|
||||
Save
|
||||
</x-forms.button>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input label="Name" id="database.name" />
|
||||
<x-forms.input label="Description" id="database.description" />
|
||||
<x-forms.input label="Image" id="database.image" required
|
||||
helper="For all available images, check here:<br><br><a target='_blank' href='https://hub.docker.com/_/mariadb'>https://hub.docker.com/_/mariadb</a>" />
|
||||
</div>
|
||||
@if ($database->started_at)
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input label="Root Password" id="database.mariadb_root_password" type="password" readonly
|
||||
helper="You can only change this in the database." />
|
||||
<x-forms.input label="Normal User" id="database.mariadb_user" required readonly
|
||||
helper="You can only change this in the database." />
|
||||
<x-forms.input label="Normal User Password" id="database.mariadb_password" type="password" required
|
||||
readonly helper="You can only change this in the database." />
|
||||
<x-forms.input label="Initial Database" id="database.mariadb_database"
|
||||
placeholder="If empty, it will be the same as Username." readonly
|
||||
helper="You can only change this in the database." />
|
||||
</div>
|
||||
@else
|
||||
<div class="pt-8 text-warning">Please verify these values. You can only modify them before the initial
|
||||
start. After that, you need to modify it in the database.
|
||||
</div>
|
||||
<div class="flex gap-2 pb-8">
|
||||
<x-forms.input label="Root Password" id="database.mariadb_root_password" type="password"
|
||||
helper="You can only change this in the database." />
|
||||
<x-forms.input label="Normal User" id="database.mariadb_user" required
|
||||
helper="You can only change this in the database." />
|
||||
<x-forms.input label="Normal User Password" id="database.mariadb_password" type="password" required
|
||||
helper="You can only change this in the database." />
|
||||
<x-forms.input label="Initial Database" id="database.mariadb_database"
|
||||
placeholder="If empty, it will be the same as Username."
|
||||
helper="You can only change this in the database." />
|
||||
</div>
|
||||
@endif
|
||||
<div class="flex flex-col gap-2">
|
||||
<h3 class="py-2">Network</h3>
|
||||
<div class="flex items-end gap-2">
|
||||
<x-forms.input placeholder="3000:5432" id="database.ports_mappings" label="Ports Mappings"
|
||||
helper="A comma separated list of ports you would like to map to the host system.<br><span class='inline-block font-bold text-warning'>Example</span>3000:5432,3002:5433" />
|
||||
<x-forms.input placeholder="5432" disabled="{{ $database->is_public }}" id="database.public_port"
|
||||
label="Public Port" />
|
||||
<x-forms.checkbox instantSave id="database.is_public" label="Accessible over the internet" />
|
||||
</div>
|
||||
<x-forms.input label="MariaDB URL"
|
||||
helper="If you change the user/password/port, this could be different. This is with the default values."
|
||||
type="password" readonly wire:model="db_url" />
|
||||
</div>
|
||||
<x-forms.textarea label="Custom MariaDB Configuration" rows="10" id="database.mariadb_conf" />
|
||||
</form>
|
||||
</div>
|
@ -0,0 +1,58 @@
|
||||
<div>
|
||||
<form wire:submit.prevent="submit" class="flex flex-col gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<h2>General</h2>
|
||||
<x-forms.button type="submit">
|
||||
Save
|
||||
</x-forms.button>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input label="Name" id="database.name" />
|
||||
<x-forms.input label="Description" id="database.description" />
|
||||
<x-forms.input label="Image" id="database.image" required
|
||||
helper="For all available images, check here:<br><br><a target='_blank' href='https://hub.docker.com/_/mysql'>https://hub.docker.com/_/mysql</a>" />
|
||||
</div>
|
||||
@if ($database->started_at)
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input label="Root Password" id="database.mysql_root_password" type="password" readonly
|
||||
helper="You can only change this in the database." />
|
||||
<x-forms.input label="Normal User" id="database.mysql_user" required readonly
|
||||
helper="You can only change this in the database." />
|
||||
<x-forms.input label="Normal User Password" id="database.mysql_password" type="password" required
|
||||
readonly helper="You can only change this in the database." />
|
||||
<x-forms.input label="Initial Database" id="database.mysql_database"
|
||||
placeholder="If empty, it will be the same as Username." readonly
|
||||
helper="You can only change this in the database." />
|
||||
</div>
|
||||
@else
|
||||
<div class="pt-8 text-warning">Please verify these values. You can only modify them before the initial
|
||||
start. After that, you need to modify it in the database.
|
||||
</div>
|
||||
<div class="flex gap-2 pb-8">
|
||||
<x-forms.input label="Root Password" id="database.mysql_root_password" type="password"
|
||||
helper="You can only change this in the database." />
|
||||
<x-forms.input label="Normal User" id="database.mysql_user" required
|
||||
helper="You can only change this in the database." />
|
||||
<x-forms.input label="Normal User Password" id="database.mysql_password" type="password" required
|
||||
helper="You can only change this in the database." />
|
||||
<x-forms.input label="Initial Database" id="database.mysql_database"
|
||||
placeholder="If empty, it will be the same as Username."
|
||||
helper="You can only change this in the database." />
|
||||
</div>
|
||||
@endif
|
||||
<div class="flex flex-col gap-2">
|
||||
<h3 class="py-2">Network</h3>
|
||||
<div class="flex items-end gap-2">
|
||||
<x-forms.input placeholder="3000:5432" id="database.ports_mappings" label="Ports Mappings"
|
||||
helper="A comma separated list of ports you would like to map to the host system.<br><span class='inline-block font-bold text-warning'>Example</span>3000:5432,3002:5433" />
|
||||
<x-forms.input placeholder="5432" disabled="{{ $database->is_public }}" id="database.public_port"
|
||||
label="Public Port" />
|
||||
<x-forms.checkbox instantSave id="database.is_public" label="Accessible over the internet" />
|
||||
</div>
|
||||
<x-forms.input label="MySQL URL"
|
||||
helper="If you change the user/password/port, this could be different. This is with the default values."
|
||||
type="password" readonly wire:model="db_url" />
|
||||
</div>
|
||||
<x-forms.textarea label="Custom Mysql Configuration" rows="10" id="database.mysql_conf" />
|
||||
</form>
|
||||
</div>
|
@ -1,4 +1,4 @@
|
||||
<div x-data x-init="$wire.loadThings">
|
||||
<div x-data x-init="$wire.loadServers">
|
||||
<div class="flex gap-2 ">
|
||||
<h1>New Resource</h1>
|
||||
<div class="w-96">
|
||||
@ -90,7 +90,7 @@
|
||||
New PostgreSQL
|
||||
</div>
|
||||
<div class="description">
|
||||
The most loved relational database in the world.
|
||||
PostgreSQL is an open-source, object-relational database management system known for its robustness, advanced features, and strong standards compliance.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -100,7 +100,7 @@
|
||||
New Redis
|
||||
</div>
|
||||
<div class="description">
|
||||
The open source, in-memory data store for cache, streaming engine, and message broker.
|
||||
Redis is an open-source, in-memory data structure store used as a database, cache, and message broker, known for its high performance, flexibility, and rich data structures.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -110,7 +110,27 @@
|
||||
New MongoDB
|
||||
</div>
|
||||
<div class="description">
|
||||
MongoDB is a source-available cross-platform document-oriented database program.
|
||||
MongoDB is a source-available, NoSQL database program that uses JSON-like documents with optional schemas, known for its flexibility, scalability, and wide range of application use cases.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box group" wire:click="setType('mysql')">
|
||||
<div class="flex flex-col mx-6">
|
||||
<div class="font-bold text-white group-hover:text-white">
|
||||
New MySQL
|
||||
</div>
|
||||
<div class="description">
|
||||
MySQL is an open-source relational database management system known for its speed, reliability, and flexibility in managing and accessing data.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box group" wire:click="setType('mariadb')">
|
||||
<div class="flex flex-col mx-6">
|
||||
<div class="font-bold text-white group-hover:text-white">
|
||||
New Mariadb
|
||||
</div>
|
||||
<div class="description">
|
||||
MariaDB is an open-source relational database management system that serves as a drop-in replacement for MySQL, offering more robust, scalable, and reliable SQL server capabilities.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -128,12 +148,15 @@
|
||||
<div class="flex items-center gap-2">
|
||||
<h2 class="py-4">Services</h2>
|
||||
<x-forms.button wire:click='loadServices(true)'>Reload Services List</x-forms.button>
|
||||
<input
|
||||
class="w-full text-white rounded input input-sm bg-coolgray-200 disabled:bg-coolgray-200/50 disabled:border-none placeholder:text-coolgray-500 read-only:text-neutral-500 read-only:bg-coolgray-200/50"
|
||||
wire:model.debounce.200ms="search" placeholder="Search..."></input>
|
||||
</div>
|
||||
<div class="grid justify-start grid-cols-1 gap-2 text-left xl:grid-cols-3">
|
||||
@if ($loadingServices)
|
||||
<span class="loading loading-xs loading-spinner"></span>
|
||||
@else
|
||||
@foreach ($services as $serviceName => $service)
|
||||
@forelse ($services as $serviceName => $service)
|
||||
@if (data_get($service, 'disabled'))
|
||||
<button class="text-left cursor-not-allowed bg-coolgray-200/20 box-without-bg" disabled>
|
||||
<div class="flex flex-col mx-6">
|
||||
@ -158,7 +181,9 @@
|
||||
</div>
|
||||
</button>
|
||||
@endif
|
||||
@endforeach
|
||||
@empty
|
||||
<div>No service found.</div>
|
||||
@endforelse
|
||||
@endif
|
||||
</div>
|
||||
<div class="py-4 pb-10">Trademarks Policy: The respective trademarks mentioned here are owned by the
|
||||
|
@ -4,16 +4,25 @@
|
||||
<div class="flex h-full pt-6">
|
||||
<div class="flex flex-col items-start gap-4 min-w-fit">
|
||||
<a target="_blank" href="{{ $service->documentation() }}">Documentation <x-external-link /></a>
|
||||
<a :class="activeTab === 'service-stack' && 'text-white'" @click.prevent="activeTab = 'service-stack';
|
||||
window.location.hash = 'service-stack'" href="#">Service Stack</a>
|
||||
<a :class="activeTab === 'storages' && 'text-white'" @click.prevent="activeTab = 'storages';
|
||||
window.location.hash = 'storages'" href="#">Storages</a>
|
||||
<a :class="activeTab === 'service-stack' && 'text-white'"
|
||||
@click.prevent="activeTab = 'service-stack';
|
||||
window.location.hash = 'service-stack'"
|
||||
href="#">Service Stack</a>
|
||||
<a :class="activeTab === 'storages' && 'text-white'"
|
||||
@click.prevent="activeTab = 'storages';
|
||||
window.location.hash = 'storages'"
|
||||
href="#">Storages</a>
|
||||
<a :class="activeTab === 'webhooks' && 'text-white'"
|
||||
@click.prevent="activeTab = 'webhooks'; window.location.hash = 'webhooks'" href="#">Webhooks
|
||||
</a>
|
||||
<a :class="activeTab === 'environment-variables' && 'text-white'"
|
||||
@click.prevent="activeTab = 'environment-variables'; window.location.hash = 'environment-variables'"
|
||||
href="#">Environment
|
||||
Variables</a>
|
||||
<a :class="activeTab === 'danger' && 'text-white'" @click.prevent="activeTab = 'danger';
|
||||
window.location.hash = 'danger'" href="#">Danger Zone
|
||||
<a :class="activeTab === 'danger' && 'text-white'"
|
||||
@click.prevent="activeTab = 'danger';
|
||||
window.location.hash = 'danger'"
|
||||
href="#">Danger Zone
|
||||
</a>
|
||||
</div>
|
||||
<div class="w-full pl-8">
|
||||
@ -100,7 +109,9 @@
|
||||
@foreach ($databases as $database)
|
||||
<livewire:project.service.storage wire:key="database-{{ $database->id }}" :resource="$database" />
|
||||
@endforeach
|
||||
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'webhooks'">
|
||||
<livewire:project.shared.webhooks :resource="$service" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'environment-variables'">
|
||||
<div x-cloak x-show="activeTab === 'environment-variables'">
|
||||
|
@ -28,8 +28,7 @@
|
||||
@endif
|
||||
@else
|
||||
<form wire:submit.prevent='saveVariables(false)' class="flex flex-col gap-2">
|
||||
<x-forms.textarea rows=25 class="whitespace-pre-wrap"
|
||||
id="variables"></x-forms.textarea>
|
||||
<x-forms.textarea rows=25 class="whitespace-pre-wrap" id="variables"></x-forms.textarea>
|
||||
<x-forms.button type="submit" class="btn btn-primary">Save</x-forms.button>
|
||||
</form>
|
||||
@if ($showPreview)
|
||||
|
@ -6,36 +6,54 @@
|
||||
</x-slot:modalBody>
|
||||
</x-modal>
|
||||
<form wire:submit.prevent='submit' class="flex flex-col items-center gap-2 xl:flex-row">
|
||||
@if ($isDisabled)
|
||||
@if ($isLocked)
|
||||
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
|
||||
<path d="M5 13a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-6z" />
|
||||
<path d="M11 16a1 1 0 1 0 2 0a1 1 0 0 0-2 0m-3-5V7a4 4 0 1 1 8 0v4" />
|
||||
</g>
|
||||
</svg>
|
||||
<x-forms.input disabled id="env.key" />
|
||||
<x-forms.input disabled type="password" id="env.value" />
|
||||
@if ($type !== 'service')
|
||||
<x-forms.checkbox instantSave id="env.is_build_time" label="Build Variable?" />
|
||||
@endif
|
||||
@else
|
||||
<x-forms.input id="env.key" />
|
||||
<x-forms.input type="password" id="env.value" />
|
||||
@if ($type !== 'service')
|
||||
<x-forms.checkbox instantSave id="env.is_build_time" label="Build Variable?" />
|
||||
@if ($isDisabled)
|
||||
<x-forms.input disabled id="env.key" />
|
||||
<x-forms.input disabled type="password" id="env.value" />
|
||||
@if ($type !== 'service')
|
||||
<x-forms.checkbox instantSave id="env.is_build_time" label="Build Variable?" />
|
||||
@endif
|
||||
@else
|
||||
<x-forms.input id="env.key" />
|
||||
<x-forms.input type="password" id="env.value" />
|
||||
@if ($type !== 'service')
|
||||
<x-forms.checkbox instantSave id="env.is_build_time" label="Build Variable?" />
|
||||
@endif
|
||||
@endif
|
||||
@endif
|
||||
<div class="flex gap-2">
|
||||
@if ($isDisabled)
|
||||
<x-forms.button disabled type="submit">
|
||||
Update
|
||||
</x-forms.button>
|
||||
<x-forms.button disabled isError isModal modalId="{{ $modalId }}">
|
||||
Delete
|
||||
</x-forms.button>
|
||||
@else
|
||||
<x-forms.button type="submit">
|
||||
Update
|
||||
</x-forms.button>
|
||||
@if ($isLocked)
|
||||
<x-forms.button isError isModal modalId="{{ $modalId }}">
|
||||
Delete
|
||||
</x-forms.button>
|
||||
@else
|
||||
@if ($isDisabled)
|
||||
<x-forms.button disabled type="submit">
|
||||
Update
|
||||
</x-forms.button>
|
||||
<x-forms.button disabled isError isModal modalId="{{ $modalId }}">
|
||||
Delete
|
||||
</x-forms.button>
|
||||
@else
|
||||
<x-forms.button type="submit">
|
||||
Update
|
||||
</x-forms.button>
|
||||
<x-forms.button wire:click='lock'>
|
||||
Lock
|
||||
</x-forms.button>
|
||||
<x-forms.button isError isModal modalId="{{ $modalId }}">
|
||||
Delete
|
||||
</x-forms.button>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
10
resources/views/livewire/project/shared/webhooks.blade.php
Normal file
10
resources/views/livewire/project/shared/webhooks.blade.php
Normal file
@ -0,0 +1,10 @@
|
||||
<div>
|
||||
<div class="flex items-center gap-2">
|
||||
<h2>Webhooks</h2>
|
||||
<x-helper
|
||||
helper="For more details goto our <a class='text-white underline' href='https://coolify.io/docs/api-endpoints' target='_blank'>docs</a>." />
|
||||
</div>
|
||||
<div>
|
||||
<x-forms.input readonly label="Deploy Webhook (auth required)" id="deploywebhook"></x-forms.input>
|
||||
</div>
|
||||
</div>
|
@ -19,6 +19,9 @@
|
||||
<a :class="activeTab === 'storages' && 'text-white'"
|
||||
@click.prevent="activeTab = 'storages'; window.location.hash = 'storages'" href="#">Storages
|
||||
</a>
|
||||
<a :class="activeTab === 'webhooks' && 'text-white'"
|
||||
@click.prevent="activeTab = 'webhooks'; window.location.hash = 'webhooks'" href="#">Webhooks
|
||||
</a>
|
||||
@if ($application->git_based())
|
||||
<a :class="activeTab === 'previews' && 'text-white'"
|
||||
@click.prevent="activeTab = 'previews'; window.location.hash = 'previews'" href="#">Preview
|
||||
@ -57,6 +60,9 @@
|
||||
<div x-cloak x-show="activeTab === 'storages'">
|
||||
<livewire:project.service.storage :resource="$application" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'webhooks'">
|
||||
<livewire:project.shared.webhooks :resource="$application" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'previews'">
|
||||
<livewire:project.application.previews :application="$application" />
|
||||
</div>
|
||||
|
@ -12,7 +12,7 @@
|
||||
</x-slot:modalSubmit>
|
||||
</x-modal>
|
||||
<div class="pt-6">
|
||||
<livewire:project.database.backup-edit :backup="$backup" :s3s="$s3s" :status="data_get($database,'status')" />
|
||||
<livewire:project.database.backup-edit :backup="$backup" :s3s="$s3s" :status="data_get($database, 'status')" />
|
||||
<h3 class="py-4">Executions</h3>
|
||||
<livewire:project.database.backup-executions :backup="$backup" :executions="$executions" />
|
||||
</div>
|
||||
|
@ -13,36 +13,51 @@
|
||||
</x-modal>
|
||||
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'general' }" class="flex h-full pt-6">
|
||||
<div class="flex flex-col gap-4 min-w-fit">
|
||||
<a :class="activeTab === 'general' && 'text-white'" @click.prevent="activeTab = 'general';
|
||||
window.location.hash = 'general'" href="#">General</a>
|
||||
<a :class="activeTab === 'general' && 'text-white'"
|
||||
@click.prevent="activeTab = 'general';
|
||||
window.location.hash = 'general'"
|
||||
href="#">General</a>
|
||||
<a :class="activeTab === 'environment-variables' && 'text-white'"
|
||||
@click.prevent="activeTab = 'environment-variables'; window.location.hash = 'environment-variables'"
|
||||
href="#">Environment
|
||||
Variables</a>
|
||||
<a :class="activeTab === 'server' && 'text-white'" @click.prevent="activeTab = 'server';
|
||||
window.location.hash = 'server'" href="#">Server
|
||||
<a :class="activeTab === 'server' && 'text-white'"
|
||||
@click.prevent="activeTab = 'server';
|
||||
window.location.hash = 'server'"
|
||||
href="#">Server
|
||||
</a>
|
||||
<a :class="activeTab === 'storages' && 'text-white'" @click.prevent="activeTab = 'storages';
|
||||
window.location.hash = 'storages'" href="#">Storages
|
||||
<a :class="activeTab === 'storages' && 'text-white'"
|
||||
@click.prevent="activeTab = 'storages';
|
||||
window.location.hash = 'storages'"
|
||||
href="#">Storages
|
||||
</a>
|
||||
<a :class="activeTab === 'resource-limits' && 'text-white'" @click.prevent="activeTab = 'resource-limits';
|
||||
window.location.hash = 'resource-limits'" href="#">Resource Limits
|
||||
<a :class="activeTab === 'webhooks' && 'text-white'"
|
||||
@click.prevent="activeTab = 'webhooks'; window.location.hash = 'webhooks'" href="#">Webhooks
|
||||
</a>
|
||||
<a :class="activeTab === 'danger' && 'text-white'" @click.prevent="activeTab = 'danger';
|
||||
window.location.hash = 'danger'" href="#">Danger Zone
|
||||
<a :class="activeTab === 'resource-limits' && 'text-white'"
|
||||
@click.prevent="activeTab = 'resource-limits';
|
||||
window.location.hash = 'resource-limits'"
|
||||
href="#">Resource Limits
|
||||
</a>
|
||||
<a :class="activeTab === 'danger' && 'text-white'"
|
||||
@click.prevent="activeTab = 'danger';
|
||||
window.location.hash = 'danger'"
|
||||
href="#">Danger Zone
|
||||
</a>
|
||||
</div>
|
||||
<div class="w-full pl-8">
|
||||
<div x-cloak x-show="activeTab === 'general'" class="h-full">
|
||||
@if ($database->type() === 'standalone-postgresql')
|
||||
<livewire:project.database.postgresql.general :database="$database" />
|
||||
@endif
|
||||
@if ($database->type() === 'standalone-redis')
|
||||
@elseif ($database->type() === 'standalone-redis')
|
||||
<livewire:project.database.redis.general :database="$database" />
|
||||
@elseif ($database->type() === 'standalone-mongodb')
|
||||
<livewire:project.database.mongodb.general :database="$database" />
|
||||
@elseif ($database->type() === 'standalone-mysql')
|
||||
<livewire:project.database.mysql.general :database="$database" />
|
||||
@elseif ($database->type() === 'standalone-mariadb')
|
||||
<livewire:project.database.mariadb.general :database="$database" />
|
||||
@endif
|
||||
@if ($database->type() === 'standalone-mongodb')
|
||||
<livewire:project.database.mongodb.general :database="$database" />
|
||||
@endif
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'environment-variables'">
|
||||
<livewire:project.shared.environment-variable.all :resource="$database" />
|
||||
@ -53,6 +68,9 @@
|
||||
<div x-cloak x-show="activeTab === 'storages'">
|
||||
<livewire:project.service.storage :resource="$database" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'webhooks'">
|
||||
<livewire:project.shared.webhooks :resource="$database" />
|
||||
</div>
|
||||
<div x-cloak x-show="activeTab === 'resource-limits'">
|
||||
<livewire:project.shared.resource-limits :resource="$database" />
|
||||
</div>
|
||||
|
@ -2,17 +2,21 @@
|
||||
<div class="flex flex-col">
|
||||
<div class="flex items-center gap-2">
|
||||
<h1>Resources</h1>
|
||||
@if ($environment->can_delete_environment())
|
||||
@if ($environment->isEmpty())
|
||||
<a class="font-normal text-white normal-case border-none rounded hover:no-underline btn btn-primary btn-sm no-animation"
|
||||
href="{{ route('project.clone', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => request()->route('environment_name')]) }}">
|
||||
Clone
|
||||
</a>
|
||||
<livewire:project.delete-environment :environment_id="$environment->id" />
|
||||
@else
|
||||
<a href="{{ route('project.resources.new', ['project_uuid' => request()->route('project_uuid'), 'environment_name' => request()->route('environment_name')]) }} "
|
||||
class="font-normal text-white normal-case border-none rounded hover:no-underline btn btn-primary btn-sm no-animation">+
|
||||
New</a>
|
||||
<a class="font-normal text-white normal-case border-none rounded hover:no-underline btn btn-primary btn-sm no-animation"
|
||||
href="{{ route('project.clone', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => request()->route('environment_name')]) }}">
|
||||
Clone
|
||||
</a>
|
||||
@endif
|
||||
<a class="font-normal text-white normal-case border-none rounded hover:no-underline btn btn-primary btn-sm no-animation"
|
||||
href="{{ route('project.clone', ['project_uuid' => data_get($project, 'uuid'), 'environment_name' => request()->route('environment_name')]) }}">
|
||||
Clone
|
||||
</a>
|
||||
</div>
|
||||
<nav class="flex pt-2 pb-10">
|
||||
<ol class="flex items-center">
|
||||
@ -36,7 +40,7 @@
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
@if ($environment->can_delete_environment())
|
||||
@if ($environment->isEmpty())
|
||||
<a href="{{ route('project.resources.new', ['project_uuid' => request()->route('project_uuid'), 'environment_name' => request()->route('environment_name')]) }} "
|
||||
class="items-center justify-center box">+ Add New Resource</a>
|
||||
@endif
|
||||
|
@ -1,6 +1,8 @@
|
||||
<?php
|
||||
|
||||
use App\Actions\Database\StartMariadb;
|
||||
use App\Actions\Database\StartMongodb;
|
||||
use App\Actions\Database\StartMysql;
|
||||
use App\Actions\Database\StartPostgresql;
|
||||
use App\Actions\Database\StartRedis;
|
||||
use App\Actions\Service\StartService;
|
||||
@ -32,7 +34,6 @@ Route::group([
|
||||
$teamId = data_get($token, 'team_id');
|
||||
$uuid = $request->query->get('uuid');
|
||||
$force = $request->query->get('force') ?? false;
|
||||
|
||||
if (is_null($teamId)) {
|
||||
return response()->json(['error' => 'Invalid token.'], 400);
|
||||
}
|
||||
@ -50,29 +51,56 @@ Route::group([
|
||||
);
|
||||
return response()->json(['message' => 'Deployment queued.'], 200);
|
||||
} else if ($type === 'App\Models\StandalonePostgresql') {
|
||||
if (str($resource->status)->startsWith('running')) {
|
||||
return response()->json(['message' => 'Database already running.'], 200);
|
||||
}
|
||||
StartPostgresql::run($resource);
|
||||
$resource->update([
|
||||
'started_at' => now(),
|
||||
]);
|
||||
return response()->json(['message' => 'Database started.'], 200);
|
||||
} else if ($type === 'App\Models\StandaloneRedis') {
|
||||
if (str($resource->status)->startsWith('running')) {
|
||||
return response()->json(['message' => 'Database already running.'], 200);
|
||||
}
|
||||
StartRedis::run($resource);
|
||||
$resource->update([
|
||||
'started_at' => now(),
|
||||
]);
|
||||
return response()->json(['message' => 'Database started.'], 200);
|
||||
} else if ($type === 'App\Models\StandaloneMongodb') {
|
||||
if (str($resource->status)->startsWith('running')) {
|
||||
return response()->json(['message' => 'Database already running.'], 200);
|
||||
}
|
||||
StartMongodb::run($resource);
|
||||
$resource->update([
|
||||
'started_at' => now(),
|
||||
]);
|
||||
return response()->json(['message' => 'Database started.'], 200);
|
||||
}else if ($type === 'App\Models\Service') {
|
||||
} else if ($type === 'App\Models\StandaloneMysql') {
|
||||
if (str($resource->status)->startsWith('running')) {
|
||||
return response()->json(['message' => 'Database already running.'], 200);
|
||||
}
|
||||
StartMysql::run($resource);
|
||||
$resource->update([
|
||||
'started_at' => now(),
|
||||
]);
|
||||
return response()->json(['message' => 'Database started.'], 200);
|
||||
} else if ($type === 'App\Models\StandaloneMariadb') {
|
||||
if (str($resource->status)->startsWith('running')) {
|
||||
return response()->json(['message' => 'Database already running.'], 200);
|
||||
}
|
||||
StartMariadb::run($resource);
|
||||
$resource->update([
|
||||
'started_at' => now(),
|
||||
]);
|
||||
return response()->json(['message' => 'Database started.'], 200);
|
||||
} else if ($type === 'App\Models\Service') {
|
||||
StartService::run($resource);
|
||||
return response()->json(['message' => 'Service started.'], 200);
|
||||
return response()->json(['message' => 'Service started. It could take a while, be patient.'], 200);
|
||||
}
|
||||
}
|
||||
return response()->json(['error' => 'No resource found.'], 404);
|
||||
return response()->json(['error' => "No resource found with {$uuid}."], 404);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
## Always run "php artisan app:sync-to-bunny-cdn --env=secrets" or "scripts/run sync-bunny" if you update this file.
|
||||
###########
|
||||
|
||||
VERSION="1.0.0"
|
||||
VERSION="1.0.1"
|
||||
DOCKER_VERSION="24.0"
|
||||
|
||||
CDN="https://cdn.coollabs.io/coolify"
|
||||
|
@ -5,7 +5,7 @@
|
||||
## Always run "php artisan app:sync-to-bunny-cdn --env=secrets" if you update this file.
|
||||
###########
|
||||
|
||||
VERSION="1.0.0"
|
||||
VERSION="1.0.1"
|
||||
CDN="https://cdn.coollabs.io/coolify"
|
||||
|
||||
curl -fsSL $CDN/docker-compose.yml -o /data/coolify/source/docker-compose.yml
|
||||
@ -15,4 +15,7 @@ curl -fsSL $CDN/.env.production -o /data/coolify/source/.env.production
|
||||
# Merge .env and .env.production. New values will be added to .env
|
||||
sort -u -t '=' -k 1,1 /data/coolify/source/.env /data/coolify/source/.env.production | sed '/^$/d' > /data/coolify/source/.env.temp && mv /data/coolify/source/.env.temp /data/coolify/source/.env
|
||||
|
||||
# Make sure coolify network exists
|
||||
docker network create coolify 2>/dev/null
|
||||
|
||||
docker run --pull always -v /data/coolify/source:/data/coolify/source -v /var/run/docker.sock:/var/run/docker.sock --rm ghcr.io/coollabsio/coolify-helper bash -c "LATEST_IMAGE=${1:-} docker compose --env-file /data/coolify/source/.env -f /data/coolify/source/docker-compose.yml -f /data/coolify/source/docker-compose.prod.yml up -d --pull always --remove-orphans --force-recreate"
|
||||
|
@ -1,5 +1,6 @@
|
||||
# documentation: https://docs.appsmith.com
|
||||
# slogan: Appsmith is an open-source, self-hosted application development platform that enables you to build powerful web applications with ease.
|
||||
# tags: lowcode,nocode,no,low,platform
|
||||
|
||||
services:
|
||||
appsmith:
|
||||
@ -13,3 +14,5 @@ services:
|
||||
- APPSMITH_SMART_LOOK_ID=
|
||||
volumes:
|
||||
- stacks-data:/appsmith-stacks
|
||||
healthcheck:
|
||||
test: ["NONE"]
|
||||
|
@ -1,6 +1,7 @@
|
||||
# documentation: https://appwrite.io/docs
|
||||
# slogan: Appwrite is a self-hosted backend-as-a-service platform that simplifies the development of web and mobile applications by providing a range of features and APIs.
|
||||
# env_file: appwrite.env
|
||||
# tags: backend-as-a-service, platform
|
||||
|
||||
|
||||
x-logging: &x-logging
|
||||
|
@ -1,5 +1,6 @@
|
||||
# documentation: https://docs.baby-buddy.net
|
||||
# slogan: Baby Buddy is an open-source web application that helps parents track their baby's daily activities, growth, and health with ease.
|
||||
# tags: baby, parents, health, growth, activities
|
||||
|
||||
services:
|
||||
babybuddy:
|
||||
|
@ -1,5 +1,6 @@
|
||||
# documentation: https://coder.com/docs/code-server/latest/guide
|
||||
# slogan: Code-Server is a self-hosted, web-based code editor that enables remote coding and collaboration from any device, anywhere.
|
||||
# tags: code, editor, remote, collaboration
|
||||
|
||||
services:
|
||||
code-server:
|
||||
|
@ -1,5 +1,6 @@
|
||||
# documentation: https://github.com/phntxx/dashboard/wiki/Installation#installation-using-docker
|
||||
# slogan: A dashboard. Inspired by SUI, it offers simple customization through JSON-files and a handy search bar to help you browse the internet more efficiently.
|
||||
# tags: dashboard, web, search, bookmarks
|
||||
|
||||
services:
|
||||
dashboard:
|
||||
|
@ -1,5 +1,6 @@
|
||||
# documentation: https://www.dokuwiki.org/faq
|
||||
# slogan: A lightweight and easy-to-use wiki platform for creating and managing documentation and knowledge bases with simplicity and flexibility.
|
||||
# tags: wiki, documentation, knowledge, base
|
||||
|
||||
services:
|
||||
dokuwiki:
|
||||
|
@ -1,5 +1,6 @@
|
||||
# documentation: https://emby.media/support/articles/Home.html
|
||||
# slogan: A media server software that allows you to organize, stream, and access your multimedia content effortlessly, making it easy to enjoy your favorite movies, TV shows, music, and more.
|
||||
# tags: media, server, movies, tv, music
|
||||
|
||||
services:
|
||||
emby:
|
||||
|
@ -1,5 +1,6 @@
|
||||
# documentation: https://github.com/mregni/EmbyStat/wiki/docker
|
||||
# slogan: EmyStat is an open-source, self-hosted web analytics tool, designed to provide insight into website traffic and user behavior, of your local Emby deployement, all within your control.
|
||||
# tags: media, server, movies, tv, music
|
||||
|
||||
services:
|
||||
embystat:
|
||||
|
@ -1,5 +1,6 @@
|
||||
# documentation: https://fider.io/doc
|
||||
# slogan: Fider is an open-source feedback platform for collecting and managing user feedback, helping you prioritize improvements to your products and services.
|
||||
# tags: feedback, user-feedback
|
||||
|
||||
services:
|
||||
fider:
|
||||
|
@ -1,5 +1,6 @@
|
||||
# documentation: https://ghost.org/docs
|
||||
# slogan: Ghost is a popular open-source content management system (CMS) and blogging platform, known for its simplicity and focus on content creation.
|
||||
# tags: cms, blog, content, management, system
|
||||
|
||||
services:
|
||||
ghost:
|
||||
|
40
templates/compose/grafana-with-postgresql.yaml
Normal file
40
templates/compose/grafana-with-postgresql.yaml
Normal file
@ -0,0 +1,40 @@
|
||||
# documentation: https://grafana.com/docs/grafana/latest/installation/docker/
|
||||
# slogan: Grafana is the open source analytics & monitoring solution for every database.
|
||||
# tags: grafana,analytics,monitoring,dashboard
|
||||
|
||||
services:
|
||||
grafana:
|
||||
image: grafana/grafana-oss
|
||||
environment:
|
||||
- SERVICE_FQDN_GRAFANA
|
||||
- GF_SERVER_ROOT_URL=${SERVICE_FQDN_GRAFANA}
|
||||
- GF_SERVER_DOMAIN=${SERVICE_FQDN_GRAFANA}
|
||||
- GF_SECURITY_ADMIN_PASSWORD=${SERVICE_PASSWORD_GRAFANA}
|
||||
- GF_DATABASE_TYPE=postgres
|
||||
- GF_DATABASE_HOST=postgresql
|
||||
- GF_DATABASE_USER=$SERVICE_USER_POSTGRES
|
||||
- GF_DATABASE_PASSWORD=$SERVICE_PASSWORD_POSTGRES
|
||||
- GF_DATABASE_NAME=${POSTGRES_DB:-grafana}
|
||||
volumes:
|
||||
- grafana-data:/var/lib/grafana
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
depends_on:
|
||||
- postgresql
|
||||
postgresql:
|
||||
image: postgres:15-alpine
|
||||
volumes:
|
||||
- postgresql-data:/var/lib/postgresql/data
|
||||
environment:
|
||||
- POSTGRES_USER=$SERVICE_USER_POSTGRES
|
||||
- POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
|
||||
- POSTGRES_DB=${POSTGRES_DB:-grafana}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
19
templates/compose/grafana.yaml
Normal file
19
templates/compose/grafana.yaml
Normal file
@ -0,0 +1,19 @@
|
||||
# documentation: https://grafana.com/docs/grafana/latest/installation/docker/
|
||||
# slogan: Grafana is the open source analytics & monitoring solution for every database.
|
||||
# tags: grafana,analytics,monitoring,dashboard
|
||||
|
||||
services:
|
||||
grafana:
|
||||
image: grafana/grafana-oss
|
||||
environment:
|
||||
- SERVICE_FQDN_GRAFANA
|
||||
- GF_SERVER_ROOT_URL=${SERVICE_FQDN_GRAFANA}
|
||||
- GF_SERVER_DOMAIN=${SERVICE_FQDN_GRAFANA}
|
||||
- GF_SECURITY_ADMIN_PASSWORD=${SERVICE_PASSWORD_GRAFANA}
|
||||
volumes:
|
||||
- grafana-data:/var/lib/grafana
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
@ -1,5 +1,6 @@
|
||||
# documentation: https://github.com/grocy/grocy
|
||||
# slogan: Grocy is a self-hosted, web-based household management and grocery list application, designed to simplify your household chores and grocery shopping.
|
||||
# tags: groceries, household, management, grocery, shopping
|
||||
|
||||
services:
|
||||
grocy:
|
||||
|
@ -1,5 +1,6 @@
|
||||
# documentation: https://github.com/linuxserver/Heimdall
|
||||
# slogan: Heimdall is a self-hosted dashboard for managing and organizing your server applications, providing a centralized and efficient interface.
|
||||
# tags: dashboard, server, applications, interface
|
||||
|
||||
services:
|
||||
heimdall:
|
||||
|
@ -1,5 +1,6 @@
|
||||
# documentation: https://github.com/alexta69/metube
|
||||
# slogan: A web GUI for youtube-dl with playlist support. It enables you to effortlessly download videos from YouTube and dozens of other sites.
|
||||
# tags: youtube, download, videos, playlist
|
||||
|
||||
services:
|
||||
metube:
|
||||
|
@ -1,5 +1,6 @@
|
||||
# documentation: https://docs.min.io/docs/minio-docker-quickstart-guide.html
|
||||
# slogan: MinIO is a high performance object storage server compatible with Amazon S3 APIs.
|
||||
# tags: object, storage, server, s3, api
|
||||
|
||||
services:
|
||||
minio:
|
||||
|
38
templates/compose/n8n-with-postgresql.yaml
Normal file
38
templates/compose/n8n-with-postgresql.yaml
Normal file
@ -0,0 +1,38 @@
|
||||
# documentation: https://docs.n8n.io/hosting/
|
||||
# slogan: n8n is an extendable workflow automation tool which enables you to connect anything to everything via its open, fair-code model.
|
||||
# tags: n8n,workflow,automation,open,source,low,code
|
||||
|
||||
services:
|
||||
n8n:
|
||||
image: docker.n8n.io/n8nio/n8n
|
||||
environment:
|
||||
- SERVICE_FQDN_N8N
|
||||
- N8N_EDITOR_BASE_URL=${SERVICE_FQDN_N8N}
|
||||
- N8N_HOST=${SERVICE_FQDN_N8N}
|
||||
- GENERIC_TIMEZONE="Europe/Berlin"
|
||||
- TZ="Europe/Berlin"
|
||||
- DB_TYPE=postgresdb
|
||||
- DB_POSTGRESDB_DATABASE=${POSTGRES_DB:-umami}
|
||||
- DB_POSTGRESDB_HOST=postgresql
|
||||
- DB_POSTGRESDB_PORT=5432
|
||||
- DB_POSTGRESDB_USER=$SERVICE_USER_POSTGRES
|
||||
- DB_POSTGRESDB_SCHEMA=public
|
||||
- DB_POSTGRESDB_PASSWORD=$SERVICE_PASSWORD_POSTGRES
|
||||
volumes:
|
||||
- n8n-data:/home/node/.n8n
|
||||
depends_on:
|
||||
- postgresql
|
||||
postgresql:
|
||||
image: postgres:15-alpine
|
||||
volumes:
|
||||
- postgresql-data:/var/lib/postgresql/data
|
||||
environment:
|
||||
- POSTGRES_USER=$SERVICE_USER_POSTGRES
|
||||
- POSTGRES_PASSWORD=$SERVICE_PASSWORD_POSTGRES
|
||||
- POSTGRES_DB=${POSTGRES_DB:-umami}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
15
templates/compose/n8n.yaml
Normal file
15
templates/compose/n8n.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
# documentation: https://docs.n8n.io/hosting/
|
||||
# slogan: n8n is an extendable workflow automation tool which enables you to connect anything to everything via its open, fair-code model.
|
||||
# tags: n8n,workflow,automation,open,source,low,code
|
||||
|
||||
services:
|
||||
n8n:
|
||||
image: docker.n8n.io/n8nio/n8n
|
||||
environment:
|
||||
- SERVICE_FQDN_N8N
|
||||
- N8N_EDITOR_BASE_URL=${SERVICE_FQDN_N8N}
|
||||
- N8N_HOST=${SERVICE_FQDN_N8N}
|
||||
- GENERIC_TIMEZONE="Europe/Berlin"
|
||||
- TZ="Europe/Berlin"
|
||||
volumes:
|
||||
- n8n-data:/home/node/.n8n
|
27
templates/compose/openblocks.yaml
Normal file
27
templates/compose/openblocks.yaml
Normal file
@ -0,0 +1,27 @@
|
||||
# documentation: https://docs.openblocks.dev/self-hosting
|
||||
# slogan: OpenBlocks is a self-hosted, open-source, low-code platform for building internal tools.
|
||||
# tags: openblocks,low,code,platform,open,source,low,code
|
||||
|
||||
services:
|
||||
openblocks:
|
||||
image: openblocksdev/openblocks-ce
|
||||
environment:
|
||||
- SERVICE_FQDN_OPENBLOCKS
|
||||
- REDIS_ENABLED=true
|
||||
- MONGODB_ENABLED=true
|
||||
- API_SERVICE_ENABLED=true
|
||||
- NODE_SERVICE_ENABLED=true
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- MONGODB_URI=mongodb://localhost:27017/openblocks?authSource=admin
|
||||
- REDIS_URL=redis://localhost:6379
|
||||
- JS_EXECUTOR_URI=http://localhost:6060
|
||||
- ENABLE_USER_SIGN_UP=${ENABLE_USER_SIGN_UP:-true}
|
||||
- ENCRYPTION_PASSWORD=$SERVICE_
|
||||
volumes:
|
||||
- openblocks-data:/openblocks-stacks
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
@ -1,5 +1,6 @@
|
||||
# documentation: https://github.com/schlagmichdoch/PairDrop/blob/master/docs/faq.md
|
||||
# documentation: https://github.com/schlagmichdoch/PairDrop
|
||||
# slogan: Pairdrop is a self-hosted file sharing and collaboration platform, offering secure file sharing and collaboration capabilities for efficient teamwork.
|
||||
# tags: file, sharing, collaboration, teamwork
|
||||
|
||||
services:
|
||||
pairdrop:
|
||||
|
@ -1,6 +1,7 @@
|
||||
# ignore: true
|
||||
# documentation: https://plausible.io/docs/self-hosting
|
||||
# slogan: "Plausible Analytics is a simple, open-source, lightweight (< 1 KB) and privacy-friendly web analytics alternative to Google Analytics."
|
||||
# tags: analytics, privacy, google, alternative
|
||||
|
||||
version: "3.3"
|
||||
services:
|
||||
|
@ -1,5 +1,6 @@
|
||||
# documentation: https://github.com/RobinLinus/snapdrop/blob/master/docs/faq.md
|
||||
# documentation: https://github.com/RobinLinus/snapdrop
|
||||
# slogan: A self-hosted file-sharing service for secure and convenient file transfers, whether on a local network or the internet.
|
||||
# tags: file, sharing, transfer, local, network, internet
|
||||
|
||||
services:
|
||||
snapdrop:
|
||||
|
@ -1,5 +1,6 @@
|
||||
# documentation: https://umami.is/docs/getting-started
|
||||
# slogan: Umami is a lightweight, self-hosted web analytics platform designed to provide website owners with insights into visitor behavior without compromising user privacy.
|
||||
# tags: analytics, insights, privacy
|
||||
|
||||
services:
|
||||
umami:
|
||||
|
@ -1,5 +1,6 @@
|
||||
# documentation: https://github.com/louislam/uptime-kuma/wiki
|
||||
# slogan: Uptime Kuma is a free, self-hosted monitoring tool for tracking the status and performance of your web services and applications in real-time.
|
||||
# tags: monitoring, status, performance, web, services, applications, real-time
|
||||
|
||||
services:
|
||||
uptime-kuma:
|
||||
|
@ -1,5 +1,6 @@
|
||||
# documentation: https://wordpress.org/documentation/
|
||||
# slogan: "WordPress is open source software you can use to create a beautiful website, blog, or app."
|
||||
# slogan: WordPress with MariaDB. Wordpress is open source software you can use to create a beautiful website, blog, or app.
|
||||
# tags: cms, blog, content, management, mariadb
|
||||
|
||||
services:
|
||||
wordpress:
|
||||
|
@ -1,5 +1,6 @@
|
||||
# documentation: https://wordpress.org/documentation/
|
||||
# slogan: "WordPress is open source software you can use to create a beautiful website, blog, or app."
|
||||
# slogan: WordPress with MySQL. Wordpress is open source software you can use to create a beautiful website, blog, or app.
|
||||
# tags: cms, blog, content, management, mysql
|
||||
|
||||
services:
|
||||
wordpress:
|
||||
|
@ -1,5 +1,6 @@
|
||||
# documentation: https://wordpress.org/documentation/
|
||||
# slogan: "WordPress is open source software you can use to create a beautiful website, blog, or app."
|
||||
# slogan: WordPress with external database. Wordpress is open source software you can use to create a beautiful website, blog, or app.
|
||||
# tags: cms, blog, content, management
|
||||
|
||||
services:
|
||||
wordpress:
|
||||
|
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user