Compare commits

...

43 Commits

Author SHA1 Message Date
Andras Bacsai
2d0838b112 Merge pull request #1742 from coollabsio/next
v4.0.0-beta.215
2024-02-14 10:32:48 +01:00
Andras Bacsai
07b94a8e48 Update filebrowser.yaml and service-templates.json 2024-02-14 10:31:05 +01:00
Andras Bacsai
4b08abc144 Save storage on initial creation 2024-02-14 10:21:53 +01:00
Andras Bacsai
93e4fc2f32 Update filebrowser image and volume bindings 2024-02-14 10:14:14 +01:00
Andras Bacsai
6dd86eec30 Fix directory creation issue in LocalFileVolume.php and parseDockerComposeFile() 2024-02-14 10:13:49 +01:00
Andras Bacsai
a7ab5d55d3 Add syncthing data volumes and update syncthing service template 2024-02-14 10:00:27 +01:00
Andras Bacsai
689547463c Merge pull request #1712 from RayBB/syncthing-template
add Syncthing template
2024-02-14 09:50:50 +01:00
Andras Bacsai
8a0046c571 update packages + fix tests 2024-02-14 09:21:25 +01:00
Andras Bacsai
fb3991321a Update Coolify version and Sentry configuration 2024-02-14 08:53:13 +01:00
Andras Bacsai
ca6543a919 Merge pull request #1741 from coollabsio/next
v4.0.0-beta.214
2024-02-14 08:44:42 +01:00
Andras Bacsai
364a6aa3a2 fix: boolean docker options 2024-02-14 08:42:47 +01:00
Andras Bacsai
82b0667277 Update version numbers 2024-02-12 12:56:04 +01:00
Andras Bacsai
74c126c731 Merge pull request #1729 from coollabsio/next
v4.0.0-beta.213
2024-02-12 11:56:40 +01:00
Andras Bacsai
d87a0fe74f Refactor authentication check in Index.php 2024-02-12 11:53:28 +01:00
Andras Bacsai
9a858f628d Merge pull request #1732 from fipnooone/fix/previews-flex-wrap
Flex-wrap deployment previews
2024-02-12 11:50:41 +01:00
Andras Bacsai
fed01fa9d2 Fix subscription retrieval and handle missing subscriptions 2024-02-12 11:48:28 +01:00
Andras Bacsai
e1468da36a feat: add proxy start to server validation
fix: boarding flow updated
2024-02-12 11:46:36 +01:00
Andras Bacsai
ddfc1440cd fix: menu 2024-02-12 10:05:45 +01:00
Andras Bacsai
5fc46384e6 Refactor status component to exclude parentheses in status message 2024-02-11 18:08:36 +01:00
Andras Bacsai
48d9df1e43 Add conditional display of deployment server name in previews.blade.php 2024-02-11 17:29:14 +01:00
Andras Bacsai
6b62d91f82 Update service_name parameter to stack_service_uuid in index.blade.php 2024-02-11 17:24:20 +01:00
Andras Bacsai
059748ad3b fix: get service stack as uuid, not name 2024-02-11 15:44:02 +01:00
Andras Bacsai
a334f998a2 Add Bitnami Docker images for MariaDB, MongoDB, MySQL, PostgreSQL, and Redis 2024-02-11 15:40:02 +01:00
Andras Bacsai
53a5ccef31 fix: add docker compose check during server validation 2024-02-11 15:32:58 +01:00
Andras Bacsai
9eea73cefb Update Docker command in InstallLogDrain.php 2024-02-11 14:35:07 +01:00
Andras Bacsai
b210e1f243 fix: lock logdrain configuration when one of them are enabled 2024-02-11 14:31:21 +01:00
fipnooone
ef3202101c fix: flex wrap deployment previews 2024-02-10 13:40:35 +07:00
Andras Bacsai
ad54358de7 Refactor resource index.blade.php file 2024-02-09 13:57:37 +01:00
Andras Bacsai
3689b58b92 Add success message for cleanup_queue 2024-02-09 13:51:31 +01:00
Andras Bacsai
5a4180a750 Update navbar dropdown menu styling 2024-02-09 13:50:19 +01:00
Andras Bacsai
047922b13a fix: new menu ui 2024-02-09 13:48:40 +01:00
Andras Bacsai
798d747164 add Docker run command parse test 2024-02-09 13:38:17 +01:00
Andras Bacsai
29676ffb22 Update Teams link in navbar.blade.php 2024-02-09 08:42:39 +01:00
Andras Bacsai
8a50b063d4 fix: user proper image_tag, if set 2024-02-08 15:22:07 +01:00
Andras Bacsai
3d2444ab2e Update version 4 to 4.0.0-beta.213 2024-02-08 14:54:30 +01:00
Andras Bacsai
7c395edab4 Fix conditional statement in navbar.blade.php 2024-02-08 14:06:43 +01:00
Andras Bacsai
7e7f322e21 Refactor admin authentication and routing***
***Add redirect for non-cloud users and instance admins without admin token.***

***Always include admin route, regardless of cloud status.
2024-02-08 14:01:16 +01:00
Andras Bacsai
9350fb4b97 Fix access control in Admin Index and hide Admin link in navbar 2024-02-08 13:54:16 +01:00
Andras Bacsai
59c3cc6ce1 Refactor admin authentication logic in Index component 2024-02-08 13:46:43 +01:00
Andras Bacsai
d7001937ac Fix access control in Admin Index and Navbar components 2024-02-08 13:40:26 +01:00
Andras Bacsai
3fe58ec66b Fix isInstanceAdmin function call in Index.php 2024-02-08 13:33:51 +01:00
Andras Bacsai
ed12f73483 Update admin authentication and version numbers 2024-02-08 13:33:34 +01:00
RayBB
f357f40fc7 add syncthing template 2024-02-05 16:45:46 +01:00
50 changed files with 1048 additions and 856 deletions

View File

@@ -198,7 +198,7 @@ Files:
}
$restart_command = [
"echo 'Stopping old Fluent Bit'",
"cd $config_path && docker rm -f coolify-log-drain || true",
"cd $config_path && docker compose down --remove-orphans || true",
"echo 'Starting Fluent Bit'",
"cd $config_path && docker compose up -d --remove-orphans",
];

View File

@@ -25,7 +25,6 @@ class UpdateCoolify
CleanupDocker::run($this->server, false);
$this->latestVersion = get_latest_version_of_coolify();
$this->currentVersion = config('version');
ray('latest version:' . $this->latestVersion . " current version: " . $this->currentVersion . ' force: ' . $force);
if ($settings->next_channel) {
ray('next channel enabled');
$this->latestVersion = 'next';
@@ -44,7 +43,7 @@ class UpdateCoolify
}
$this->update();
}
send_internal_notification('InstanceAutoUpdateJob done to version: ' . $this->latestVersion . ' from version: ' . $this->currentVersion);
send_internal_notification("Instance updated from {$this->currentVersion} -> {$this->latestVersion}");
} catch (\Throwable $e) {
ray('InstanceAutoUpdateJob failed');
ray($e->getMessage());

View File

@@ -618,6 +618,9 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
}
} else {
$this->dockerImageTag = str($this->commit)->substr(0, 128);
if ($this->application->docker_registry_image_tag) {
$this->dockerImageTag = $this->application->docker_registry_image_tag;
}
if ($this->application->docker_registry_image_name) {
$this->build_image_name = Str::lower("{$this->application->docker_registry_image_name}:{$this->dockerImageTag}-build");
$this->production_image_name = Str::lower("{$this->application->docker_registry_image_name}:{$this->dockerImageTag}");

View File

@@ -11,6 +11,9 @@ class Index extends Component
public $users = [];
public function mount()
{
if (!isCloud()) {
return redirect()->route('dashboard');
}
if (auth()->user()->id !== 0 && session('adminToken') === null) {
return redirect()->route('dashboard');
}

View File

@@ -3,6 +3,7 @@
namespace App\Livewire\Boarding;
use App\Actions\Server\InstallDocker;
use App\Enums\ProxyTypes;
use App\Models\PrivateKey;
use App\Models\Project;
use App\Models\Server;
@@ -121,15 +122,16 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
}
$this->selectedExistingPrivateKey = $this->createdServer->privateKey->id;
$this->serverPublicKey = $this->createdServer->privateKey->publicKey();
$this->validateServer();
$this->installServer();
}
public function getProxyType()
{
$proxyTypeSet = $this->createdServer->proxy->type;
if (!$proxyTypeSet) {
$this->currentState = 'select-proxy';
return;
}
$this->selectProxy(ProxyTypes::TRAEFIK_V2->value);
// $proxyTypeSet = $this->createdServer->proxy->type;
// if (!$proxyTypeSet) {
// $this->currentState = 'select-proxy';
// return;
// }
$this->getProjects();
}
public function selectExistingPrivateKey()
@@ -193,7 +195,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
$this->createdServer->settings->is_cloudflare_tunnel = $this->isCloudflareTunnel;
$this->createdServer->settings->save();
$this->createdServer->addInitialNetwork();
$this->validateServer();
$this->currentState = 'validate-server';
}
public function installServer()
{
@@ -219,7 +221,7 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
$dockerVersion = instant_remote_process(["docker version|head -2|grep -i version| awk '{print $2}'"], $this->createdServer, true);
$dockerVersion = checkMinimumDockerEngineVersion($dockerVersion);
if (is_null($dockerVersion)) {
$this->currentState = 'install-docker';
$this->currentState = 'validate-server';
throw new \Exception('Docker not found or old version is installed.');
}
$this->createdServer->settings()->update([
@@ -227,27 +229,10 @@ uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
]);
$this->getProxyType();
} catch (\Throwable $e) {
// $this->dockerInstallationStarted = false;
return handleError(error: $e, livewire: $this);
}
}
public function installDocker()
{
try {
$this->dockerInstallationStarted = true;
$activity = InstallDocker::run($this->createdServer);
$this->dispatch('installDocker');
$this->dispatch('activityMonitor', $activity->id);
} catch (\Throwable $e) {
$this->dockerInstallationStarted = false;
return handleError(error: $e, livewire: $this);
}
}
public function dockerInstalledOrSkipped()
{
$this->validateServer();
}
public function selectProxy(string|null $proxyType = null)
public function selectProxy(?string $proxyType = null)
{
if (!$proxyType) {
return $this->getProjects();

View File

@@ -22,6 +22,7 @@ class Dashboard extends Component
}
public function cleanup_queue()
{
$this->dispatch('success', 'Cleanup started.');
Artisan::queue('app:init', [
'--cleanup-deployments' => 'true'
]);

View File

@@ -56,15 +56,15 @@ class Heading extends Component
return;
}
if ($this->application->destination->server->isSwarm() && str($this->application->docker_registry_image_name)->isEmpty()) {
$this->dispatch('error', 'Failed to deploy', 'To deploy to a Swarm cluster you must set a Docker image name first.');
$this->dispatch('error', 'Failed to deploy.', 'To deploy to a Swarm cluster you must set a Docker image name first.');
return;
}
if (data_get($this->application, 'settings.is_build_server_enabled') && str($this->application->docker_registry_image_name)->isEmpty()) {
$this->dispatch('error', 'Failed to deploy', 'To use a build server, you must first set a Docker image.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/build-server">documentation</a>');
$this->dispatch('error', 'Failed to deploy.', 'To use a build server, you must first set a Docker image.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/build-server">documentation</a>');
return;
}
if ($this->application->additional_servers->count() > 0 && str($this->application->docker_registry_image_name)->isEmpty()) {
$this->dispatch('error', 'Failed to deploy', 'To deploy to more than one server, you must first set a Docker image.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/build-server">documentation</a>');
$this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/multiple-servers">documentation</a>');
return;
}
$this->setDeploymentUuid();
@@ -103,7 +103,7 @@ class Heading extends Component
public function restart()
{
if ($this->application->additional_servers->count() > 0 && str($this->application->docker_registry_image_name)->isEmpty()) {
$this->dispatch('error', 'Failed to deploy', 'To deploy to more than one server, you must first set a Docker image.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/build-server">documentation</a>');
$this->dispatch('error', 'Failed to deploy', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/multiple-servers">documentation</a>');
return;
}
$this->setDeploymentUuid();

View File

@@ -27,12 +27,12 @@ class Index extends Component
$this->parameters = get_route_parameters();
$this->query = request()->query();
$this->service = Service::whereUuid($this->parameters['service_uuid'])->firstOrFail();
$service = $this->service->applications()->whereName($this->parameters['service_name'])->first();
$service = $this->service->applications()->whereUuid($this->parameters['stack_service_uuid'])->first();
if ($service) {
$this->serviceApplication = $service;
$this->serviceApplication->getFilesFromServer();
} else {
$this->serviceDatabase = $this->service->databases()->whereName($this->parameters['service_name'])->first();
$this->serviceDatabase = $this->service->databases()->whereUuid($this->parameters['stack_service_uuid'])->first();
$this->serviceDatabase->getFilesFromServer();
}
$this->s3s = currentTeam()->s3s;

View File

@@ -37,10 +37,13 @@ class Destination extends Component
$this->networks = $this->networks->reject(function ($network) use ($all_networks) {
return $all_networks->pluck('id')->contains($network->id);
});
}
public function redeploy(int $network_id, int $server_id)
{
if ($this->resource->additional_servers->count() > 0 && str($this->resource->docker_registry_image_name)->isEmpty()) {
$this->dispatch('error', 'Failed to deploy.', 'Before deploying to multiple servers, you must first set a Docker image in the General tab.<br>More information here: <a target="_blank" class="underline" href="https://coolify.io/docs/server/multiple-servers">documentation</a>');
return;
}
$deployment_uuid = new Cuid2(7);
$server = Server::find($server_id);
$destination = StandaloneDocker::find($network_id);

View File

@@ -12,7 +12,7 @@ class Status extends Component
public Server $server;
public bool $polling = false;
public int $numberOfPolls = 0;
protected $listeners = ['proxyStatusUpdated', 'startProxyPolling'];
protected $listeners = ['proxyStatusUpdated' => '$refresh', 'startProxyPolling'];
public function startProxyPolling()
{

View File

@@ -2,6 +2,7 @@
namespace App\Livewire\Server;
use App\Actions\Proxy\StartProxy;
use App\Models\Server;
use Livewire\Component;
@@ -14,18 +15,23 @@ class ValidateAndInstall extends Component
public $uptime = null;
public $supported_os_type = null;
public $docker_installed = null;
public $docker_compose_installed = null;
public $docker_version = null;
public $proxy_started = false;
public $error = null;
protected $listeners = ['validateServer' => 'init', 'validateDockerEngine', 'validateServerNow' => 'validateServer'];
public function init(bool $install = true)
{
$this->install = $install;
$this->uptime = null;
$this->supported_os_type = null;
$this->docker_installed = null;
$this->docker_version = null;
$this->docker_compose_installed = null;
$this->proxy_started = null;
$this->error = null;
$this->number_of_tries = 0;
$this->dispatch('validateServerNow');
@@ -43,6 +49,11 @@ class ValidateAndInstall extends Component
if ($swarmInstalled) {
$this->dispatch('success', 'Docker Swarm is initiated.');
}
} else {
$proxy = StartProxy::run($this->server);
if ($proxy) {
$this->proxy_started = true;
}
}
} catch (\Throwable $e) {
return handleError($e, $this);
@@ -67,9 +78,9 @@ class ValidateAndInstall extends Component
public function validateDockerEngine()
{
$this->docker_installed = $this->server->validateDockerEngine();
if (!$this->docker_installed) {
$this->docker_compose_installed = $this->server->validateDockerCompose();
if (!$this->docker_installed || !$this->docker_compose_installed) {
if ($this->install) {
ray($this->number_of_tries, $this->max_tries);
if ($this->number_of_tries == $this->max_tries) {
$this->error = 'Docker Engine could not be installed. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
return;

View File

@@ -200,9 +200,6 @@ class Application extends BaseModel
set: fn ($value) => $value === "" ? null : $value,
);
}
// Normal Deployments
public function portsMappingsArray(): Attribute
{
return Attribute::make(
@@ -214,7 +211,7 @@ class Application extends BaseModel
}
public function realStatus()
{
return $this->getRawOriginal('status');
return $this->getRawOriginal('status');
}
public function status(): Attribute
{

View File

@@ -10,12 +10,19 @@ class LocalFileVolume extends BaseModel
use HasFactory;
protected $guarded = [];
protected static function booted()
{
static::created(function (LocalFileVolume $fileVolume) {
$fileVolume->saveStorageOnServer($fileVolume->service);
});
}
public function service()
{
return $this->morphTo('resource');
}
public function saveStorageOnServer(ServiceApplication|ServiceDatabase $service)
{
ray('saveStorageOnServer');
$workdir = $service->service->workdir();
$server = $service->service->server;
$commands = collect([
@@ -39,7 +46,7 @@ class LocalFileVolume extends BaseModel
if (!$fileVolume->is_directory && $isDir == 'NOK') {
$content = base64_encode($content);
$commands->push("echo '$content' | base64 -d > $path");
} else if ($isDir == 'NOK' && $fileVolume->is_directory) {
} else if ($isDir == 'NOK' && $fileVolume->is_directory) {
$commands->push("mkdir -p $path > /dev/null 2>&1 || true");
}
ray($commands->toArray());

View File

@@ -443,6 +443,21 @@ class Server extends BaseModel
$this->validateCoolifyNetwork(isSwarm: false, isBuildServer: $this->settings->is_build_server);
return true;
}
public function validateDockerCompose($throwError = false)
{
$dockerCompose = instant_remote_process(["docker compose version"], $this, false);
if (is_null($dockerCompose)) {
$this->settings->is_usable = false;
$this->settings->save();
if ($throwError) {
throw new \Exception('Server is not usable. Docker Compose is not installed.');
}
return false;
}
$this->settings->is_usable = true;
$this->settings->save();
return true;
}
public function validateDockerSwarm()
{
$swarmStatus = instant_remote_process(["docker info|grep -i swarm"], $this, false);

View File

@@ -13,6 +13,11 @@ const VALID_CRON_STRINGS = [
const RESTART_MODE = 'unless-stopped';
const DATABASE_DOCKER_IMAGES = [
'bitnami/mariadb',
'bitnami/mongodb',
'bitnami/mysql',
'bitnami/postgresql',
'bitnami/redis',
'mysql',
'mariadb',
'postgres',

View File

@@ -340,25 +340,20 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null
'--cap-drop' => 'cap_drop',
'--security-opt' => 'security_opt',
'--sysctl' => 'sysctls',
'--device' => 'devices',
'--ulimit' => 'ulimits',
'--device' => 'devices',
'--init' => 'init',
'--ulimit' => 'ulimits',
'--privileged' => 'privileged',
]);
foreach ($matches as $match) {
$option = $match[1];
$value = isset($match[2]) && $match[2] !== '' ? $match[2] : true;
if ($list_options->contains($option)) {
$value = explode(',', $value);
}
if (array_key_exists($option, $options)) {
if (is_array($options[$option])) {
$options[$option][] = $value;
} else {
$options[$option] = [$options[$option], $value];
}
if (isset($match[2]) && $match[2] !== '') {
$value = $match[2];
$options[$option][] = $value;
$options[$option] = array_unique($options[$option]);
} else {
$value = true;
$options[$option] = $value;
}
}
@@ -370,7 +365,7 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null
}
if ($option === '--ulimit') {
$ulimits = collect([]);
collect($value)->map(function ($ulimit) use ($ulimits){
collect($value)->map(function ($ulimit) use ($ulimits) {
$ulimit = explode('=', $ulimit);
$type = $ulimit[0];
$limits = explode(':', $ulimit[1]);
@@ -381,7 +376,6 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null
'soft' => $soft_limit,
'hard' => $hard_limit
]);
} else {
$soft_limit = $ulimit[1];
$ulimits->put($type, [

View File

@@ -782,14 +782,14 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
$source = data_get_str($volume, 'source');
$target = data_get_str($volume, 'target');
$content = data_get($volume, 'content');
$isDirectory = (bool) data_get($volume, 'isDirectory', false);
$isDirectory = (bool) data_get($volume, 'isDirectory', false) || (bool) data_get($volume, 'is_directory', false);
$foundConfig = $savedService->fileStorages()->whereMountPath($target)->first();
if ($foundConfig) {
$contentNotNull = data_get($foundConfig, 'content');
if ($contentNotNull) {
$content = $contentNotNull;
}
$isDirectory = (bool) data_get($foundConfig, 'is_directory');
$isDirectory = (bool) data_get($volume, 'isDirectory', false) || (bool) data_get($volume, 'is_directory', false);
}
}
if ($type->value() === 'bind') {
@@ -1054,6 +1054,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
data_set($service, 'container_name', $containerName);
data_forget($service, 'volumes.*.content');
data_forget($service, 'volumes.*.isDirectory');
data_forget($service, 'volumes.*.is_directory');
// Remove unnecessary variables from service.environment
// $withoutServiceEnvs = collect([]);
// collect(data_get($service, 'environment'))->each(function ($value, $key) use ($withoutServiceEnvs) {

803
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ return [
// 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.212',
'release' => '4.0.0-beta.215',
// When left empty or `null` the Laravel environment will be used
'environment' => config('app.env'),

View File

@@ -1,3 +1,3 @@
<?php
return '4.0.0-beta.212';
return '4.0.0-beta.215';

176
package-lock.json generated
View File

@@ -7,7 +7,7 @@
"dependencies": {
"@tailwindcss/typography": "0.5.10",
"alpinejs": "3.13.5",
"daisyui": "4.4.19",
"daisyui": "4.7.2",
"ioredis": "5.3.2",
"tailwindcss-scrollbar": "0.1.0"
},
@@ -17,11 +17,11 @@
"axios": "1.6.7",
"laravel-echo": "1.15.3",
"laravel-vite-plugin": "0.8.1",
"postcss": "8.4.33",
"postcss": "8.4.35",
"pusher-js": "8.4.0-rc2",
"tailwindcss": "3.4.1",
"vite": "4.5.2",
"vue": "3.4.15"
"vue": "3.4.19"
}
},
"node_modules/@alloc/quick-lru": {
@@ -524,77 +524,77 @@
}
},
"node_modules/@vue/compiler-core": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.15.tgz",
"integrity": "sha512-XcJQVOaxTKCnth1vCxEChteGuwG6wqnUHxAm1DO3gCz0+uXKaJNx8/digSz4dLALCy8n2lKq24jSUs8segoqIw==",
"version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.19.tgz",
"integrity": "sha512-gj81785z0JNzRcU0Mq98E56e4ltO1yf8k5PQ+tV/7YHnbZkrM0fyFyuttnN8ngJZjbpofWE/m4qjKBiLl8Ju4w==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.23.6",
"@vue/shared": "3.4.15",
"@babel/parser": "^7.23.9",
"@vue/shared": "3.4.19",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.0.2"
}
},
"node_modules/@vue/compiler-core/node_modules/@vue/shared": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz",
"integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g==",
"version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==",
"dev": true
},
"node_modules/@vue/compiler-dom": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.15.tgz",
"integrity": "sha512-wox0aasVV74zoXyblarOM3AZQz/Z+OunYcIHe1OsGclCHt8RsRm04DObjefaI82u6XDzv+qGWZ24tIsRAIi5MQ==",
"version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.19.tgz",
"integrity": "sha512-vm6+cogWrshjqEHTzIDCp72DKtea8Ry/QVpQRYoyTIg9k7QZDX6D8+HGURjtmatfgM8xgCFtJJaOlCaRYRK3QA==",
"dev": true,
"dependencies": {
"@vue/compiler-core": "3.4.15",
"@vue/shared": "3.4.15"
"@vue/compiler-core": "3.4.19",
"@vue/shared": "3.4.19"
}
},
"node_modules/@vue/compiler-dom/node_modules/@vue/shared": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz",
"integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g==",
"version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==",
"dev": true
},
"node_modules/@vue/compiler-sfc": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.15.tgz",
"integrity": "sha512-LCn5M6QpkpFsh3GQvs2mJUOAlBQcCco8D60Bcqmf3O3w5a+KWS5GvYbrrJBkgvL1BDnTp+e8q0lXCLgHhKguBA==",
"version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.19.tgz",
"integrity": "sha512-LQ3U4SN0DlvV0xhr1lUsgLCYlwQfUfetyPxkKYu7dkfvx7g3ojrGAkw0AERLOKYXuAGnqFsEuytkdcComei3Yg==",
"dev": true,
"dependencies": {
"@babel/parser": "^7.23.6",
"@vue/compiler-core": "3.4.15",
"@vue/compiler-dom": "3.4.15",
"@vue/compiler-ssr": "3.4.15",
"@vue/shared": "3.4.15",
"@babel/parser": "^7.23.9",
"@vue/compiler-core": "3.4.19",
"@vue/compiler-dom": "3.4.19",
"@vue/compiler-ssr": "3.4.19",
"@vue/shared": "3.4.19",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.5",
"magic-string": "^0.30.6",
"postcss": "^8.4.33",
"source-map-js": "^1.0.2"
}
},
"node_modules/@vue/compiler-sfc/node_modules/@vue/shared": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz",
"integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g==",
"version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==",
"dev": true
},
"node_modules/@vue/compiler-ssr": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.15.tgz",
"integrity": "sha512-1jdeQyiGznr8gjFDadVmOJqZiLNSsMa5ZgqavkPZ8O2wjHv0tVuAEsw5hTdUoUW4232vpBbL/wJhzVW/JwY1Uw==",
"version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.19.tgz",
"integrity": "sha512-P0PLKC4+u4OMJ8sinba/5Z/iDT84uMRRlrWzadgLA69opCpI1gG4N55qDSC+dedwq2fJtzmGald05LWR5TFfLw==",
"dev": true,
"dependencies": {
"@vue/compiler-dom": "3.4.15",
"@vue/shared": "3.4.15"
"@vue/compiler-dom": "3.4.19",
"@vue/shared": "3.4.19"
}
},
"node_modules/@vue/compiler-ssr/node_modules/@vue/shared": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz",
"integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g==",
"version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==",
"dev": true
},
"node_modules/@vue/reactivity": {
@@ -606,64 +606,64 @@
}
},
"node_modules/@vue/runtime-core": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.15.tgz",
"integrity": "sha512-6E3by5m6v1AkW0McCeAyhHTw+3y17YCOKG0U0HDKDscV4Hs0kgNT5G+GCHak16jKgcCDHpI9xe5NKb8sdLCLdw==",
"version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.19.tgz",
"integrity": "sha512-/Z3tFwOrerJB/oyutmJGoYbuoadphDcJAd5jOuJE86THNZji9pYjZroQ2NFsZkTxOq0GJbb+s2kxTYToDiyZzw==",
"dev": true,
"dependencies": {
"@vue/reactivity": "3.4.15",
"@vue/shared": "3.4.15"
"@vue/reactivity": "3.4.19",
"@vue/shared": "3.4.19"
}
},
"node_modules/@vue/runtime-core/node_modules/@vue/reactivity": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.15.tgz",
"integrity": "sha512-55yJh2bsff20K5O84MxSvXKPHHt17I2EomHznvFiJCAZpJTNW8IuLj1xZWMLELRhBK3kkFV/1ErZGHJfah7i7w==",
"version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.19.tgz",
"integrity": "sha512-+VcwrQvLZgEclGZRHx4O2XhyEEcKaBi50WbxdVItEezUf4fqRh838Ix6amWTdX0CNb/b6t3Gkz3eOebfcSt+UA==",
"dev": true,
"dependencies": {
"@vue/shared": "3.4.15"
"@vue/shared": "3.4.19"
}
},
"node_modules/@vue/runtime-core/node_modules/@vue/shared": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz",
"integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g==",
"version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==",
"dev": true
},
"node_modules/@vue/runtime-dom": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.15.tgz",
"integrity": "sha512-EVW8D6vfFVq3V/yDKNPBFkZKGMFSvZrUQmx196o/v2tHKdwWdiZjYUBS+0Ez3+ohRyF8Njwy/6FH5gYJ75liUw==",
"version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.19.tgz",
"integrity": "sha512-IyZzIDqfNCF0OyZOauL+F4yzjMPN2rPd8nhqPP2N1lBn3kYqJpPHHru+83Rkvo2lHz5mW+rEeIMEF9qY3PB94g==",
"dev": true,
"dependencies": {
"@vue/runtime-core": "3.4.15",
"@vue/shared": "3.4.15",
"@vue/runtime-core": "3.4.19",
"@vue/shared": "3.4.19",
"csstype": "^3.1.3"
}
},
"node_modules/@vue/runtime-dom/node_modules/@vue/shared": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz",
"integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g==",
"version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==",
"dev": true
},
"node_modules/@vue/server-renderer": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.15.tgz",
"integrity": "sha512-3HYzaidu9cHjrT+qGUuDhFYvF/j643bHC6uUN9BgM11DVy+pM6ATsG6uPBLnkwOgs7BpJABReLmpL3ZPAsUaqw==",
"version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.19.tgz",
"integrity": "sha512-eAj2p0c429RZyyhtMRnttjcSToch+kTWxFPHlzGMkR28ZbF1PDlTcmGmlDxccBuqNd9iOQ7xPRPAGgPVj+YpQw==",
"dev": true,
"dependencies": {
"@vue/compiler-ssr": "3.4.15",
"@vue/shared": "3.4.15"
"@vue/compiler-ssr": "3.4.19",
"@vue/shared": "3.4.19"
},
"peerDependencies": {
"vue": "3.4.15"
"vue": "3.4.19"
}
},
"node_modules/@vue/server-renderer/node_modules/@vue/shared": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz",
"integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g==",
"version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==",
"dev": true
},
"node_modules/@vue/shared": {
@@ -953,9 +953,9 @@
}
},
"node_modules/daisyui": {
"version": "4.4.19",
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.4.19.tgz",
"integrity": "sha512-IjOLWwnndD4N7Ut5CDxbUsaVtbqXPeVHM92IcgxGFxpuOd3CCKW/PAXZH6JoBTHFRaN57vB9XqEhdWm5yC+bPA==",
"version": "4.7.2",
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.7.2.tgz",
"integrity": "sha512-9UCss12Zmyk/22u+JbkVrHHxOzFOyY17HuqP5LeswI4hclbj6qbjJTovdj2zRy8cCH6/n6Wh0lTLjriGnyGh0g==",
"dependencies": {
"css-selector-tokenizer": "^0.8",
"culori": "^3",
@@ -1410,9 +1410,9 @@
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
},
"node_modules/magic-string": {
"version": "0.30.5",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz",
"integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==",
"version": "0.30.7",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz",
"integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==",
"dev": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15"
@@ -1598,9 +1598,9 @@
}
},
"node_modules/postcss": {
"version": "8.4.33",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz",
"integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==",
"version": "8.4.35",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
"integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==",
"funding": [
{
"type": "opencollective",
@@ -2106,16 +2106,16 @@
}
},
"node_modules/vue": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.15.tgz",
"integrity": "sha512-jC0GH4KkWLWJOEQjOpkqU1bQsBwf4R1rsFtw5GQJbjHVKWDzO6P0nWWBTmjp1xSemAioDFj1jdaK1qa3DnMQoQ==",
"version": "3.4.19",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.4.19.tgz",
"integrity": "sha512-W/7Fc9KUkajFU8dBeDluM4sRGc/aa4YJnOYck8dkjgZoXtVsn3OeTGni66FV1l3+nvPA7VBFYtPioaGKUmEADw==",
"dev": true,
"dependencies": {
"@vue/compiler-dom": "3.4.15",
"@vue/compiler-sfc": "3.4.15",
"@vue/runtime-dom": "3.4.15",
"@vue/server-renderer": "3.4.15",
"@vue/shared": "3.4.15"
"@vue/compiler-dom": "3.4.19",
"@vue/compiler-sfc": "3.4.19",
"@vue/runtime-dom": "3.4.19",
"@vue/server-renderer": "3.4.19",
"@vue/shared": "3.4.19"
},
"peerDependencies": {
"typescript": "*"
@@ -2127,9 +2127,9 @@
}
},
"node_modules/vue/node_modules/@vue/shared": {
"version": "3.4.15",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz",
"integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g==",
"version": "3.4.19",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.19.tgz",
"integrity": "sha512-/KliRRHMF6LoiThEy+4c1Z4KB/gbPrGjWwJR+crg2otgrf/egKzRaCPvJ51S5oetgsgXLfc4Rm5ZgrKHZrtMSw==",
"dev": true
},
"node_modules/wrappy": {

View File

@@ -11,16 +11,16 @@
"axios": "1.6.7",
"laravel-echo": "1.15.3",
"laravel-vite-plugin": "0.8.1",
"postcss": "8.4.33",
"postcss": "8.4.35",
"pusher-js": "8.4.0-rc2",
"tailwindcss": "3.4.1",
"vite": "4.5.2",
"vue": "3.4.15"
"vue": "3.4.19"
},
"dependencies": {
"@tailwindcss/typography": "0.5.10",
"alpinejs": "3.13.5",
"daisyui": "4.4.19",
"daisyui": "4.7.2",
"ioredis": "5.3.2",
"tailwindcss-scrollbar": "0.1.0"
}

View File

@@ -28,8 +28,8 @@
<a class="text-xs text-white rounded-none hover:no-underline hover:bg-coollabs hover:text-white"
target="_blank" href="{{ getFqdnWithoutPort($domain) }}">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
stroke-width="1.5" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M9 15l6 -6" />
<path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464" />
@@ -84,7 +84,7 @@
@endif
@if (data_get($application, 'ports_mappings_array'))
@foreach ($application->ports_mappings_array as $port)
@if (isDev())
@if ($application->destination->server->id === 0)
<li>
<a class="text-xs text-white rounded-none hover:no-underline hover:bg-coollabs hover:text-white"
target="_blank" href="http://localhost:{{ explode(':', $port)[0] }}">
@@ -114,9 +114,29 @@
<path
d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463" />
</svg>
Port {{ $port }}
{{ $application->destination->server->ip }}:{{ explode(':', $port)[0] }}
</a>
</li>
@if (count($application->additional_servers) > 0)
@foreach ($application->additional_servers as $server)
<li>
<a class="text-xs text-white rounded-none hover:no-underline hover:bg-coollabs hover:text-white"
target="_blank"
href="http://{{ $server->ip }}:{{ explode(':', $port)[0] }}">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M9 15l6 -6" />
<path d="M11 6l.463 -.536a5 5 0 0 1 7.071 7.072l-.534 .464" />
<path
d="M13 18l-.397 .534a5.068 5.068 0 0 1 -7.127 0a4.972 4.972 0 0 1 0 -7.071l.524 -.463" />
</svg>
{{ $server->ip }}:{{ explode(':', $port)[0] }}
</a>
</li>
@endforeach
@endif
@endif
@endforeach
@endif

View File

@@ -40,109 +40,133 @@
</svg>
</a>
</li>
<details x-data="{ open: false }" class="dropdown dropdown-right" x-bind:open="open">
<summary class="bg-transparent border-none btn hover:bg-transparent no-animation"
x-on:click.prevent="open = !open" x-on:click.away="open = false"> <svg class="icon"
viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor"
d="M224 128a8 8 0 0 1-8 8h-80v80a8 8 0 0 1-16 0v-80H40a8 8 0 0 1 0-16h80V40a8 8 0 0 1 16 0v80h80a8 8 0 0 1 8 8" />
</svg></summary>
<ul tabindex="0" class="w-64 p-2 border border-coolgray-200 dropdown-content menu bg-coolgray-100 ">
<li title="Tags" class="border-transparent hover:bg-coolgray-200 ">
<a class=" hover:bg-transparent hover:no-underline" href="{{ route('tags.index') }}">
<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="M3 8v4.172a2 2 0 0 0 .586 1.414l5.71 5.71a2.41 2.41 0 0 0 3.408 0l3.592-3.592a2.41 2.41 0 0 0 0-3.408l-5.71-5.71A2 2 0 0 0 9.172 6H5a2 2 0 0 0-2 2" />
<path d="m18 19l1.592-1.592a4.82 4.82 0 0 0 0-6.816L15 6m-8 4h-.01" />
</g>
</svg>
Tags
</a>
</li>
<li title="Command Center" class="hover:bg-coolgray-200">
<a class="hover:bg-transparent hover:no-underline" href="{{ route('command-center') }}">
<svg xmlns="http://www.w3.org/2000/svg"
class="{{ request()->is('command-center') ? ' icon' : 'icon' }}" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M5 7l5 5l-5 5" />
<path d="M12 19l7 0" />
</svg>
Command Center
</a>
</li>
<li title="Source" class="hover:bg-coolgray-200">
<a class="hover:bg-transparent hover:no-underline" href="{{ route('source.all') }}">
<svg class="icon" viewBox="0 0 15 15" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor"
d="m6.793 1.207l.353.354l-.353-.354ZM1.207 6.793l-.353-.354l.353.354Zm0 1.414l.354-.353l-.354.353Zm5.586 5.586l-.354.353l.354-.353Zm1.414 0l-.353-.354l.353.354Zm5.586-5.586l.353.354l-.353-.354Zm0-1.414l-.354.353l.354-.353ZM8.207 1.207l.354-.353l-.354.353ZM6.44.854L.854 6.439l.707.707l5.585-5.585L6.44.854ZM.854 8.56l5.585 5.585l.707-.707l-5.585-5.585l-.707.707Zm7.707 5.585l5.585-5.585l-.707-.707l-5.585 5.585l.707.707Zm5.585-7.707L8.561.854l-.707.707l5.585 5.585l.707-.707Zm0 2.122a1.5 1.5 0 0 0 0-2.122l-.707.707a.5.5 0 0 1 0 .708l.707.707ZM6.44 14.146a1.5 1.5 0 0 0 2.122 0l-.707-.707a.5.5 0 0 1-.708 0l-.707.707ZM.854 6.44a1.5 1.5 0 0 0 0 2.122l.707-.707a.5.5 0 0 1 0-.708L.854 6.44Zm6.292-4.878a.5.5 0 0 1 .708 0L8.56.854a1.5 1.5 0 0 0-2.122 0l.707.707Zm-2 1.293l1 1l.708-.708l-1-1l-.708.708ZM7.5 5a.5.5 0 0 1-.5-.5H6A1.5 1.5 0 0 0 7.5 6V5Zm.5-.5a.5.5 0 0 1-.5.5v1A1.5 1.5 0 0 0 9 4.5H8ZM7.5 4a.5.5 0 0 1 .5.5h1A1.5 1.5 0 0 0 7.5 3v1Zm0-1A1.5 1.5 0 0 0 6 4.5h1a.5.5 0 0 1 .5-.5V3Zm.646 2.854l1.5 1.5l.707-.708l-1.5-1.5l-.707.708ZM10.5 8a.5.5 0 0 1-.5-.5H9A1.5 1.5 0 0 0 10.5 9V8Zm.5-.5a.5.5 0 0 1-.5.5v1A1.5 1.5 0 0 0 12 7.5h-1Zm-.5-.5a.5.5 0 0 1 .5.5h1A1.5 1.5 0 0 0 10.5 6v1Zm0-1A1.5 1.5 0 0 0 9 7.5h1a.5.5 0 0 1 .5-.5V6ZM7 5.5v4h1v-4H7Zm.5 5.5a.5.5 0 0 1-.5-.5H6A1.5 1.5 0 0 0 7.5 12v-1Zm.5-.5a.5.5 0 0 1-.5.5v1A1.5 1.5 0 0 0 9 10.5H8Zm-.5-.5a.5.5 0 0 1 .5.5h1A1.5 1.5 0 0 0 7.5 9v1Zm0-1A1.5 1.5 0 0 0 6 10.5h1a.5.5 0 0 1 .5-.5V9Z" />
</svg>
Sources
</a>
</li>
<li title="Security" class="hover:bg-coolgray-200">
<a class="hover:bg-transparent hover:no-underline" href="{{ route('security.private-key.index') }}">
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2"
d="m16.555 3.843l3.602 3.602a2.877 2.877 0 0 1 0 4.069l-2.643 2.643a2.877 2.877 0 0 1-4.069 0l-.301-.301l-6.558 6.558a2 2 0 0 1-1.239.578L5.172 21H4a1 1 0 0 1-.993-.883L3 20v-1.172a2 2 0 0 1 .467-1.284l.119-.13L4 17h2v-2h2v-2l2.144-2.144l-.301-.301a2.877 2.877 0 0 1 0-4.069l2.643-2.643a2.877 2.877 0 0 1 4.069 0zM15 9h.01" />
</svg>
Security
</a>
</li>
<li title="Profile" class="hover:bg-coolgray-200">
<a class="hover:bg-transparent hover:no-underline" href="{{ route('profile') }}">
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" />
<path d="M12 10m-3 0a3 3 0 1 0 6 0a3 3 0 1 0 -6 0" />
<path d="M6.168 18.849a4 4 0 0 1 3.832 -2.849h4a4 4 0 0 1 3.834 2.855" />
</svg>
Profile
</a>
</li>
<li title="Teams" class="hover:bg-coolgray-200">
<a class="hover:bg-transparent hover:no-underline" href="{{ route('team.index') }}">
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M10 13a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" />
<path d="M8 21v-1a2 2 0 0 1 2 -2h4a2 2 0 0 1 2 2v1" />
<path d="M15 5a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" />
<path d="M17 10h2a2 2 0 0 1 2 2v1" />
<path d="M5 5a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" />
<path d="M3 13v-1a2 2 0 0 1 2 -2h2" />
</svg>
Teams
</a>
</li>
@if (isInstanceAdmin())
<li title="Settings" class="hover:bg-coolgray-200">
<a class="hover:bg-transparent hover:no-underline" href="/settings">
<div class="inline-block text-left " x-data="{ open: false }">
<div>
<button x-on:click.prevent="open = !open" x-on:click.away="open = false" type="button"
class="py-4 mx-4" id="menu-button" aria-expanded="true" aria-haspopup="true">
<svg class="icon" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor"
d="M224 128a8 8 0 0 1-8 8h-80v80a8 8 0 0 1-16 0v-80H40a8 8 0 0 1 0-16h80V40a8 8 0 0 1 16 0v80h80a8 8 0 0 1 8 8" />
</svg>
</button>
</div>
<div x-show="open" x-cloak
class="absolute left-0 z-10 w-56 mx-4 mt-2 origin-top-right rounded shadow-lg bg-coolgray-100 ring-1 ring-black ring-opacity-5 focus:outline-none"
role="menu" aria-orientation="vertical" aria-labelledby="menu-button" tabindex="-1">
<div class="py-1" role="none">
<li title="Tags" class="border-transparent hover:bg-coolgray-200 ">
<a class=" hover:bg-transparent hover:no-underline" href="{{ route('tags.index') }}">
<svg class="{{ request()->is('tags*') ? 'text-warning icon' : '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="M3 8v4.172a2 2 0 0 0 .586 1.414l5.71 5.71a2.41 2.41 0 0 0 3.408 0l3.592-3.592a2.41 2.41 0 0 0 0-3.408l-5.71-5.71A2 2 0 0 0 9.172 6H5a2 2 0 0 0-2 2" />
<path d="m18 19l1.592-1.592a4.82 4.82 0 0 0 0-6.816L15 6m-8 4h-.01" />
</g>
</svg>
Tags
</a>
</li>
<li title="Command Center" class="hover:bg-coolgray-200">
<a class="hover:bg-transparent hover:no-underline" href="{{ route('command-center') }}">
<svg xmlns="http://www.w3.org/2000/svg"
class="{{ request()->is('settings*') ? 'text-warning icon' : 'icon' }}"
class="{{ request()->is('command-center*') ? 'text-warning icon' : 'icon' }}"
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M10.325 4.317c.426 -1.756 2.924 -1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543 -.94 3.31 .826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756 .426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543 -.826 3.31 -2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756 -2.924 1.756 -3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543 .94 -3.31 -.826 -2.37 -2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756 -.426 -1.756 -2.924 0 -3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94 -1.543 .826 -3.31 2.37 -2.37c1 .608 2.296 .07 2.572 -1.065z" />
<path d="M9 12a3 3 0 1 0 6 0a3 3 0 0 0 -6 0" />
<path d="M5 7l5 5l-5 5" />
<path d="M12 19l7 0" />
</svg>
Settings
Command Center
</a>
</li>
<li title="Source" class="hover:bg-coolgray-200">
<a class="hover:bg-transparent hover:no-underline" href="{{ route('source.all') }}">
<svg class="{{ request()->is('source*') ? 'text-warning icon' : 'icon' }}"
viewBox="0 0 15 15" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor"
d="m6.793 1.207l.353.354l-.353-.354ZM1.207 6.793l-.353-.354l.353.354Zm0 1.414l.354-.353l-.354.353Zm5.586 5.586l-.354.353l.354-.353Zm1.414 0l-.353-.354l.353.354Zm5.586-5.586l.353.354l-.353-.354Zm0-1.414l-.354.353l.354-.353ZM8.207 1.207l.354-.353l-.354.353ZM6.44.854L.854 6.439l.707.707l5.585-5.585L6.44.854ZM.854 8.56l5.585 5.585l.707-.707l-5.585-5.585l-.707.707Zm7.707 5.585l5.585-5.585l-.707-.707l-5.585 5.585l.707.707Zm5.585-7.707L8.561.854l-.707.707l5.585 5.585l.707-.707Zm0 2.122a1.5 1.5 0 0 0 0-2.122l-.707.707a.5.5 0 0 1 0 .708l.707.707ZM6.44 14.146a1.5 1.5 0 0 0 2.122 0l-.707-.707a.5.5 0 0 1-.708 0l-.707.707ZM.854 6.44a1.5 1.5 0 0 0 0 2.122l.707-.707a.5.5 0 0 1 0-.708L.854 6.44Zm6.292-4.878a.5.5 0 0 1 .708 0L8.56.854a1.5 1.5 0 0 0-2.122 0l.707.707Zm-2 1.293l1 1l.708-.708l-1-1l-.708.708ZM7.5 5a.5.5 0 0 1-.5-.5H6A1.5 1.5 0 0 0 7.5 6V5Zm.5-.5a.5.5 0 0 1-.5.5v1A1.5 1.5 0 0 0 9 4.5H8ZM7.5 4a.5.5 0 0 1 .5.5h1A1.5 1.5 0 0 0 7.5 3v1Zm0-1A1.5 1.5 0 0 0 6 4.5h1a.5.5 0 0 1 .5-.5V3Zm.646 2.854l1.5 1.5l.707-.708l-1.5-1.5l-.707.708ZM10.5 8a.5.5 0 0 1-.5-.5H9A1.5 1.5 0 0 0 10.5 9V8Zm.5-.5a.5.5 0 0 1-.5.5v1A1.5 1.5 0 0 0 12 7.5h-1Zm-.5-.5a.5.5 0 0 1 .5.5h1A1.5 1.5 0 0 0 10.5 6v1Zm0-1A1.5 1.5 0 0 0 9 7.5h1a.5.5 0 0 1 .5-.5V6ZM7 5.5v4h1v-4H7Zm.5 5.5a.5.5 0 0 1-.5-.5H6A1.5 1.5 0 0 0 7.5 12v-1Zm.5-.5a.5.5 0 0 1-.5.5v1A1.5 1.5 0 0 0 9 10.5H8Zm-.5-.5a.5.5 0 0 1 .5.5h1A1.5 1.5 0 0 0 7.5 9v1Zm0-1A1.5 1.5 0 0 0 6 10.5h1a.5.5 0 0 1 .5-.5V9Z" />
</svg>
Sources
</a>
</li>
<li title="Security" class="hover:bg-coolgray-200">
<a class="hover:bg-transparent hover:no-underline"
href="{{ route('security.private-key.index') }}">
<svg class="{{ request()->is('security*') ? 'text-warning icon' : 'icon' }}"
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill="none" stroke="currentColor" stroke-linecap="round"
stroke-linejoin="round" stroke-width="2"
d="m16.555 3.843l3.602 3.602a2.877 2.877 0 0 1 0 4.069l-2.643 2.643a2.877 2.877 0 0 1-4.069 0l-.301-.301l-6.558 6.558a2 2 0 0 1-1.239.578L5.172 21H4a1 1 0 0 1-.993-.883L3 20v-1.172a2 2 0 0 1 .467-1.284l.119-.13L4 17h2v-2h2v-2l2.144-2.144l-.301-.301a2.877 2.877 0 0 1 0-4.069l2.643-2.643a2.877 2.877 0 0 1 4.069 0zM15 9h.01" />
</svg>
Security
</a>
</li>
<li title="Profile" class="hover:bg-coolgray-200">
<a class="hover:bg-transparent hover:no-underline" href="{{ route('profile') }}">
<svg xmlns="http://www.w3.org/2000/svg"
class="{{ request()->is('profile*') ? 'text-warning icon' : 'icon' }}"
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" />
<path d="M12 10m-3 0a3 3 0 1 0 6 0a3 3 0 1 0 -6 0" />
<path d="M6.168 18.849a4 4 0 0 1 3.832 -2.849h4a4 4 0 0 1 3.834 2.855" />
</svg>
Profile
</a>
</li>
<li title="Teams" class="hover:bg-coolgray-200">
<a class="hover:bg-transparent hover:no-underline" href="{{ route('team.index') }}">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
class="{{ request()->is('team*') ? 'text-warning icon' : 'icon' }}"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M10 13a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" />
<path d="M8 21v-1a2 2 0 0 1 2 -2h4a2 2 0 0 1 2 2v1" />
<path d="M15 5a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" />
<path d="M17 10h2a2 2 0 0 1 2 2v1" />
<path d="M5 5a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" />
<path d="M3 13v-1a2 2 0 0 1 2 -2h2" />
</svg>
Teams @if (isCloud())
/ Subscription
@endif
</a>
</li>
@endif
</ul>
</details>
@if (isCloud())
@if (isInstanceAdmin())
<li title="Settings" class="hover:bg-coolgray-200">
<a class="hover:bg-transparent hover:no-underline" href="/settings">
<svg xmlns="http://www.w3.org/2000/svg"
class="{{ request()->is('settings*') ? 'text-warning icon' : 'icon' }}"
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M10.325 4.317c.426 -1.756 2.924 -1.756 3.35 0a1.724 1.724 0 0 0 2.573 1.066c1.543 -.94 3.31 .826 2.37 2.37a1.724 1.724 0 0 0 1.065 2.572c1.756 .426 1.756 2.924 0 3.35a1.724 1.724 0 0 0 -1.066 2.573c.94 1.543 -.826 3.31 -2.37 2.37a1.724 1.724 0 0 0 -2.572 1.065c-.426 1.756 -2.924 1.756 -3.35 0a1.724 1.724 0 0 0 -2.573 -1.066c-1.543 .94 -3.31 -.826 -2.37 -2.37a1.724 1.724 0 0 0 -1.065 -2.572c-1.756 -.426 -1.756 -2.924 0 -3.35a1.724 1.724 0 0 0 1.066 -2.573c-.94 -1.543 .826 -3.31 2.37 -2.37c1 .608 2.296 .07 2.572 -1.065z" />
<path d="M9 12a3 3 0 1 0 6 0a3 3 0 0 0 -6 0" />
</svg>
Settings
</a>
</li>
@endif
<li title="Boarding" class="hover:bg-coolgray-200">
<a class="hover:bg-transparent hover:no-underline" href="{{ route('boarding') }}">
<svg class="{{ request()->is('boarding*') ? 'text-warning icon' : 'icon' }}"
viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor"
d="M224 128a8 8 0 0 1-8 8h-88a8 8 0 0 1 0-16h88a8 8 0 0 1 8 8m-96-56h88a8 8 0 0 0 0-16h-88a8 8 0 0 0 0 16m88 112h-88a8 8 0 0 0 0 16h88a8 8 0 0 0 0-16M82.34 42.34L56 68.69L45.66 58.34a8 8 0 0 0-11.32 11.32l16 16a8 8 0 0 0 11.32 0l32-32a8 8 0 0 0-11.32-11.32m0 64L56 132.69l-10.34-10.35a8 8 0 0 0-11.32 11.32l16 16a8 8 0 0 0 11.32 0l32-32a8 8 0 0 0-11.32-11.32m0 64L56 196.69l-10.34-10.35a8 8 0 0 0-11.32 11.32l16 16a8 8 0 0 0 11.32 0l32-32a8 8 0 0 0-11.32-11.32" />
</svg>
Boarding
</a>
</li>
</div>
</div>
</div>
@if (isCloud() && isInstanceAdmin())
<li title="Admin">
<a class="hover:bg-transparent" href="/admin">
<svg class="text-pink-600 icon" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
@@ -180,11 +204,12 @@
</div>
</li>
<form action="/logout" method="POST" class="hover:bg-transparent">
<li title="Logout" class="mb-6 hover:transparent">
<li title="Logout" class="mb-6 hover:transparent">
@csrf
<button type="submit" class="rounded-none hover:text-white hover:bg-transparent">
<svg class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor" d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2a9.985 9.985 0 0 1 8 4h-2.71a8 8 0 1 0 .001 12h2.71A9.985 9.985 0 0 1 12 22m7-6v-3h-8v-2h8V8l5 4z"/>
<path fill="currentColor"
d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2a9.985 9.985 0 0 1 8 4h-2.71a8 8 0 1 0 .001 12h2.71A9.985 9.985 0 0 1 12 22m7-6v-3h-8v-2h8V8l5 4z" />
</svg>
</button>
</li>

View File

@@ -1,4 +1,4 @@
@props(['closeWithX' => 'false', 'fullScreen' => 'false'])
@props(['closeWithX' => false, 'fullScreen' => false])
<div x-data="{
slideOverOpen: false
}" class="relative w-auto h-auto">
@@ -19,7 +19,7 @@
x-transition:leave-start="translate-x-0" x-transition:leave-end="translate-x-full"
@class([
'max-w-md w-screen' => !$fullScreen,
'max-w-7xl w-screen' => $fullScreen,
'max-w-4xl w-screen' => $fullScreen,
])>
<div
class="flex flex-col h-full py-6 overflow-hidden border-l shadow-lg bg-base-100 border-neutral-800">

View File

@@ -7,7 +7,7 @@
<div class="pl-2 pr-1 text-xs font-bold tracking-widerr text-warning">
{{ str($status)->before(':')->headline() }}
</div>
@if (!str($status)->startsWith('Proxy'))
@if (!str($status)->startsWith('Proxy') && !str($status)->contains('('))
<div class="text-xs text-warning">({{ str($status)->after(':') }})</div>
@endif
</div>

View File

@@ -7,7 +7,7 @@
<div class="pl-2 pr-1 text-xs font-bold tracking-wider text-success">
{{ str($status)->before(':')->headline() }}
</div>
@if (!str($status)->startsWith('Proxy'))
@if (!str($status)->startsWith('Proxy') && !str($status)->contains('('))
<div class="text-xs text-success">({{ str($status)->after(':') }})</div>
@endif
</div>

View File

@@ -121,18 +121,24 @@
There are already servers available for your Team. Do you want to use one of them?
</x-slot:question>
<x-slot:actions>
<x-forms.button class="justify-center w-64 box" wire:click="createNewServer">No (create one for me)
</x-forms.button>
<div>
<form wire:submit='selectExistingServer' class="flex flex-col w-full gap-4 lg:w-96">
<x-forms.select label="Existing servers" class="w-96" id='selectedExistingServer'>
@foreach ($servers as $server)
<option wire:key="{{ $loop->index }}" value="{{ $server->id }}">
{{ $server->name }}</option>
@endforeach
</x-forms.select>
<x-forms.button type="submit">Use this Server</x-forms.button>
</form>
<div class="flex flex-col gap-4">
<div>
<x-forms.button class="justify-center w-64 box" wire:click="createNewServer">No (create one
for
me)
</x-forms.button>
</div>
<div>
<form wire:submit='selectExistingServer' class="flex flex-col w-full gap-4 lg:w-96">
<x-forms.select label="Existing servers" class="w-96" id='selectedExistingServer'>
@foreach ($servers as $server)
<option wire:key="{{ $loop->index }}" value="{{ $server->id }}">
{{ $server->name }}</option>
@endforeach
</x-forms.select>
<x-forms.button type="submit">Use this Server</x-forms.button>
</form>
</div>
</div>
@if (!$serverReachable)
This server is not reachable with the following public key.
@@ -216,7 +222,7 @@
helper="If you are using Cloudflare Tunnels, enable this. It will proxy all ssh requests to your server through Cloudflare.<br><span class='text-warning'>Coolify does not install/setup Cloudflare (cloudflared) on your server.</span>"
id="isCloudflareTunnel" label="Cloudflare Tunnel" />
</div>
<x-forms.button type="submit">Check Connection</x-forms.button>
<x-forms.button type="submit">Continue</x-forms.button>
</form>
</x-slot:actions>
<x-slot:explanation>
@@ -227,14 +233,15 @@
@endif
</div>
<div>
@if ($currentState === 'install-docker')
<x-boarding-step title="Install Docker">
@if ($currentState === 'validate-server')
<x-boarding-step title="Validate & Configure Server">
<x-slot:question>
Could not find Docker Engine on your server. Do you want me to install it for you?
I need to validate your server (connection, Docker Engine, etc) and configure if something is
missing for me. Are you okay with this?
</x-slot:question>
<x-slot:actions>
<x-slide-over closeWithX fullScreen>
<x-slot:title>Configuring Server</x-slot:title>
<x-slot:title>Validating & Configuring</x-slot:title>
<x-slot:content>
<livewire:server.validate-and-install :server="$this->createdServer" />
</x-slot:content>
@@ -254,7 +261,7 @@
</x-boarding-step>
@endif
</div>
<div>
{{-- <div>
@if ($currentState === 'select-proxy')
<x-boarding-step title="Select a Proxy">
<x-slot:question>
@@ -281,7 +288,7 @@
</x-slot:explanation>
</x-boarding-step>
@endif
</div>
</div> --}}
<div>
@if ($currentState === 'create-project')
<x-boarding-step title="Project">

View File

@@ -1,5 +1,9 @@
<div>
<livewire:project.application.preview.form :application="$application" />
@if (count($application->additional_servers) > 0)
<div class="pb-4">Previews will be deployed on <span
class="text-warning">{{ $application->destination->server->name }}</span>.</div>
@endif
<div>
@if ($application->is_github_based())
<div class="flex items-center gap-2">
@@ -51,7 +55,7 @@
</div>
@if ($application->previews->count() > 0)
<div class="pb-4">Previews</div>
<div class="flex gap-6 ">
<div class="flex flex-wrap gap-6">
@foreach ($application->previews as $preview)
<div class="flex flex-col p-4 bg-coolgray-200">
<div class="flex gap-2">PR #{{ data_get($preview, 'pull_request_id') }} |

View File

@@ -213,9 +213,9 @@
@endif --}}
<div class="flex flex-col justify-center gap-4 text-left xl:flex-row xl:flex-wrap">
@forelse($servers as $server)
<div class="box group" wire:click="setServer({{ $server }})">
<div class="w-64 box group" wire:click="setServer({{ $server }})">
<div class="flex flex-col mx-6">
<div class="group-hover:text-white">
<div class="font-bold group-hover:text-white">
{{ $server->name }}
</div>
<div class="text-xs group-hover:text-white">
@@ -263,13 +263,13 @@
Standalone Docker <span class="text-xs">({{ $standaloneDocker->name }})</span>
</div>
<div class="text-xs group-hover:text-white">
network: {{ $standaloneDocker->network }}</div>
Network: {{ $standaloneDocker->network }}</div>
</div>
</div>
@endforeach
@endif
<a href="{{ route('destination.new', ['server_id' => $server_id]) }}"
class="items-center justify-center pb-10 text-center box-without-bg group bg-coollabs hover:bg-coollabs-100">
class="items-center justify-center text-center box-without-bg group bg-coollabs hover:bg-coollabs-100">
<div class="flex flex-col mx-6 ">
<div class="font-bold text-white">
+ Add New

View File

@@ -48,25 +48,27 @@
<x-forms.input placeholder="Search for name, fqdn..." class="w-full" x-model="search" />
<div class="grid grid-cols-1 gap-4 pt-4 lg:grid-cols-2 xl:grid-cols-3">
<template x-for="item in filteredApplications" :key="item.id">
<span class="relative">
<span>
<a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col mx-6">
<div class="pb-2 font-bold text-white" x-text="item.name"></div>
<div class="flex gap-2">
<div class="pb-2 font-bold text-white" x-text="item.name"></div>
<template x-if="item.status.startsWith('running')">
<div title="running" class="mt-1 bg-success badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div title="exited" class="mt-1 bg-error badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div title="restarting" class="mt-1 bg-warningbadge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('degraded')">
<div title="degraded" class="mt-1 bg-warning badge badge-xs"></div>
</template>
</div>
<div class="description" x-text="item.description"></div>
<div class="description" x-text="item.fqdn"></div>
</div>
<template x-if="item.status.startsWith('running')">
<div class="absolute bg-success -top-1 -left-1 badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div class="absolute bg-error -top-1 -left-1 badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('restarting')" >
<div class="absolute bg-warning -top-1 -left-1 badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('degraded')">
<div class="absolute bg-warning -top-1 -left-1 badge badge-xs"></div>
</template>
</a>
<div class="flex gap-1 pt-1 group-hover:text-white group min-h-6">
<template x-for="tag in item.tags">
@@ -79,21 +81,26 @@
</span>
</template>
<template x-for="item in filteredPostgresqls" :key="item.id">
<span class="relative">
<span>
<a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col mx-6">
<div class="font-bold text-white" x-text="item.name"></div>
<div class="flex gap-2">
<div class="pb-2 font-bold text-white" x-text="item.name"></div>
<template x-if="item.status.startsWith('running')">
<div title="running" class="mt-1 bg-success badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div title="exited" class="mt-1 bg-error badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div title="restarting" class="mt-1 bg-warningbadge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('degraded')">
<div title="degraded" class="mt-1 bg-warning badge badge-xs"></div>
</template>
</div>
<div class="description" x-text="item.description"></div>
</div>
<template x-if="item.status.startsWith('running')">
<div class="absolute bg-success -top-1 -left-1 badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div class="absolute bg-error -top-1 -left-1 badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div class="absolute bg-warning -top-1 -left-1 badge badge-xs"></div>
</template>
</a>
<div class="flex gap-1 pt-1 group-hover:text-white group min-h-6">
<template x-for="tag in item.tags">
@@ -106,22 +113,26 @@
</span>
</template>
<template x-for="item in filteredRedis" :key="item.id">
<span class="relative">
<span>
<a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col mx-6">
<div class="font-bold text-white" x-text="item.name"></div>
<div class="flex gap-2">
<div class="pb-2 font-bold text-white" x-text="item.name"></div>
<template x-if="item.status.startsWith('running')">
<div title="running" class="mt-1 bg-success badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div title="exited" class="mt-1 bg-error badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div title="restarting" class="mt-1 bg-warningbadge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('degraded')">
<div title="degraded" class="mt-1 bg-warning badge badge-xs"></div>
</template>
</div>
<div class="description" x-text="item.description"></div>
</div>
<template x-if="item.status.startsWith('running')">
<div class="absolute bg-success -top-1 -left-1 badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div class="absolute bg-error -top-1 -left-1 badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div class="absolute bg-warning -top-1 -left-1 badge badge-xs"></div>
</template>
</a>
<div class="flex gap-1 pt-1 group-hover:text-white group min-h-6">
<template x-for="tag in item.tags">
@@ -134,21 +145,26 @@
</span>
</template>
<template x-for="item in filteredMongodbs" :key="item.id">
<span class="relative">
<span>
<a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col mx-6">
<div class="font-bold text-white" x-text="item.name"></div>
<div class="flex gap-2">
<div class="pb-2 font-bold text-white" x-text="item.name"></div>
<template x-if="item.status.startsWith('running')">
<div title="running" class="mt-1 bg-success badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div title="exited" class="mt-1 bg-error badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div title="restarting" class="mt-1 bg-warningbadge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('degraded')">
<div title="degraded" class="mt-1 bg-warning badge badge-xs"></div>
</template>
</div>
<div class="description" x-text="item.description"></div>
</div>
<template x-if="item.status.startsWith('running')">
<div class="absolute bg-success -top-1 -left-1 badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div class="absolute bg-error -top-1 -left-1 badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div class="absolute bg-warning -top-1 -left-1 badge badge-xs"></div>
</template>
</a>
<div class="flex gap-1 pt-1 group-hover:text-white group min-h-6">
<template x-for="tag in item.tags">
@@ -161,21 +177,26 @@
</span>
</template>
<template x-for="item in filteredMysqls" :key="item.id">
<span class="relative">
<span>
<a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col mx-6">
<div class="font-bold text-white" x-text="item.name"></div>
<div class="flex gap-2">
<div class="pb-2 font-bold text-white" x-text="item.name"></div>
<template x-if="item.status.startsWith('running')">
<div title="running" class="mt-1 bg-success badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div title="exited" class="mt-1 bg-error badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div title="restarting" class="mt-1 bg-warningbadge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('degraded')">
<div title="degraded" class="mt-1 bg-warning badge badge-xs"></div>
</template>
</div>
<div class="description" x-text="item.description"></div>
</div>
<template x-if="item.status.startsWith('running')">
<div class="absolute bg-success -top-1 -left-1 badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div class="absolute bg-error -top-1 -left-1 badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div class="absolute bg-warning -top-1 -left-1 badge badge-xs"></div>
</template>
</a>
<div class="flex gap-1 pt-1 group-hover:text-white group min-h-6">
<template x-for="tag in item.tags">
@@ -188,21 +209,26 @@
</span>
</template>
<template x-for="item in filteredMariadbs" :key="item.id">
<span class="relative">
<span>
<a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col mx-6">
<div class="font-bold text-white" x-text="item.name"></div>
<div class="flex gap-2">
<div class="pb-2 font-bold text-white" x-text="item.name"></div>
<template x-if="item.status.startsWith('running')">
<div title="running" class="mt-1 bg-success badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div title="exited" class="mt-1 bg-error badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div title="restarting" class="mt-1 bg-warningbadge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('degraded')">
<div title="degraded" class="mt-1 bg-warning badge badge-xs"></div>
</template>
</div>
<div class="description" x-text="item.description"></div>
</div>
<template x-if="item.status.startsWith('running')">
<div class="absolute bg-success -top-1 -left-1 badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div class="absolute bg-error -top-1 -left-1 badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div class="absolute bg-warning -top-1 -left-1 badge badge-xs"></div>
</template>
</a>
<div class="flex gap-1 pt-1 group-hover:text-white group min-h-6">
<template x-for="tag in item.tags">
@@ -215,21 +241,26 @@
</span>
</template>
<template x-for="item in filteredServices" :key="item.id">
<span class="relative">
<span>
<a class="h-24 box group" :href="item.hrefLink">
<div class="flex flex-col mx-6">
<div class="font-bold text-white" x-text="item.name"></div>
<div class="flex gap-2">
<div class="pb-2 font-bold text-white" x-text="item.name"></div>
<template x-if="item.status.startsWith('running')">
<div title="running" class="mt-1 bg-success badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div title="exited" class="mt-1 bg-error badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('restarting')">
<div title="restarting" class="mt-1 bg-warningbadge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('degraded')">
<div title="degraded" class="mt-1 bg-warning badge badge-xs"></div>
</template>
</div>
<div class="description" x-text="item.description"></div>
</div>
<template x-if="item.status.startsWith('running')">
<div class="absolute bg-success -top-1 -left-1 badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('exited')">
<div class="absolute bg-error -top-1 -left-1 badge badge-xs"></div>
</template>
<template x-if="item.status.startsWith('degraded')">
<div class="absolute bg-warning -top-1 -left-1 badge badge-xs"></div>
</template>
</a>
<div class="flex gap-1 pt-1 group-hover:text-white group min-h-6">
<template x-for="tag in item.tags">

View File

@@ -76,7 +76,7 @@
</div>
<div class="flex items-center px-4">
<a class="flex flex-col flex-1 group-hover:text-white hover:no-underline"
href="{{ route('project.service.index', [...$parameters, 'service_name' => $application->name]) }}">
href="{{ route('project.service.index', [...$parameters, 'stack_service_uuid' => $application->uuid]) }}">
<svg xmlns="http://www.w3.org/2000/svg" class="icon hover:text-warning"
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
@@ -122,7 +122,7 @@
</div>
<div class="flex items-center px-4">
<a class="flex flex-col flex-1 group-hover:text-white hover:no-underline"
href="{{ route('project.service.index', [...$parameters, 'service_name' => $database->name]) }}">
href="{{ route('project.service.index', [...$parameters, 'stack_service_uuid' => $database->uuid]) }}">
<svg xmlns="http://www.w3.org/2000/svg" class="icon hover:text-warning"
viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">

View File

@@ -3,7 +3,7 @@
<div class="flex h-full pt-6">
<div class="flex flex-col gap-4 min-w-fit">
<a class="{{ request()->routeIs('project.service.configuration') ? 'text-white' : '' }}"
href="{{ route('project.service.configuration', [...$parameters, 'service_name' => null]) }}">
href="{{ route('project.service.configuration', [...$parameters, 'stack_service_uuid' => null]) }}">
<button><- Back</button>
</a>
<a :class="activeTab === 'general' && 'text-white'"

View File

@@ -55,21 +55,25 @@
@endforeach
@endif
</div>
{{-- @if ($resource->getMorphClass() === 'App\Models\Application')
@if ($resource->getMorphClass() === 'App\Models\Application')
@if (count($networks) > 0)
<h4>Choose another server</h4>
<div class="pb-4 description">(experimental) </div>
<div class="grid grid-cols-1 gap-4 ">
<div class="grid grid-cols-1 gap-4">
@foreach ($networks as $network)
<div wire:click="addServer('{{ $network->id }}','{{ data_get($network, 'server.id') }}')"
class="box w-96">
{{ data_get($network, 'server.name') }}
{{ $network->name }}
class="relative flex flex-col text-white cursor-default box w-96">
<div>
Server: {{ data_get($network, 'server.name') }}
</div>
<div>
Network: {{ data_get($network, 'name') }}
</div>
</div>
@endforeach
</div>
@else
<div class="text-neutral-500">No additional servers available to attach.</div>
@endif
@endif --}}
@endif
</div>

View File

@@ -19,7 +19,7 @@
@endif
@if ((!$server->settings->is_reachable || !$server->settings->is_usable) && $server->id !== 0)
<x-slide-over closeWithX fullScreen>
<x-slot:title>Configuring Server</x-slot:title>
<x-slot:title>Validating & Configuring</x-slot:title>
<x-slot:content>
<livewire:server.validate-and-install :server="$server" />
</x-slot:content>

View File

@@ -12,12 +12,21 @@
</div>
<div class="flex flex-col gap-4">
<div class="flex flex-col w-full gap-2 xl:flex-row">
<x-forms.input type="password" required id="server.settings.logdrain_newrelic_license_key"
label="License Key" />
<x-forms.input required id="server.settings.logdrain_newrelic_base_uri"
placeholder="https://log-api.eu.newrelic.com/log/v1"
helper="For EU use: https://log-api.eu.newrelic.com/log/v1<br>For US use: https://log-api.newrelic.com/log/v1"
label="Endpoint" />
@if ($server->isLogDrainEnabled())
<x-forms.input disabled type="password" required
id="server.settings.logdrain_newrelic_license_key" label="License Key" />
<x-forms.input disabled required id="server.settings.logdrain_newrelic_base_uri"
placeholder="https://log-api.eu.newrelic.com/log/v1"
helper="For EU use: https://log-api.eu.newrelic.com/log/v1<br>For US use: https://log-api.newrelic.com/log/v1"
label="Endpoint" />
@else
<x-forms.input type="password" required id="server.settings.logdrain_newrelic_license_key"
label="License Key" />
<x-forms.input required id="server.settings.logdrain_newrelic_base_uri"
placeholder="https://log-api.eu.newrelic.com/log/v1"
helper="For EU use: https://log-api.eu.newrelic.com/log/v1<br>For US use: https://log-api.newrelic.com/log/v1"
label="Endpoint" />
@endif
</div>
</div>
<div class="flex justify-end gap-4 pt-6">
@@ -35,9 +44,17 @@
<form wire:submit='submit("axiom")' class="flex flex-col">
<div class="flex flex-col gap-4">
<div class="flex flex-col w-full gap-2 xl:flex-row">
<x-forms.input type="password" required id="server.settings.logdrain_axiom_api_key"
label="API Key" />
<x-forms.input required id="server.settings.logdrain_axiom_dataset_name" label="Dataset Name" />
@if ($server->isLogDrainEnabled())
<x-forms.input disabled type="password" required id="server.settings.logdrain_axiom_api_key"
label="API Key" />
<x-forms.input disabled required id="server.settings.logdrain_axiom_dataset_name"
label="Dataset Name" />
@else
<x-forms.input type="password" required id="server.settings.logdrain_axiom_api_key"
label="API Key" />
<x-forms.input required id="server.settings.logdrain_axiom_dataset_name"
label="Dataset Name" />
@endif
</div>
</div>
<div class="flex justify-end gap-4 pt-6">
@@ -71,10 +88,18 @@
</div>
<form wire:submit='submit("custom")' class="flex flex-col">
<div class="flex flex-col gap-4">
<x-forms.textarea rows="6" required id="server.settings.logdrain_custom_config"
label="Custom FluentBit Configuration" />
<x-forms.textarea id="server.settings.logdrain_custom_config_parser"
label="Custom Parser Configuration" />
@if ($server->isLogDrainEnabled())
<x-forms.textarea disabled rows="6" required id="server.settings.logdrain_custom_config"
label="Custom FluentBit Configuration" />
<x-forms.textarea disabled id="server.settings.logdrain_custom_config_parser"
label="Custom Parser Configuration" />
@else
<x-forms.textarea rows="6" required id="server.settings.logdrain_custom_config"
label="Custom FluentBit Configuration" />
<x-forms.textarea id="server.settings.logdrain_custom_config_parser"
label="Custom Parser Configuration" />
@endif
</div>
<div class="flex justify-end gap-4 pt-6">
<x-forms.button type="submit">

View File

@@ -51,7 +51,7 @@
</x-forms.button>
</div>
@else
<button onclick="checkProxy()"
<button x-on:click="$wire.dispatch('checkProxy')"
class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-warning" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round"
@@ -64,9 +64,6 @@
@endif
@endif
<script>
function checkProxy() {
window.Livewire.dispatch('checkProxy')
}
Livewire.on('proxyChecked', () => {
startProxy.showModal();
window.Livewire.dispatch('startProxy');

View File

@@ -68,6 +68,51 @@
<div class="w-64"><x-loading text="Docker is installed:" /></div>
@endif
@endif
@if ($docker_compose_installed)
<div class="flex w-64 gap-2">Docker Compose is installed: <svg class="w-5 h-5 text-success"
viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<g fill="currentColor">
<path
d="m237.66 85.26l-128.4 128.4a8 8 0 0 1-11.32 0l-71.6-72a8 8 0 0 1 0-11.31l24-24a8 8 0 0 1 11.32 0l36.68 35.32a8 8 0 0 0 11.32 0l92.68-91.32a8 8 0 0 1 11.32 0l24 23.6a8 8 0 0 1 0 11.31"
opacity=".2" />
<path
d="m243.28 68.24l-24-23.56a16 16 0 0 0-22.58 0L104 136l-.11-.11l-36.64-35.27a16 16 0 0 0-22.57.06l-24 24a16 16 0 0 0 0 22.61l71.62 72a16 16 0 0 0 22.63 0l128.4-128.38a16 16 0 0 0-.05-22.67M103.62 208L32 136l24-24l.11.11l36.64 35.27a16 16 0 0 0 22.52 0L208.06 56L232 79.6Z" />
</g>
</svg></div>
@if ($proxy_started)
<div class="flex w-64 gap-2">Proxy Started: <svg class="w-5 h-5 text-success" viewBox="0 0 256 256"
xmlns="http://www.w3.org/2000/svg">
<g fill="currentColor">
<path
d="m237.66 85.26l-128.4 128.4a8 8 0 0 1-11.32 0l-71.6-72a8 8 0 0 1 0-11.31l24-24a8 8 0 0 1 11.32 0l36.68 35.32a8 8 0 0 0 11.32 0l92.68-91.32a8 8 0 0 1 11.32 0l24 23.6a8 8 0 0 1 0 11.31"
opacity=".2" />
<path
d="m243.28 68.24l-24-23.56a16 16 0 0 0-22.58 0L104 136l-.11-.11l-36.64-35.27a16 16 0 0 0-22.57.06l-24 24a16 16 0 0 0 0 22.61l71.62 72a16 16 0 0 0 22.63 0l128.4-128.38a16 16 0 0 0-.05-22.67M103.62 208L32 136l24-24l.11.11l36.64 35.27a16 16 0 0 0 22.52 0L208.06 56L232 79.6Z" />
</g>
</svg></div>
@else
@if ($error)
<div class="flex w-64 gap-2">Proxy Started: <svg class="w-5 h-5 text-error" viewBox="0 0 256 256"
xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor"
d="M208.49 191.51a12 12 0 0 1-17 17L128 145l-63.51 63.49a12 12 0 0 1-17-17L111 128L47.51 64.49a12 12 0 0 1 17-17L128 111l63.51-63.52a12 12 0 0 1 17 17L145 128Z" />
</svg></div>
@else
<div class="w-64"><x-loading text="Proxy Started:" /></div>
@endif
@endif
@else
@if ($error)
<div class="flex w-64 gap-2">Docker Compose is installed: <svg class="w-5 h-5 text-error"
viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor"
d="M208.49 191.51a12 12 0 0 1-17 17L128 145l-63.51 63.49a12 12 0 0 1-17-17L111 128L47.51 64.49a12 12 0 0 1 17-17L128 111l63.51-63.52a12 12 0 0 1 17 17L145 128Z" />
</svg></div>
@else
<div class="w-64"><x-loading text="Docker Compose is installed:" /></div>
@endif
@endif
@endif
@isset($docker_version)
<div class="flex w-64 gap-2">Minimum Docker version installed: <svg class="w-5 h-5 text-success"
@@ -81,6 +126,7 @@
</g>
</svg></div>
@endisset
<livewire:new-activity-monitor header="Logs" />
@isset($error)
<pre class="font-bold whitespace-pre-line text-error">{!! $error !!}</pre>

View File

@@ -79,9 +79,7 @@ use App\Livewire\Waitlist\Index as WaitlistIndex;
if (isDev()) {
Route::get('/dev/compose', Compose::class)->name('dev.compose');
}
if (isCloud()) {
Route::get('/admin', AdminIndex::class)->name('admin.index');
}
Route::get('/admin', AdminIndex::class)->name('admin.index');
Route::post('/forgot-password', [Controller::class, 'forgot_password'])->name('password.forgot');
Route::get('/api/v1/test/realtime', [Controller::class, 'realtime_test'])->middleware('auth');
@@ -165,7 +163,7 @@ Route::middleware(['auth', 'verified'])->group(function () {
});
Route::prefix('project/{project_uuid}/{environment_name}/service/{service_uuid}')->group(function () {
Route::get('/', ServiceConfiguration::class)->name('project.service.configuration');
Route::get('/{service_name}', ServiceIndex::class)->name('project.service.index');
Route::get('/{stack_service_uuid}', ServiceIndex::class)->name('project.service.index');
Route::get('/command', ExecuteContainerCommand::class)->name('project.service.command');
Route::get('/tasks/{task_uuid}', ScheduledTaskShow::class)->name('project.service.scheduled-tasks');
});

View File

@@ -861,7 +861,11 @@ Route::post('/payments/stripe/events', function () {
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
if (!$subscription) {
Sleep::for(5)->seconds();
$subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail();
$subscription = Subscription::where('stripe_customer_id', $customerId)->first();
}
if (!$subscription) {
send_internal_notification('No subscription found for: ' . $customerId);
return response("No subscription found", 400);
}
$trialEndedAlready = data_get($subscription, 'stripe_trial_already_ended');
$cancelAtPeriodEnd = data_get($data, 'cancel_at_period_end');

View File

@@ -4,15 +4,20 @@
services:
filebrowser:
image: filebrowser/filebrowser:s6
image: filebrowser/filebrowser:latest
environment:
- SERVICE_FQDN_FILEBROWSER
- PUID=1000
- PGID=1000
volumes:
- filebrowser-srv:/srv
- filebrowser-database:/database/filebrowser.db
- filebrowser-config:/config/settings.json
- type: bind
source: ./srv
target: /srv
isDirectory: true
- ./database.db:/database.db
- type: bind
source: ./filebrowser.json
target: /.filebrowser.json
read_only: true
content: "{}"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80"]
interval: 2s

View File

@@ -0,0 +1,20 @@
# documentation: https://syncthing.net/
# slogan: Syncthing is a continuous file synchronization program. It synchronizes files between two or more computers in real time, safely protected from prying eyes. Your data is your data alone and you deserve to choose where it is stored, whether it is shared with some third party, and how its transmitted over the internet.
# tags: filestorage, data, synchronization
services:
syncthing:
image: 'lscr.io/linuxserver/syncthing:latest'
environment:
- SERVICE_FQDN_SYNCTHING
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
volumes:
- 'syncthing-config:/config'
- 'syncthing-data1:/data1'
- 'syncthing-data2:/data2'
ports:
- '22000:22000/tcp'
- '22000:22000/udp'
- '21027:21027/udp'

View File

@@ -143,7 +143,7 @@
"filebrowser": {
"documentation": "https:\/\/filebrowser.org\/configuration",
"slogan": "FileBrowser is a self-hosted, web-based file manager and file explorer with a user-friendly interface. It allows you to manage and organize your files and directories directly from your web browser.",
"compose": "c2VydmljZXM6CiAgZmlsZWJyb3dzZXI6CiAgICBpbWFnZTogJ2ZpbGVicm93c2VyL2ZpbGVicm93c2VyOnM2JwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gU0VSVklDRV9GUUROX0ZJTEVCUk9XU0VSCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICB2b2x1bWVzOgogICAgICAtICdmaWxlYnJvd3Nlci1zcnY6L3NydicKICAgICAgLSAnZmlsZWJyb3dzZXItZGF0YWJhc2U6L2RhdGFiYXNlL2ZpbGVicm93c2VyLmRiJwogICAgICAtICdmaWxlYnJvd3Nlci1jb25maWc6L2NvbmZpZy9zZXR0aW5ncy5qc29uJwogICAgaGVhbHRoY2hlY2s6CiAgICAgIHRlc3Q6CiAgICAgICAgLSBDTUQKICAgICAgICAtIGN1cmwKICAgICAgICAtICctZicKICAgICAgICAtICdodHRwOi8vbG9jYWxob3N0OjgwJwogICAgICBpbnRlcnZhbDogMnMKICAgICAgdGltZW91dDogMTBzCiAgICAgIHJldHJpZXM6IDE1Cg==",
"compose": "c2VydmljZXM6CiAgZmlsZWJyb3dzZXI6CiAgICBpbWFnZTogJ2ZpbGVicm93c2VyL2ZpbGVicm93c2VyOmxhdGVzdCcKICAgIGVudmlyb25tZW50OgogICAgICAtIFNFUlZJQ0VfRlFETl9GSUxFQlJPV1NFUgogICAgdm9sdW1lczoKICAgICAgLQogICAgICAgIHR5cGU6IGJpbmQKICAgICAgICBzb3VyY2U6IC4vc3J2CiAgICAgICAgdGFyZ2V0OiAvc3J2CiAgICAgICAgaXNEaXJlY3Rvcnk6IHRydWUKICAgICAgLSAnLi9kYXRhYmFzZS5kYjovZGF0YWJhc2UuZGInCiAgICAgIC0KICAgICAgICB0eXBlOiBiaW5kCiAgICAgICAgc291cmNlOiAuL2ZpbGVicm93c2VyLmpzb24KICAgICAgICB0YXJnZXQ6IC8uZmlsZWJyb3dzZXIuanNvbgogICAgICAgIHJlYWRfb25seTogdHJ1ZQogICAgICAgIGNvbnRlbnQ6ICd7fScKICAgIGhlYWx0aGNoZWNrOgogICAgICB0ZXN0OgogICAgICAgIC0gQ01ECiAgICAgICAgLSBjdXJsCiAgICAgICAgLSAnLWYnCiAgICAgICAgLSAnaHR0cDovL2xvY2FsaG9zdDo4MCcKICAgICAgaW50ZXJ2YWw6IDJzCiAgICAgIHRpbWVvdXQ6IDEwcwogICAgICByZXRyaWVzOiAxNQo=",
"tags": [
"file-management",
"storage-access",
@@ -492,6 +492,16 @@
"tool"
]
},
"syncthing": {
"documentation": "https:\/\/syncthing.net\/",
"slogan": "Syncthing is a continuous file synchronization program. It synchronizes files between two or more computers in real time, safely protected from prying eyes. Your data is your data alone and you deserve to choose where it is stored, whether it is shared with some third party, and how it\u2019s transmitted over the internet.",
"compose": "c2VydmljZXM6CiAgc3luY3RoaW5nOgogICAgaW1hZ2U6ICdsc2NyLmlvL2xpbnV4c2VydmVyL3N5bmN0aGluZzpsYXRlc3QnCiAgICBlbnZpcm9ubWVudDoKICAgICAgLSBTRVJWSUNFX0ZRRE5fU1lOQ1RISU5HCiAgICAgIC0gUFVJRD0xMDAwCiAgICAgIC0gUEdJRD0xMDAwCiAgICAgIC0gVFo9RXRjL1VUQwogICAgdm9sdW1lczoKICAgICAgLSAnc3luY3RoaW5nLWNvbmZpZzovY29uZmlnJwogICAgICAtICdzeW5jdGhpbmctZGF0YTE6L2RhdGExJwogICAgICAtICdzeW5jdGhpbmctZGF0YTI6L2RhdGEyJwogICAgcG9ydHM6CiAgICAgIC0gJzIyMDAwOjIyMDAwL3RjcCcKICAgICAgLSAnMjIwMDA6MjIwMDAvdWRwJwogICAgICAtICcyMTAyNzoyMTAyNy91ZHAnCg==",
"tags": [
"filestorage",
"data",
"synchronization"
]
},
"trigger-with-external-database": {
"documentation": "https:\/\/trigger.dev\/docs\/documentation\/guides\/self-hosting",
"slogan": "The open source Background Jobs framework for TypeScript",

View File

@@ -0,0 +1,31 @@
<?php
test('ConvertCapAdd', function () {
$input = '--cap-add=NET_ADMIN --cap-add=NET_RAW --cap-add SYS_ADMIN';
$output = convert_docker_run_to_compose($input);
expect($output)->toBe([
'cap_add' => ['NET_ADMIN', 'NET_RAW', 'SYS_ADMIN'],
])->ray();
});
test('ConvertPrivilegedAndInit', function () {
$input = '---privileged --init';
$output = convert_docker_run_to_compose($input);
expect($output)->toBe([
'privileged' => true,
'init' => true,
])->ray();
});
test('ConvertUlimit', function () {
$input = '--ulimit nofile=262144:262144';
$output = convert_docker_run_to_compose($input);
expect($output)->toBe([
'ulimits' => [
'nofile' => [
'soft' => '262144',
'hard' => '262144',
],
],
])->ray();
});

View File

@@ -1,7 +0,0 @@
<?php
it('returns a successful response', function () {
$response = $this->get('/api/health');
$response->assertStatus(200);
});

View File

@@ -1,7 +0,0 @@
<?php
test('parse', function () {
expect($result)->toBe(3);
});

View File

@@ -1,5 +1,4 @@
<?php
/*
|--------------------------------------------------------------------------
| Test Case
@@ -10,11 +9,7 @@
| need to change it using the "uses()" function to bind a different classes or traits.
|
*/
uses(
Tests\TestCase::class,
// Illuminate\Foundation\Testing\RefreshDatabase::class,
)->in('Feature');
uses(Tests\TestCase::class)->in('Feature');
/*
|--------------------------------------------------------------------------
@@ -27,9 +22,9 @@ uses(
|
*/
expect()->extend('toBeOne', function () {
return $this->toBe(1);
});
// expect()->extend('toBeOne', function () {
// return $this->toBe(1);
// });
/*
|--------------------------------------------------------------------------
@@ -42,7 +37,7 @@ expect()->extend('toBeOne', function () {
|
*/
function something()
{
// ..
}
// function something()
// {
// // ..
// }

View File

@@ -1,5 +0,0 @@
<?php
test('that true is true', function () {
expect(true)->toBeTrue();
});

View File

@@ -1,5 +0,0 @@
<?php
test('globals')
->expect(['dd', 'dump'])
->not->toBeUsed();

View File

@@ -4,7 +4,7 @@
"version": "3.12.36"
},
"v4": {
"version": "4.0.0-beta.212"
"version": "4.0.0-beta.215"
}
}
}