wip
This commit is contained in:
parent
a0da981ec7
commit
75326f6626
30
Dockerfile
30
Dockerfile
@ -1,30 +0,0 @@
|
||||
FROM serversideup/php:8.2-fpm-nginx
|
||||
|
||||
ARG NODE_VERSION=18
|
||||
ARG POSTGRES_VERSION=15
|
||||
|
||||
RUN apt-get update \
|
||||
&& curl -sLS https://deb.nodesource.com/setup_$NODE_VERSION.x | bash - \
|
||||
&& apt-get install -y nodejs \
|
||||
&& npm install -g npm
|
||||
|
||||
RUN apt-get install -y php-pgsql openssh-client
|
||||
|
||||
RUN apt-get -y autoremove \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/*
|
||||
|
||||
# Adding Coolify User and SSH key to connect to the Testing-Host (which is simulation of a VPS)
|
||||
RUN useradd -ms /bin/bash coolify
|
||||
USER coolify
|
||||
RUN mkdir -p ~/.ssh
|
||||
RUN echo "-----BEGIN OPENSSH PRIVATE KEY-----\n\
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\n\
|
||||
QyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevAAAAJi/QySHv0Mk\n\
|
||||
hwAAAAtzc2gtZWQyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevA\n\
|
||||
AAAECBQw4jg1WRT2IGHMncCiZhURCts2s24HoDS0thHnnRKVuGmoeGq/pojrsyP1pszcNV\n\
|
||||
uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==\n\
|
||||
-----END OPENSSH PRIVATE KEY-----" >> ~/.ssh/id_ed25519
|
||||
RUN chmod 0600 ~/.ssh/id_ed25519
|
||||
|
||||
USER root
|
51
app/Http/Livewire/RunCommand.php
Executable file
51
app/Http/Livewire/RunCommand.php
Executable file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class RunCommand extends Component
|
||||
{
|
||||
public $activity;
|
||||
|
||||
public $isKeepAliveOn = false;
|
||||
|
||||
public $manualKeepAlive = false;
|
||||
|
||||
public $command = '';
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.run-command');
|
||||
}
|
||||
|
||||
public function runCommand()
|
||||
{
|
||||
// TODO Execute with Livewire Normally
|
||||
$this->activity = coolifyProcess($this->command, 'testing-host');
|
||||
|
||||
|
||||
// Override manual to experiment
|
||||
// $sleepingBeauty = 'x=1; while [ $x -le 40 ]; do sleep 0.1 && echo "Welcome $x times" $(( x++ )); done';
|
||||
//
|
||||
// $commandString = <<<EOT
|
||||
// cd projects/dummy-project
|
||||
// ~/.docker/cli-plugins/docker-compose build --no-cache
|
||||
// # $sleepingBeauty
|
||||
// EOT;
|
||||
//
|
||||
// $this->activity = coolifyProcess($commandString, 'testing-host');
|
||||
|
||||
|
||||
$this->isKeepAliveOn = true;
|
||||
}
|
||||
|
||||
public function polling()
|
||||
{
|
||||
$this->activity?->refresh();
|
||||
|
||||
if ($this->activity?->properties['status'] === 'finished') {
|
||||
$this->isKeepAliveOn = false;
|
||||
}
|
||||
}
|
||||
}
|
131
app/Jobs/ExecuteCoolifyProcess.php
Executable file
131
app/Jobs/ExecuteCoolifyProcess.php
Executable file
@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Process\InvokedProcess;
|
||||
use Illuminate\Process\ProcessResult;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Process;
|
||||
use Spatie\Activitylog\Contracts\Activity;
|
||||
|
||||
class ExecuteCoolifyProcess implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
protected $throttleIntervalMS = 500;
|
||||
|
||||
protected $timeStart;
|
||||
|
||||
protected $currentTime;
|
||||
|
||||
protected $lastWriteAt = 0;
|
||||
|
||||
protected string $stdOutIncremental = '';
|
||||
|
||||
protected string $stdErrIncremental = '';
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public Activity $activity,
|
||||
){}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): ?ProcessResult
|
||||
{
|
||||
ray()->clearAll();
|
||||
$this->timeStart = $start = hrtime(true);
|
||||
|
||||
$user = $this->activity->getExtraProperty('user');
|
||||
$destination = $this->activity->getExtraProperty('destination');
|
||||
$port = $this->activity->getExtraProperty('port');
|
||||
$command = $this->activity->getExtraProperty('command');
|
||||
|
||||
$delimiter = 'EOF-COOLIFY-SSH';
|
||||
|
||||
File::chmod(base_path('coolify_id25519'), '0600');
|
||||
|
||||
$sshCommand = 'ssh '
|
||||
. '-i ./coolify_id25519'
|
||||
. '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null '
|
||||
. '-o PasswordAuthentication=no '
|
||||
. "{$user}@{$destination} "
|
||||
. " 'bash -se' << \\$delimiter" . PHP_EOL
|
||||
. $command . PHP_EOL
|
||||
. $delimiter;
|
||||
|
||||
// $sshCommand = "whoami ; pwd ; ls ";
|
||||
|
||||
$process = Process::start(
|
||||
$sshCommand,
|
||||
$this->handleOutput(...),
|
||||
);
|
||||
|
||||
|
||||
$res = $process->wait();
|
||||
|
||||
if (app()->environment('testing')) {
|
||||
return $res;
|
||||
}
|
||||
|
||||
// TODO Why is this not persisting?? Immutable property??
|
||||
$this->activity->properties->put('pid', $process->id());
|
||||
$this->activity->properties->put('exitCode', $res->exitCode());
|
||||
$this->activity->properties->put('stdout', $res->output());
|
||||
$this->activity->properties->put('stderr', $res->errorOutput());
|
||||
$this->activity->save();
|
||||
}
|
||||
|
||||
protected function handleOutput(string $type, string $output)
|
||||
{
|
||||
$this->currentTime = $this->elapsedTime();
|
||||
|
||||
if ($type === 'out') {
|
||||
$this->stdOutIncremental .= $output;
|
||||
} else {
|
||||
$this->stdErrIncremental .= $output;
|
||||
}
|
||||
|
||||
$this->activity->description .= $output;
|
||||
|
||||
if ($this->isAfterLastThrottle()) {
|
||||
// Let's write to database.
|
||||
DB::transaction(function () {
|
||||
$this->activity->save();
|
||||
$this->lastWriteAt = $this->currentTime;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides if it's time to write again to database.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isAfterLastThrottle()
|
||||
{
|
||||
// If DB was never written, then we immediately decide we have to write.
|
||||
if ($this->lastWriteAt === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ($this->currentTime - $this->throttleIntervalMS) > $this->lastWriteAt;
|
||||
}
|
||||
|
||||
protected function elapsedTime(): int
|
||||
{
|
||||
$timeMs = (hrtime(true) - $this->timeStart) / 1_000_000;
|
||||
|
||||
return intval($timeMs);
|
||||
}
|
||||
}
|
45
app/Services/CoolifyProcess.php
Normal file
45
app/Services/CoolifyProcess.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Jobs\ExecuteCoolifyProcess;
|
||||
use Illuminate\Process\ProcessResult;
|
||||
use Spatie\Activitylog\Contracts\Activity;
|
||||
|
||||
class CoolifyProcess
|
||||
{
|
||||
protected Activity $activity;
|
||||
|
||||
// TODO Left 'root' as default user instead of 'coolify' because
|
||||
// there's a task at TODO.md to run docker without sudo
|
||||
public function __construct(
|
||||
protected string $destination,
|
||||
protected string $command,
|
||||
protected ?int $port = 22,
|
||||
protected ?string $user = 'root',
|
||||
){
|
||||
$this->activity = activity()
|
||||
->withProperty('type', 'COOLIFY_PROCESS')
|
||||
->withProperty('user', $this->user)
|
||||
->withProperty('destination', $this->destination)
|
||||
->withProperty('port', $this->port)
|
||||
->withProperty('command', $this->command)
|
||||
->withProperty('status', ProcessStatus::HOLDING)
|
||||
->log("Awaiting to start command...\n\n");
|
||||
}
|
||||
|
||||
public function __invoke(): Activity|ProcessResult
|
||||
{
|
||||
$job = new ExecuteCoolifyProcess($this->activity);
|
||||
|
||||
if (app()->environment('testing')) {
|
||||
return $job->handle();
|
||||
}
|
||||
|
||||
dispatch($job);
|
||||
|
||||
return $this->activity;
|
||||
}
|
||||
|
||||
|
||||
}
|
11
app/Services/ProcessStatus.php
Normal file
11
app/Services/ProcessStatus.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
enum ProcessStatus: string
|
||||
{
|
||||
case HOLDING = 'holding';
|
||||
case IN_PROGRESS = 'in_progress';
|
||||
case FINISHED = 'finished';
|
||||
case ERROR = 'error';
|
||||
}
|
259
app/Services/Ssh.php
Normal file
259
app/Services/Ssh.php
Normal file
@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Closure;
|
||||
use Exception;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
/**
|
||||
* Started from a copy of spatie/ssh
|
||||
*/
|
||||
class Ssh
|
||||
{
|
||||
protected string $user;
|
||||
|
||||
protected string $host;
|
||||
|
||||
protected array $extraOptions = [];
|
||||
|
||||
protected Closure $processConfigurationClosure;
|
||||
|
||||
protected Closure $onOutput;
|
||||
|
||||
public function __construct(string $user, string $host, int $port = null)
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
$this->host = $host;
|
||||
|
||||
if ($port !== null) {
|
||||
$this->usePort($port);
|
||||
}
|
||||
|
||||
$this->processConfigurationClosure = fn(Process $process) => null;
|
||||
|
||||
$this->onOutput = fn($type, $line) => null;
|
||||
}
|
||||
|
||||
public static function create(...$args): self
|
||||
{
|
||||
return new static(...$args);
|
||||
}
|
||||
|
||||
public function usePrivateKey(string $pathToPrivateKey): self
|
||||
{
|
||||
$this->extraOptions['private_key'] = '-i ' . $pathToPrivateKey;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function useJumpHost(string $jumpHost): self
|
||||
{
|
||||
$this->extraOptions['jump_host'] = '-J ' . $jumpHost;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function usePort(int $port): self
|
||||
{
|
||||
if ($port < 0) {
|
||||
throw new Exception('Port must be a positive integer.');
|
||||
}
|
||||
$this->extraOptions['port'] = '-p ' . $port;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function useMultiplexing(string $controlPath, string $controlPersist = '10m'): self
|
||||
{
|
||||
$this->extraOptions['control_master'] = '-o ControlMaster=auto -o ControlPath=' . $controlPath . ' -o ControlPersist=' . $controlPersist;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function configureProcess(Closure $processConfigurationClosure): self
|
||||
{
|
||||
$this->processConfigurationClosure = $processConfigurationClosure;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function onOutput(Closure $onOutput): self
|
||||
{
|
||||
$this->onOutput = $onOutput;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function enableStrictHostKeyChecking(): self
|
||||
{
|
||||
unset($this->extraOptions['enable_strict_check']);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function disableStrictHostKeyChecking(): self
|
||||
{
|
||||
$this->extraOptions['enable_strict_check'] = '-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function enableQuietMode(): self
|
||||
{
|
||||
$this->extraOptions['quiet'] = '-q';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function disableQuietMode(): self
|
||||
{
|
||||
unset($this->extraOptions['quiet']);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function disablePasswordAuthentication(): self
|
||||
{
|
||||
$this->extraOptions['password_authentication'] = '-o PasswordAuthentication=no';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function enablePasswordAuthentication(): self
|
||||
{
|
||||
unset($this->extraOptions['password_authentication']);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addExtraOption(string $option): self
|
||||
{
|
||||
$this->extraOptions[] = $option;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $command
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getExecuteCommand($command): string
|
||||
{
|
||||
$commands = $this->wrapArray($command);
|
||||
|
||||
$extraOptions = implode(' ', $this->getExtraOptions());
|
||||
|
||||
$commandString = implode(PHP_EOL, $commands);
|
||||
|
||||
$delimiter = 'EOF-SPATIE-SSH';
|
||||
|
||||
$target = $this->getTargetForSsh();
|
||||
|
||||
if (in_array($this->host, ['local', 'localhost', '127.0.0.1'])) {
|
||||
return $commandString;
|
||||
}
|
||||
|
||||
return "ssh {$extraOptions} {$target} 'bash -se' << \\$delimiter" . PHP_EOL
|
||||
. $commandString . PHP_EOL
|
||||
. $delimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $command
|
||||
*
|
||||
* @return \Symfony\Component\Process\Process
|
||||
**/
|
||||
public function execute($command): Process
|
||||
{
|
||||
$sshCommand = $this->getExecuteCommand($command);
|
||||
|
||||
return $this->run($sshCommand);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $command
|
||||
*
|
||||
* @return \Symfony\Component\Process\Process
|
||||
*/
|
||||
public function executeAsync($command): Process
|
||||
{
|
||||
$sshCommand = $this->getExecuteCommand($command);
|
||||
|
||||
return $this->run($sshCommand, 'start');
|
||||
}
|
||||
|
||||
public function getDownloadCommand(string $sourcePath, string $destinationPath): string
|
||||
{
|
||||
return "scp {$this->getExtraScpOptions()} {$this->getTargetForScp()}:$sourcePath $destinationPath";
|
||||
}
|
||||
|
||||
public function download(string $sourcePath, string $destinationPath): Process
|
||||
{
|
||||
$downloadCommand = $this->getDownloadCommand($sourcePath, $destinationPath);
|
||||
|
||||
return $this->run($downloadCommand);
|
||||
}
|
||||
|
||||
public function getUploadCommand(string $sourcePath, string $destinationPath): string
|
||||
{
|
||||
return "scp {$this->getExtraScpOptions()} $sourcePath {$this->getTargetForScp()}:$destinationPath";
|
||||
}
|
||||
|
||||
public function upload(string $sourcePath, string $destinationPath): Process
|
||||
{
|
||||
$uploadCommand = $this->getUploadCommand($sourcePath, $destinationPath);
|
||||
|
||||
return $this->run($uploadCommand);
|
||||
}
|
||||
|
||||
protected function getExtraScpOptions(): string
|
||||
{
|
||||
$extraOptions = $this->extraOptions;
|
||||
|
||||
if (isset($extraOptions['port'])) {
|
||||
$extraOptions['port'] = str_replace('-p', '-P', $extraOptions['port']);
|
||||
}
|
||||
|
||||
$extraOptions[] = '-r';
|
||||
|
||||
return implode(' ', array_values($extraOptions));
|
||||
}
|
||||
|
||||
private function getExtraOptions(): array
|
||||
{
|
||||
return array_values($this->extraOptions);
|
||||
}
|
||||
|
||||
protected function wrapArray($arrayOrString): array
|
||||
{
|
||||
return (array)$arrayOrString;
|
||||
}
|
||||
|
||||
protected function run(string $command, string $method = 'run'): Process
|
||||
{
|
||||
$process = Process::fromShellCommandline($command);
|
||||
|
||||
$process->setTimeout(0);
|
||||
|
||||
($this->processConfigurationClosure)($process);
|
||||
|
||||
$process->{$method}($this->onOutput);
|
||||
|
||||
return $process;
|
||||
}
|
||||
|
||||
protected function getTargetForScp(): string
|
||||
{
|
||||
$host = filter_var($this->host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? '[' . $this->host . ']' : $this->host;
|
||||
|
||||
return "{$this->user}@{$host}";
|
||||
}
|
||||
|
||||
protected function getTargetForSsh(): string
|
||||
{
|
||||
return "{$this->user}@{$this->host}";
|
||||
}
|
||||
}
|
24
bootstrap/helpers.php
Normal file
24
bootstrap/helpers.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
use App\Services\CoolifyProcess;
|
||||
use Illuminate\Process\ProcessResult;
|
||||
use Spatie\Activitylog\Contracts\Activity;
|
||||
|
||||
if (! function_exists('coolifyProcess')) {
|
||||
|
||||
/**
|
||||
* Run a Coolify Process, which SSH's into a machine to run the command(s).
|
||||
*
|
||||
*/
|
||||
function coolifyProcess($command, $destination): Activity|ProcessResult
|
||||
{
|
||||
$process = resolve(CoolifyProcess::class, [
|
||||
'destination' => $destination,
|
||||
'command' => $command,
|
||||
]);
|
||||
|
||||
$activityLog = $process();
|
||||
|
||||
return $activityLog;
|
||||
}
|
||||
}
|
@ -9,7 +9,10 @@
|
||||
"guzzlehttp/guzzle": "^7.2",
|
||||
"laravel/framework": "^10.0",
|
||||
"laravel/sanctum": "^3.2",
|
||||
"laravel/tinker": "^2.8"
|
||||
"laravel/tinker": "^2.8",
|
||||
"livewire/livewire": "^2.12",
|
||||
"spatie/laravel-activitylog": "^4.7",
|
||||
"spatie/laravel-ray": "^1.32"
|
||||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.9.1",
|
||||
@ -21,6 +24,9 @@
|
||||
"spatie/laravel-ignition": "^2.0"
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap/helpers.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"App\\": "app/",
|
||||
"Database\\Factories\\": "database/factories/",
|
||||
|
966
composer.lock
generated
966
composer.lock
generated
File diff suppressed because it is too large
Load Diff
108
config/ray.php
Normal file
108
config/ray.php
Normal file
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
/*
|
||||
* This setting controls whether data should be sent to Ray.
|
||||
*
|
||||
* By default, `ray()` will only transmit data in non-production environments.
|
||||
*/
|
||||
'enable' => env('RAY_ENABLED', true),
|
||||
|
||||
/*
|
||||
* When enabled, all cache events will automatically be sent to Ray.
|
||||
*/
|
||||
'send_cache_to_ray' => env('SEND_CACHE_TO_RAY', false),
|
||||
|
||||
/*
|
||||
* When enabled, all things passed to `dump` or `dd`
|
||||
* will be sent to Ray as well.
|
||||
*/
|
||||
'send_dumps_to_ray' => env('SEND_DUMPS_TO_RAY', true),
|
||||
|
||||
/*
|
||||
* When enabled all job events will automatically be sent to Ray.
|
||||
*/
|
||||
'send_jobs_to_ray' => env('SEND_JOBS_TO_RAY', false),
|
||||
|
||||
/*
|
||||
* When enabled, all things logged to the application log
|
||||
* will be sent to Ray as well.
|
||||
*/
|
||||
'send_log_calls_to_ray' => env('SEND_LOG_CALLS_TO_RAY', true),
|
||||
|
||||
/*
|
||||
* When enabled, all queries will automatically be sent to Ray.
|
||||
*/
|
||||
'send_queries_to_ray' => env('SEND_QUERIES_TO_RAY', false),
|
||||
|
||||
/**
|
||||
* When enabled, all duplicate queries will automatically be sent to Ray.
|
||||
*/
|
||||
'send_duplicate_queries_to_ray' => env('SEND_DUPLICATE_QUERIES_TO_RAY', false),
|
||||
|
||||
/*
|
||||
* When enabled, slow queries will automatically be sent to Ray.
|
||||
*/
|
||||
'send_slow_queries_to_ray' => env('SEND_SLOW_QUERIES_TO_RAY', false),
|
||||
|
||||
/**
|
||||
* Queries that are longer than this number of milliseconds will be regarded as slow.
|
||||
*/
|
||||
'slow_query_threshold_in_ms' => env('RAY_SLOW_QUERY_THRESHOLD_IN_MS', 500),
|
||||
|
||||
/*
|
||||
* When enabled, all requests made to this app will automatically be sent to Ray.
|
||||
*/
|
||||
'send_requests_to_ray' => env('SEND_REQUESTS_TO_RAY', false),
|
||||
|
||||
/**
|
||||
* When enabled, all Http Client requests made by this app will be automatically sent to Ray.
|
||||
*/
|
||||
'send_http_client_requests_to_ray' => env('SEND_HTTP_CLIENT_REQUESTS_TO_RAY', false),
|
||||
|
||||
/*
|
||||
* When enabled, all views that are rendered automatically be sent to Ray.
|
||||
*/
|
||||
'send_views_to_ray' => env('SEND_VIEWS_TO_RAY', false),
|
||||
|
||||
/*
|
||||
* When enabled, all exceptions will be automatically sent to Ray.
|
||||
*/
|
||||
'send_exceptions_to_ray' => env('SEND_EXCEPTIONS_TO_RAY', true),
|
||||
|
||||
/*
|
||||
* When enabled, all deprecation notices will be automatically sent to Ray.
|
||||
*/
|
||||
'send_deprecated_notices_to_ray' => env('SEND_DEPRECATED_NOTICES_TO_RAY', false),
|
||||
|
||||
/*
|
||||
* The host used to communicate with the Ray app.
|
||||
* When using Docker on Mac or Windows, you can replace localhost with 'host.docker.internal'
|
||||
* When using Docker on Linux, you can replace localhost with '172.17.0.1'
|
||||
* When using Homestead with the VirtualBox provider, you can replace localhost with '10.0.2.2'
|
||||
* When using Homestead with the Parallels provider, you can replace localhost with '10.211.55.2'
|
||||
*/
|
||||
'host' => env('RAY_HOST', 'host.docker.internal'),
|
||||
|
||||
/*
|
||||
* The port number used to communicate with the Ray app.
|
||||
*/
|
||||
'port' => env('RAY_PORT', 23517),
|
||||
|
||||
/*
|
||||
* Absolute base path for your sites or projects in Homestead,
|
||||
* Vagrant, Docker, or another remote development server.
|
||||
*/
|
||||
'remote_path' => env('RAY_REMOTE_PATH', null),
|
||||
|
||||
/*
|
||||
* Absolute base path for your sites or projects on your local
|
||||
* computer where your IDE or code editor is running on.
|
||||
*/
|
||||
'local_path' => env('RAY_LOCAL_PATH', null),
|
||||
|
||||
/*
|
||||
* When this setting is enabled, the package will not try to format values sent to Ray.
|
||||
*/
|
||||
'always_send_raw_values' => false,
|
||||
];
|
7
coolify_id25519
Normal file
7
coolify_id25519
Normal file
@ -0,0 +1,7 @@
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||
QyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevAAAAJi/QySHv0Mk
|
||||
hwAAAAtzc2gtZWQyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevA
|
||||
AAAECBQw4jg1WRT2IGHMncCiZhURCts2s24HoDS0thHnnRKVuGmoeGq/pojrsyP1pszcNV
|
||||
uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
-----END OPENSSH PRIVATE KEY-----
|
27
database/migrations/2023_03_20_112410_create_activity_log_table.php
Executable file
27
database/migrations/2023_03_20_112410_create_activity_log_table.php
Executable file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class CreateActivityLogTable extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::connection(config('activitylog.database_connection'))->create(config('activitylog.table_name'), function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->string('log_name')->nullable();
|
||||
$table->text('description');
|
||||
$table->nullableMorphs('subject', 'subject');
|
||||
$table->nullableMorphs('causer', 'causer');
|
||||
$table->json('properties')->nullable();
|
||||
$table->timestamps();
|
||||
$table->index('log_name');
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::connection(config('activitylog.database_connection'))->dropIfExists(config('activitylog.table_name'));
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddEventColumnToActivityLogTable extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) {
|
||||
$table->string('event')->nullable()->after('subject_type');
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) {
|
||||
$table->dropColumn('event');
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddBatchUuidColumnToActivityLogTable extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) {
|
||||
$table->uuid('batch_uuid')->nullable()->after('properties');
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::connection(config('activitylog.database_connection'))->table(config('activitylog.table_name'), function (Blueprint $table) {
|
||||
$table->dropColumn('batch_uuid');
|
||||
});
|
||||
}
|
||||
}
|
32
database/migrations/2023_03_20_112810_create_jobs_table.php
Executable file
32
database/migrations/2023_03_20_112810_create_jobs_table.php
Executable file
@ -0,0 +1,32 @@
|
||||
<?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('jobs', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->string('queue')->index();
|
||||
$table->longText('payload');
|
||||
$table->unsignedTinyInteger('attempts');
|
||||
$table->unsignedInteger('reserved_at')->nullable();
|
||||
$table->unsignedInteger('available_at');
|
||||
$table->unsignedInteger('created_at');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('jobs');
|
||||
}
|
||||
};
|
@ -3,8 +3,8 @@ services:
|
||||
php:
|
||||
image: coolify:4
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
context: ./docker/dev
|
||||
ports:
|
||||
- "${APP_PORT:-8000}:80"
|
||||
- "${VITE_PORT:-5173}:${VITE_PORT:-5173}"
|
||||
|
20
docker/dev/Dockerfile
Normal file
20
docker/dev/Dockerfile
Normal file
@ -0,0 +1,20 @@
|
||||
FROM serversideup/php:8.2-fpm-nginx
|
||||
|
||||
ARG NODE_VERSION=18
|
||||
ARG POSTGRES_VERSION=15
|
||||
|
||||
RUN apt-get update \
|
||||
&& curl -sLS https://deb.nodesource.com/setup_$NODE_VERSION.x | bash - \
|
||||
&& apt-get install -y nodejs \
|
||||
&& npm install -g npm
|
||||
|
||||
RUN apt-get install -y php-pgsql openssh-client
|
||||
|
||||
RUN apt-get -y autoremove \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/*
|
||||
|
||||
USER root
|
||||
|
||||
# S6 Overlay config
|
||||
COPY --chmod=755 etc/s6-overlay/ /etc/s6-overlay/
|
2
docker/dev/etc/s6-overlay/s6-rc.d/queue-worker/run
Normal file
2
docker/dev/etc/s6-overlay/s6-rc.d/queue-worker/run
Normal file
@ -0,0 +1,2 @@
|
||||
#!/command/execlineb -P
|
||||
su webuser -c "php /var/www/html/artisan queue:listen --tries=3"
|
1
docker/dev/etc/s6-overlay/s6-rc.d/queue-worker/type
Normal file
1
docker/dev/etc/s6-overlay/s6-rc.d/queue-worker/type
Normal file
@ -0,0 +1 @@
|
||||
longrun
|
3
resources/views/components/button.blade.php
Normal file
3
resources/views/components/button.blade.php
Normal file
@ -0,0 +1,3 @@
|
||||
<button wire:loading.remove {{ $attributes }} class="btn btn-primary rounded-none btn-xs no-animation"> {{ $slot }} </button>
|
||||
|
||||
<button wire:loading class="btn btn-disabled rounded-none btn-xs no-animation"> {{ $slot }}</button>
|
24
resources/views/components/layout.blade.php
Normal file
24
resources/views/components/layout.blade.php
Normal file
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" data-theme="light" class="h-full">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{ $title ?? 'Coolify' }}</title>
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
@vite(['resources/js/app.js', 'resources/css/app.css'])
|
||||
@livewireStyles
|
||||
</head>
|
||||
|
||||
<body class="h-full">
|
||||
@auth
|
||||
<x-navbar />
|
||||
@endauth
|
||||
<main class="h-full pt-10 p-4">
|
||||
{{ $slot }}
|
||||
</main>
|
||||
|
||||
@livewireScripts
|
||||
</body>
|
||||
|
||||
</html>
|
15
resources/views/components/navbar.blade.php
Normal file
15
resources/views/components/navbar.blade.php
Normal file
@ -0,0 +1,15 @@
|
||||
<nav class="flex space-x-4 p-2 fixed right-0">
|
||||
@env('local')
|
||||
<a href="/run-command">Run command</a>
|
||||
{{-- <livewire:create-token /> --}}
|
||||
<a href="/debug">Debug</a>
|
||||
<a href="/debug/logs" target="_blank">Debug Logs</a>
|
||||
{{-- <a href="/debug/inertia">Debug(inertia)</a> --}}
|
||||
@endenv
|
||||
<a href="/">Dashboard</a>
|
||||
<a href="/profile">Profile</a>
|
||||
<form action="/logout" method="POST">
|
||||
@csrf
|
||||
<button type="submit">Logout</button>
|
||||
</form>
|
||||
</nav>
|
5
resources/views/demo.blade.php
Normal file
5
resources/views/demo.blade.php
Normal file
@ -0,0 +1,5 @@
|
||||
<x-layout>
|
||||
|
||||
<livewire:run-command />
|
||||
|
||||
</x-layout>
|
44
resources/views/livewire/run-command.blade.php
Executable file
44
resources/views/livewire/run-command.blade.php
Executable file
@ -0,0 +1,44 @@
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<label for="command">
|
||||
<input class="ring-1" id="command" wire:model="command" type="text"/>
|
||||
</label>
|
||||
<button class="btn btn-success btn-xs rounded-none" wire:click="runCommand">
|
||||
Run command
|
||||
<button>
|
||||
</div>
|
||||
|
||||
@isset($activity?->id)
|
||||
<div>
|
||||
Activity: <span>{{ $activity?->id ?? 'waiting' }}</span>
|
||||
</div>
|
||||
@endisset
|
||||
</div>
|
||||
|
||||
<div class="w-full h-10"></div>
|
||||
|
||||
<pre
|
||||
style="
|
||||
background-color: #FFFFFF;
|
||||
width: 1200px;
|
||||
height: 600px;
|
||||
overflow-y: scroll;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
"
|
||||
placeholder="Build output"
|
||||
@if($isKeepAliveOn || $manualKeepAlive) wire:poll.750ms="polling" @endif
|
||||
>
|
||||
{{ data_get($activity, 'description') }}
|
||||
</pre>
|
||||
|
||||
<div>
|
||||
<input id="manualKeepAlive" name="manualKeepAlive" type="checkbox" wire:model="manualKeepAlive">
|
||||
<label for="manualKeepAlive"> Live content </label>
|
||||
</div>
|
||||
|
||||
@if($isKeepAliveOn || $manualKeepAlive)
|
||||
Polling...
|
||||
@endif
|
||||
</div>
|
@ -13,6 +13,10 @@
|
||||
<h1 class="text-3xl font-bold">
|
||||
Hello from Coolify!
|
||||
</h1>
|
||||
|
||||
<p class="mt-4">
|
||||
<a href="/demo"> See demo </a>
|
||||
</p>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
@ -16,3 +16,5 @@
|
||||
Route::get('/', function () {
|
||||
return view('welcome');
|
||||
});
|
||||
|
||||
Route::view('/demo', 'demo');
|
||||
|
Loading…
Reference in New Issue
Block a user