commit
8a9ad04744
@ -22,8 +22,6 @@ You can ask for guidance anytime on our
|
||||
- Run `spin up` - You can notice that errors will be thrown. Don't worry.
|
||||
- If you see weird permission errors, especially on Mac, run `sudo spin up` instead.
|
||||
|
||||
- Run `./scripts/run setup:dev` - This will generate a secret key for you, delete any existing database layouts, migrate database to the new layout, and seed your database.
|
||||
|
||||
### 4) Start development
|
||||
You can login your Coolify instance at `localhost:8000` with `test@example.com` and `password`.
|
||||
|
||||
@ -31,7 +29,6 @@ Your horizon (Laravel scheduler): `localhost:8000/horizon` - Only reachable if y
|
||||
|
||||
Mails are caught by Mailpit: `localhost:8025`
|
||||
|
||||
|
||||
## New Service Contribution
|
||||
Check out the docs [here](https://coolify.io/docs/how-to-add-a-service).
|
||||
|
||||
|
@ -18,7 +18,7 @@ class UpdateCoolify
|
||||
try {
|
||||
$settings = InstanceSettings::get();
|
||||
ray('Running InstanceAutoUpdateJob');
|
||||
$this->server = Server::find(0)->first();
|
||||
$this->server = Server::find(0);
|
||||
if (!$this->server) {
|
||||
return;
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ class StartService
|
||||
use AsAction;
|
||||
public function handle(Service $service)
|
||||
{
|
||||
ray('Starting service: ' . $service->name);
|
||||
$network = $service->destination->network;
|
||||
$service->saveComposeConfigs();
|
||||
$commands[] = "cd " . $service->workdir();
|
||||
|
@ -10,6 +10,7 @@ class StopService
|
||||
use AsAction;
|
||||
public function handle(Service $service)
|
||||
{
|
||||
ray('Stopping service: ' . $service->name);
|
||||
$applications = $service->applications()->get();
|
||||
foreach ($applications as $application) {
|
||||
instant_remote_process(["docker rm -f {$application->name}-{$service->uuid}"], $service->server);
|
||||
|
25
app/Actions/Shared/PullImage.php
Normal file
25
app/Actions/Shared/PullImage.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Shared;
|
||||
|
||||
use App\Models\Service;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class PullImage
|
||||
{
|
||||
use AsAction;
|
||||
public function handle(Service $resource)
|
||||
{
|
||||
$resource->saveComposeConfigs();
|
||||
|
||||
$commands[] = "cd " . $resource->workdir();
|
||||
$commands[] = "echo 'Saved configuration files to {$resource->workdir()}.'";
|
||||
$commands[] = "docker compose pull";
|
||||
|
||||
$server = data_get($resource, 'server');
|
||||
|
||||
if (!$server) return;
|
||||
|
||||
instant_remote_process($commands, $resource->server);
|
||||
}
|
||||
}
|
33
app/Console/Commands/Dev.php
Normal file
33
app/Console/Commands/Dev.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\InstanceSettings;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\Process;
|
||||
|
||||
class Dev extends Command
|
||||
{
|
||||
protected $signature = 'dev:init';
|
||||
protected $description = 'Init the app in dev mode';
|
||||
|
||||
public function handle()
|
||||
{
|
||||
// Generate APP_KEY if not exists
|
||||
if (empty(env('APP_KEY'))) {
|
||||
echo "Generating APP_KEY.\n";
|
||||
Artisan::call('key:generate');
|
||||
}
|
||||
// Seed database if it's empty
|
||||
$settings = InstanceSettings::find(0);
|
||||
if (!$settings) {
|
||||
echo "Initializing instance, seeding database.\n";
|
||||
Artisan::call('migrate --seed');
|
||||
} else {
|
||||
echo "Instance already initialized.\n";
|
||||
}
|
||||
// Set permissions
|
||||
Process::run(['chmod', '-R', 'o+rwx', '.']);
|
||||
}
|
||||
}
|
@ -99,7 +99,7 @@ class Init extends Command
|
||||
// Cleanup any failed deployments
|
||||
|
||||
try {
|
||||
$halted_deployments = ApplicationDeploymentQueue::where('status', '==', 'in_progress')->get();
|
||||
$halted_deployments = ApplicationDeploymentQueue::where('status', '==', ApplicationDeploymentStatus::IN_PROGRESS)->where('status', '==', ApplicationDeploymentStatus::QUEUED)->get();
|
||||
foreach ($halted_deployments as $deployment) {
|
||||
$deployment->status = ApplicationDeploymentStatus::FAILED->value;
|
||||
$deployment->save();
|
||||
@ -115,16 +115,19 @@ class Init extends Command
|
||||
$applications = Application::all();
|
||||
foreach ($applications as $application) {
|
||||
if (!data_get($application, 'environment')) {
|
||||
echo 'Application without environment' . $application->name . 'deleting\n';
|
||||
echo 'Application without environment: ' . $application->name . ' soft deleting\n';
|
||||
$application->delete();
|
||||
continue;
|
||||
}
|
||||
if (!$application->destination()) {
|
||||
echo 'Application without destination' . $application->name . 'deleting\n';
|
||||
echo 'Application without destination: ' . $application->name . ' soft deleting\n';
|
||||
$application->delete();
|
||||
continue;
|
||||
}
|
||||
if (!data_get($application, 'destination.server')) {
|
||||
echo 'Application without server' . $application->name . 'deleting\n';
|
||||
echo 'Application without server: ' . $application->name . ' soft deleting\n';
|
||||
$application->delete();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
@ -134,16 +137,19 @@ class Init extends Command
|
||||
$postgresqls = StandalonePostgresql::all();
|
||||
foreach ($postgresqls as $postgresql) {
|
||||
if (!data_get($postgresql, 'environment')) {
|
||||
echo 'Postgresql without environment' . $postgresql->name . 'deleting\n';
|
||||
echo 'Postgresql without environment: ' . $postgresql->name . ' soft deleting\n';
|
||||
$postgresql->delete();
|
||||
continue;
|
||||
}
|
||||
if (!$postgresql->destination()) {
|
||||
echo 'Postgresql without destination' . $postgresql->name . 'deleting\n';
|
||||
echo 'Postgresql without destination: ' . $postgresql->name . ' soft deleting\n';
|
||||
$postgresql->delete();
|
||||
continue;
|
||||
}
|
||||
if (!data_get($postgresql, 'destination.server')) {
|
||||
echo 'Postgresql without server' . $postgresql->name . 'deleting\n';
|
||||
echo 'Postgresql without server: ' . $postgresql->name . ' soft deleting\n';
|
||||
$postgresql->delete();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
@ -153,16 +159,19 @@ class Init extends Command
|
||||
$redis = StandaloneRedis::all();
|
||||
foreach ($redis as $redis) {
|
||||
if (!data_get($redis, 'environment')) {
|
||||
echo 'Redis without environment' . $redis->name . 'deleting\n';
|
||||
echo 'Redis without environment: ' . $redis->name . ' soft deleting\n';
|
||||
$redis->delete();
|
||||
continue;
|
||||
}
|
||||
if (!$redis->destination()) {
|
||||
echo 'Redis without destination' . $redis->name . 'deleting\n';
|
||||
echo 'Redis without destination: ' . $redis->name . ' soft deleting\n';
|
||||
$redis->delete();
|
||||
continue;
|
||||
}
|
||||
if (!data_get($redis, 'destination.server')) {
|
||||
echo 'Redis without server' . $redis->name . 'deleting\n';
|
||||
echo 'Redis without server: ' . $redis->name . ' soft deleting\n';
|
||||
$redis->delete();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
@ -173,16 +182,19 @@ class Init extends Command
|
||||
$mongodbs = StandaloneMongodb::all();
|
||||
foreach ($mongodbs as $mongodb) {
|
||||
if (!data_get($mongodb, 'environment')) {
|
||||
echo 'Mongodb without environment' . $mongodb->name . 'deleting\n';
|
||||
echo 'Mongodb without environment: ' . $mongodb->name . ' soft deleting\n';
|
||||
$mongodb->delete();
|
||||
continue;
|
||||
}
|
||||
if (!$mongodb->destination()) {
|
||||
echo 'Mongodb without destination' . $mongodb->name . 'deleting\n';
|
||||
echo 'Mongodb without destination: ' . $mongodb->name . ' soft deleting\n';
|
||||
$mongodb->delete();
|
||||
continue;
|
||||
}
|
||||
if (!data_get($mongodb, 'destination.server')) {
|
||||
echo 'Mongodb without server' . $mongodb->name . 'deleting\n';
|
||||
echo 'Mongodb without server: ' . $mongodb->name . ' soft deleting\n';
|
||||
$mongodb->delete();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
@ -193,16 +205,19 @@ class Init extends Command
|
||||
$mysqls = StandaloneMysql::all();
|
||||
foreach ($mysqls as $mysql) {
|
||||
if (!data_get($mysql, 'environment')) {
|
||||
echo 'Mysql without environment' . $mysql->name . 'deleting\n';
|
||||
echo 'Mysql without environment: ' . $mysql->name . ' soft deleting\n';
|
||||
$mysql->delete();
|
||||
continue;
|
||||
}
|
||||
if (!$mysql->destination()) {
|
||||
echo 'Mysql without destination' . $mysql->name . 'deleting\n';
|
||||
echo 'Mysql without destination: ' . $mysql->name . ' soft deleting\n';
|
||||
$mysql->delete();
|
||||
continue;
|
||||
}
|
||||
if (!data_get($mysql, 'destination.server')) {
|
||||
echo 'Mysql without server' . $mysql->name . 'deleting\n';
|
||||
echo 'Mysql without server: ' . $mysql->name . ' soft deleting\n';
|
||||
$mysql->delete();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
@ -213,16 +228,19 @@ class Init extends Command
|
||||
$mariadbs = StandaloneMariadb::all();
|
||||
foreach ($mariadbs as $mariadb) {
|
||||
if (!data_get($mariadb, 'environment')) {
|
||||
echo 'Mariadb without environment' . $mariadb->name . 'deleting\n';
|
||||
echo 'Mariadb without environment: ' . $mariadb->name . ' soft deleting\n';
|
||||
$mariadb->delete();
|
||||
continue;
|
||||
}
|
||||
if (!$mariadb->destination()) {
|
||||
echo 'Mariadb without destination' . $mariadb->name . 'deleting\n';
|
||||
echo 'Mariadb without destination: ' . $mariadb->name . ' soft deleting\n';
|
||||
$mariadb->delete();
|
||||
continue;
|
||||
}
|
||||
if (!data_get($mariadb, 'destination.server')) {
|
||||
echo 'Mariadb without server' . $mariadb->name . 'deleting\n';
|
||||
echo 'Mariadb without server: ' . $mariadb->name . ' soft deleting\n';
|
||||
$mariadb->delete();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
@ -233,16 +251,19 @@ class Init extends Command
|
||||
$services = Service::all();
|
||||
foreach ($services as $service) {
|
||||
if (!data_get($service, 'environment')) {
|
||||
echo 'Service without environment' . $service->name . 'deleting\n';
|
||||
echo 'Service without environment: ' . $service->name . ' soft deleting\n';
|
||||
$service->delete();
|
||||
continue;
|
||||
}
|
||||
if (!$service->destination()) {
|
||||
echo 'Service without destination' . $service->name . 'deleting\n';
|
||||
echo 'Service without destination: ' . $service->name . ' soft deleting\n';
|
||||
$service->delete();
|
||||
continue;
|
||||
}
|
||||
if (!data_get($service, 'server')) {
|
||||
echo 'Service without server' . $service->name . 'deleting\n';
|
||||
echo 'Service without server: ' . $service->name . ' soft deleting\n';
|
||||
$service->delete();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
@ -252,8 +273,9 @@ class Init extends Command
|
||||
$serviceApplications = ServiceApplication::all();
|
||||
foreach ($serviceApplications as $service) {
|
||||
if (!data_get($service, 'service')) {
|
||||
echo 'ServiceApplication without service' . $service->name . 'deleting\n';
|
||||
echo 'ServiceApplication without service: ' . $service->name . ' soft deleting\n';
|
||||
$service->delete();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
@ -263,8 +285,9 @@ class Init extends Command
|
||||
$serviceDatabases = ServiceDatabase::all();
|
||||
foreach ($serviceDatabases as $service) {
|
||||
if (!data_get($service, 'service')) {
|
||||
echo 'ServiceDatabase without service' . $service->name . 'deleting\n';
|
||||
echo 'ServiceDatabase without service: ' . $service->name . ' soft deleting\n';
|
||||
$service->delete();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
|
@ -39,7 +39,7 @@ class Controller extends BaseController
|
||||
} else {
|
||||
$team = $user->teams()->first();
|
||||
}
|
||||
if (is_null(data_get($user, 'email_verified_at'))){
|
||||
if (is_null(data_get($user, 'email_verified_at'))) {
|
||||
$user->email_verified_at = now();
|
||||
$user->save();
|
||||
}
|
||||
@ -137,16 +137,28 @@ class Controller extends BaseController
|
||||
public function acceptInvitation()
|
||||
{
|
||||
try {
|
||||
$invitation = TeamInvitation::whereUuid(request()->route('uuid'))->firstOrFail();
|
||||
$resetPassword = request()->query('reset-password');
|
||||
$invitationUuid = request()->route('uuid');
|
||||
$invitation = TeamInvitation::whereUuid($invitationUuid)->firstOrFail();
|
||||
$user = User::whereEmail($invitation->email)->firstOrFail();
|
||||
if (auth()->user()->id !== $user->id) {
|
||||
abort(401);
|
||||
}
|
||||
$invitationValid = $invitation->isValid();
|
||||
if ($invitationValid) {
|
||||
if ($resetPassword) {
|
||||
$user->update([
|
||||
'password' => Hash::make($invitationUuid),
|
||||
'force_password_reset' => true
|
||||
]);
|
||||
}
|
||||
if ($user->teams()->where('team_id', $invitation->team->id)->exists()) {
|
||||
$invitation->delete();
|
||||
return redirect()->route('team.index');
|
||||
}
|
||||
$user->teams()->attach($invitation->team->id, ['role' => $invitation->role]);
|
||||
refreshSession($invitation->team);
|
||||
$invitation->delete();
|
||||
if (auth()->user()?->id !== $user->id) {
|
||||
return redirect()->route('login');
|
||||
}
|
||||
refreshSession($invitation->team);
|
||||
return redirect()->route('team.index');
|
||||
} else {
|
||||
abort(401);
|
||||
|
@ -24,7 +24,7 @@ class CheckForcePasswordReset
|
||||
}
|
||||
$force_password_reset = auth()->user()->force_password_reset;
|
||||
if ($force_password_reset) {
|
||||
if ($request->routeIs('auth.force-password-reset') || $request->path() === 'livewire/message/force-password-reset') {
|
||||
if ($request->routeIs('auth.force-password-reset') || $request->path() === 'force-password-reset' || $request->path() === 'livewire/update' || $request->path() === 'logout') {
|
||||
return $next($request);
|
||||
}
|
||||
return redirect()->route('auth.force-password-reset');
|
||||
|
@ -11,6 +11,9 @@ class DecideWhatToDoWithUser
|
||||
{
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
if(auth()?->user()?->currentTeam()){
|
||||
refreshSession(auth()->user()->currentTeam());
|
||||
}
|
||||
if (!auth()->user() || !isCloud() || isInstanceAdmin()) {
|
||||
if (!isCloud() && showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) {
|
||||
return redirect('boarding');
|
||||
|
@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class IsBoardingFlow
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
// ray()->showQueries()->color('orange');
|
||||
if (showBoarding() && !in_array($request->path(), allowedPathsForBoardingAccounts())) {
|
||||
if (Str::startsWith($request->path(), 'invitations')) {
|
||||
return $next($request);
|
||||
}
|
||||
return redirect('boarding');
|
||||
}
|
||||
return $next($request);
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class IsSubscriptionValid
|
||||
{
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
if (isInstanceAdmin()) {
|
||||
return $next($request);
|
||||
}
|
||||
if (!auth()->user() || !isCloud()) {
|
||||
if ($request->path() === 'subscription') {
|
||||
return redirect('/');
|
||||
} else {
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
if (isSubscriptionActive() && $request->path() === 'subscription') {
|
||||
// ray('active subscription Middleware');
|
||||
return redirect('/');
|
||||
}
|
||||
if (isSubscriptionOnGracePeriod()) {
|
||||
// ray('is_subscription_in_grace_period Middleware');
|
||||
return $next($request);
|
||||
}
|
||||
if (!isSubscriptionActive() && !isSubscriptionOnGracePeriod()) {
|
||||
// ray('SubscriptionValid Middleware');
|
||||
if (!in_array($request->path(), allowedPathsForUnsubscribedAccounts())) {
|
||||
if (Str::startsWith($request->path(), 'invitations')) {
|
||||
return $next($request);
|
||||
}
|
||||
return redirect('subscription');
|
||||
} else {
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
return $next($request);
|
||||
}
|
||||
}
|
@ -874,20 +874,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$environment_variables = $this->generate_environment_variables($ports);
|
||||
|
||||
if (data_get($this->application, 'custom_labels')) {
|
||||
if (base64_encode(base64_decode(data_get($this->application, 'custom_labels'), true)) === data_get($this->application, 'custom_labels')) {
|
||||
ray('custom_labels is base64 encoded');
|
||||
} else {
|
||||
ray('custom_labels is not base64 encoded');
|
||||
$this->application->custom_labels = str($this->application->custom_labels)->replace(',', "\n");
|
||||
$this->application->custom_labels = base64_encode(data_get($this->application, 'custom_labels'));
|
||||
$this->application->save();
|
||||
}
|
||||
|
||||
if (mb_detect_encoding(base64_decode($this->application->custom_labels), 'ASCII', true) === false) {
|
||||
ray('custom_labels contains non-ascii characters');
|
||||
$this->application->custom_labels = base64_encode(str(implode(",", generateLabelsApplication($this->application, $this->preview)))->replace(',', "\n"));
|
||||
$this->application->save();
|
||||
}
|
||||
$this->application->parseContainerLabels();
|
||||
$labels = collect(preg_split("/\r\n|\n|\r/", base64_decode($this->application->custom_labels)));
|
||||
$labels = $labels->filter(function ($value, $key) {
|
||||
return !Str::startsWith($value, 'coolify.');
|
||||
@ -1354,10 +1341,10 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
}
|
||||
queue_next_deployment($this->application);
|
||||
if ($status === ApplicationDeploymentStatus::FINISHED->value) {
|
||||
$this->application->environment->project->team->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview));
|
||||
$this->application->environment->project->team?->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview));
|
||||
}
|
||||
if ($status === ApplicationDeploymentStatus::FAILED->value) {
|
||||
$this->application->environment->project->team->notify(new DeploymentFailed($this->application, $this->deployment_uuid, $this->preview));
|
||||
$this->application->environment->project->team?->notify(new DeploymentFailed($this->application, $this->deployment_uuid, $this->preview));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,19 +63,19 @@ class CheckLogDrainContainerJob implements ShouldQueue, ShouldBeEncrypted
|
||||
Sleep::for(10)->seconds();
|
||||
if ($this->healthcheck()) {
|
||||
if ($this->server->log_drain_notification_sent) {
|
||||
$this->server->team->notify(new ContainerRestarted('Coolify Log Drainer', $this->server));
|
||||
$this->server->team?->notify(new ContainerRestarted('Coolify Log Drainer', $this->server));
|
||||
$this->server->update(['log_drain_notification_sent' => false]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!$this->server->log_drain_notification_sent) {
|
||||
ray('Log drain container still unhealthy. Sending notification...');
|
||||
$this->server->team->notify(new ContainerStopped('Coolify Log Drainer', $this->server, null));
|
||||
$this->server->team?->notify(new ContainerStopped('Coolify Log Drainer', $this->server, null));
|
||||
$this->server->update(['log_drain_notification_sent' => true]);
|
||||
}
|
||||
} else {
|
||||
if ($this->server->log_drain_notification_sent) {
|
||||
$this->server->team->notify(new ContainerRestarted('Coolify Log Drainer', $this->server));
|
||||
$this->server->team?->notify(new ContainerRestarted('Coolify Log Drainer', $this->server));
|
||||
$this->server->update(['log_drain_notification_sent' => false]);
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Sleep;
|
||||
|
||||
class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
{
|
||||
@ -37,10 +38,8 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
$this->handle();
|
||||
}
|
||||
|
||||
|
||||
public function handle()
|
||||
{
|
||||
// ray("checking container statuses for {$this->server->id}");
|
||||
try {
|
||||
if (!$this->server->isServerReady()) {
|
||||
return;
|
||||
|
@ -286,7 +286,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
if ($this->backup->save_s3) {
|
||||
$this->upload_to_s3();
|
||||
}
|
||||
$this->team->notify(new BackupSuccess($this->backup, $this->database));
|
||||
$this->team?->notify(new BackupSuccess($this->backup, $this->database));
|
||||
$this->backup_log->update([
|
||||
'status' => 'success',
|
||||
'message' => $this->backup_output,
|
||||
@ -302,7 +302,7 @@ class DatabaseBackupJob implements ShouldQueue, ShouldBeEncrypted
|
||||
]);
|
||||
}
|
||||
send_internal_notification('DatabaseBackupJob failed with: ' . $e->getMessage());
|
||||
$this->team->notify(new BackupFailed($this->backup, $this->database, $this->backup_output));
|
||||
$this->team?->notify(new BackupFailed($this->backup, $this->database, $this->backup_output));
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
@ -32,9 +32,10 @@ class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted
|
||||
try {
|
||||
$server = $this->resource->destination->server;
|
||||
if (!$server->isFunctional()) {
|
||||
$this->resource->delete();
|
||||
$this->resource->forceDelete();
|
||||
return 'Server is not functional';
|
||||
}
|
||||
$this->resource->delete();
|
||||
switch ($this->resource->type()) {
|
||||
case 'application':
|
||||
StopApplication::run($this->resource);
|
||||
@ -56,10 +57,9 @@ class DeleteResourceJob implements ShouldQueue, ShouldBeEncrypted
|
||||
break;
|
||||
}
|
||||
if ($this->resource->type() === 'service') {
|
||||
$this->resource->delete();
|
||||
DeleteService::dispatch($this->resource);
|
||||
} else {
|
||||
$this->resource->delete();
|
||||
$this->resource->forceDelete();
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
send_internal_notification('ContainerStoppingJob failed with: ' . $e->getMessage());
|
||||
|
@ -54,7 +54,7 @@ class ServerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
||||
} else {
|
||||
$this->server->high_disk_usage_notification_sent = true;
|
||||
$this->server->save();
|
||||
$this->server->team->notify(new HighDiskUsage($this->server, $this->disk_usage, $this->server->settings->cleanup_after_percentage));
|
||||
$this->server->team?->notify(new HighDiskUsage($this->server, $this->disk_usage, $this->server->settings->cleanup_after_percentage));
|
||||
}
|
||||
} else {
|
||||
DockerCleanupJob::dispatchSync($this->server);
|
||||
|
@ -52,7 +52,7 @@ class DiscordSettings extends Component
|
||||
|
||||
public function sendTestNotification()
|
||||
{
|
||||
$this->team->notify(new Test());
|
||||
$this->team?->notify(new Test());
|
||||
$this->dispatch('success', 'Test notification sent.');
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ class EmailSettings extends Component
|
||||
}
|
||||
public function sendTestNotification()
|
||||
{
|
||||
$this->team->notify(new Test($this->emails));
|
||||
$this->team?->notify(new Test($this->emails));
|
||||
$this->dispatch('success', 'Test Email sent successfully.');
|
||||
}
|
||||
public function instantSaveInstance()
|
||||
|
@ -58,7 +58,7 @@ class TelegramSettings extends Component
|
||||
|
||||
public function sendTestNotification()
|
||||
{
|
||||
$this->team->notify(new Test());
|
||||
$this->team?->notify(new Test());
|
||||
$this->dispatch('success', 'Test notification sent.');
|
||||
}
|
||||
}
|
||||
|
@ -109,24 +109,7 @@ class General extends Component
|
||||
$this->application->isConfigurationChanged(true);
|
||||
}
|
||||
$this->isConfigurationChanged = $this->application->isConfigurationChanged();
|
||||
|
||||
if (data_get($this->application, 'custom_labels')) {
|
||||
if (base64_encode(base64_decode(data_get($this->application, 'custom_labels'), true)) === data_get($this->application, 'custom_labels')) {
|
||||
ray('custom_labels is base64 encoded');
|
||||
} else {
|
||||
ray('custom_labels is not base64 encoded');
|
||||
$this->application->custom_labels = str($this->application->custom_labels)->replace(',', "\n");
|
||||
$this->application->custom_labels = base64_encode(data_get($this->application, 'custom_labels'));
|
||||
$this->application->save();
|
||||
}
|
||||
$this->customLabels = base64_decode(data_get($this->application, 'custom_labels'));
|
||||
// // Fix for non-ascii characters
|
||||
if (mb_detect_encoding($this->customLabels, 'ASCII', true) === false) {
|
||||
ray('custom_labels contains non-ascii characters');
|
||||
$this->resetDefaultLabels(false);
|
||||
}
|
||||
}
|
||||
|
||||
$this->customLabels = $this->application->parseContainerLabels();
|
||||
$this->initialDockerComposeLocation = $this->application->docker_compose_location;
|
||||
$this->checkLabelUpdates();
|
||||
}
|
||||
|
@ -19,12 +19,14 @@ class CloneProject extends Component
|
||||
public $servers;
|
||||
public ?Environment $environment = null;
|
||||
public ?int $selectedServer = null;
|
||||
public ?int $selectedDestination = null;
|
||||
public ?Server $server = null;
|
||||
public $resources = [];
|
||||
public string $newProjectName = '';
|
||||
|
||||
protected $messages = [
|
||||
'selectedServer' => 'Please select a server.',
|
||||
'selectedDestination' => 'Please select a server & destination.',
|
||||
'newProjectName' => 'Please enter a name for the new project.',
|
||||
];
|
||||
public function mount($project_uuid)
|
||||
@ -34,7 +36,7 @@ class CloneProject extends Component
|
||||
$this->environment = $this->project->environments->where('name', $this->environment_name)->first();
|
||||
$this->project_id = $this->project->id;
|
||||
$this->servers = currentTeam()->servers;
|
||||
$this->newProjectName = $this->project->name . ' (clone)';
|
||||
$this->newProjectName = str($this->project->name . '-clone-' . (string)new Cuid2(7))->slug();
|
||||
}
|
||||
|
||||
public function render()
|
||||
@ -42,9 +44,10 @@ class CloneProject extends Component
|
||||
return view('livewire.project.clone-project');
|
||||
}
|
||||
|
||||
public function selectServer($server_id)
|
||||
public function selectServer($server_id, $destination_id)
|
||||
{
|
||||
$this->selectedServer = $server_id;
|
||||
$this->selectedDestination = $destination_id;
|
||||
$this->server = $this->servers->where('id', $server_id)->first();
|
||||
}
|
||||
|
||||
@ -52,7 +55,7 @@ class CloneProject extends Component
|
||||
{
|
||||
try {
|
||||
$this->validate([
|
||||
'selectedServer' => 'required',
|
||||
'selectedDestination' => 'required',
|
||||
'newProjectName' => 'required',
|
||||
]);
|
||||
$foundProject = Project::where('name', $this->newProjectName)->first();
|
||||
@ -81,7 +84,8 @@ class CloneProject extends Component
|
||||
'fqdn' => generateFqdn($this->server, $uuid),
|
||||
'status' => 'exited',
|
||||
'environment_id' => $newEnvironment->id,
|
||||
'destination_id' => $this->selectedServer,
|
||||
// This is not correct, but we need to set it to something
|
||||
'destination_id' => $this->selectedDestination,
|
||||
]);
|
||||
$newApplication->save();
|
||||
$environmentVaribles = $application->environment_variables()->get();
|
||||
@ -107,7 +111,7 @@ class CloneProject extends Component
|
||||
'status' => 'exited',
|
||||
'started_at' => null,
|
||||
'environment_id' => $newEnvironment->id,
|
||||
'destination_id' => $this->selectedServer,
|
||||
'destination_id' => $this->selectedDestination,
|
||||
]);
|
||||
$newDatabase->save();
|
||||
$environmentVaribles = $database->environment_variables()->get();
|
||||
@ -133,7 +137,7 @@ class CloneProject extends Component
|
||||
$newService = $service->replicate()->fill([
|
||||
'uuid' => $uuid,
|
||||
'environment_id' => $newEnvironment->id,
|
||||
'destination_id' => $this->selectedServer,
|
||||
'destination_id' => $this->selectedDestination,
|
||||
]);
|
||||
$newService->save();
|
||||
foreach ($newService->applications() as $application) {
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Livewire\Project\Service;
|
||||
|
||||
use App\Actions\Shared\PullImage;
|
||||
use App\Actions\Service\StartService;
|
||||
use App\Actions\Service\StopService;
|
||||
use App\Events\ServiceStatusChanged;
|
||||
@ -69,4 +70,18 @@ class Navbar extends Component
|
||||
}
|
||||
ServiceStatusChanged::dispatch();
|
||||
}
|
||||
public function restart()
|
||||
{
|
||||
$this->checkDeployments();
|
||||
if ($this->isDeploymentProgress) {
|
||||
$this->dispatch('error', 'There is a deployment in progress.');
|
||||
return;
|
||||
}
|
||||
PullImage::run($this->service);
|
||||
$this->dispatch('image-pulled');
|
||||
StopService::run($this->service);
|
||||
$this->service->parse();
|
||||
$activity = StartService::run($this->service);
|
||||
$this->dispatch('newMonitorActivity', $activity->id);
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ class Email extends Component
|
||||
|
||||
public function sendTestNotification()
|
||||
{
|
||||
$this->settings->notify(new Test($this->emails));
|
||||
$this->settings?->notify(new Test($this->emails));
|
||||
$this->dispatch('success', 'Test email sent.');
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,10 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\ApplicationDeploymentStatus;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Collection;
|
||||
use Spatie\Activitylog\Models\Activity;
|
||||
use Illuminate\Support\Str;
|
||||
@ -13,6 +15,7 @@ use Visus\Cuid2\Cuid2;
|
||||
|
||||
class Application extends BaseModel
|
||||
{
|
||||
use SoftDeletes;
|
||||
protected $guarded = [];
|
||||
|
||||
protected static function booted()
|
||||
@ -338,7 +341,7 @@ class Application extends BaseModel
|
||||
}
|
||||
public function isDeploymentInprogress()
|
||||
{
|
||||
$deployments = ApplicationDeploymentQueue::where('application_id', $this->id)->where('status', 'in_progress')->count();
|
||||
$deployments = ApplicationDeploymentQueue::where('application_id', $this->id)->where('status', ApplicationDeploymentStatus::IN_PROGRESS)->where('status', ApplicationDeploymentStatus::QUEUED)->count();
|
||||
if ($deployments > 0) {
|
||||
return true;
|
||||
}
|
||||
@ -1024,4 +1027,24 @@ class Application extends BaseModel
|
||||
];
|
||||
}
|
||||
}
|
||||
function parseContainerLabels(?ApplicationPreview $preview = null)
|
||||
{
|
||||
$customLabels = data_get($this, 'custom_labels');
|
||||
if (!$customLabels) {
|
||||
return;
|
||||
}
|
||||
if (base64_encode(base64_decode($customLabels, true)) !== $customLabels) {
|
||||
ray('custom_labels is not base64 encoded');
|
||||
$this->custom_labels = str($customLabels)->replace(',', "\n");
|
||||
$this->custom_labels = base64_encode($customLabels);
|
||||
}
|
||||
$customLabels = base64_decode($this->custom_labels);
|
||||
if (mb_detect_encoding($customLabels, 'ASCII', true) === false) {
|
||||
ray('custom_labels contains non-ascii characters');
|
||||
$customLabels = str(implode(",", generateLabelsApplication($this, $preview)))->replace(',', "\n");
|
||||
}
|
||||
$this->custom_labels = base64_encode($customLabels);
|
||||
$this->save();
|
||||
return $customLabels;
|
||||
}
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ class Server extends BaseModel
|
||||
if ($serverUptimeCheckNumber >= $serverUptimeCheckNumberMax) {
|
||||
if ($this->unreachable_notification_sent === false) {
|
||||
ray('Server unreachable, sending notification...');
|
||||
$this->team->notify(new Unreachable($this));
|
||||
$this->team?->notify(new Unreachable($this));
|
||||
$this->update(['unreachable_notification_sent' => true]);
|
||||
}
|
||||
$this->settings()->update([
|
||||
@ -401,7 +401,7 @@ class Server extends BaseModel
|
||||
}
|
||||
|
||||
if (data_get($server, 'unreachable_notification_sent') === true) {
|
||||
$server->team->notify(new Revived($server));
|
||||
$server->team?->notify(new Revived($server));
|
||||
$server->update(['unreachable_notification_sent' => false]);
|
||||
}
|
||||
|
||||
|
@ -4,11 +4,12 @@ namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class ServiceApplication extends BaseModel
|
||||
{
|
||||
use HasFactory;
|
||||
use HasFactory, SoftDeletes;
|
||||
protected $guarded = [];
|
||||
|
||||
protected static function booted()
|
||||
|
@ -3,10 +3,11 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class ServiceDatabase extends BaseModel
|
||||
{
|
||||
use HasFactory;
|
||||
use HasFactory, SoftDeletes;
|
||||
protected $guarded = [];
|
||||
|
||||
protected static function booted()
|
||||
|
@ -6,10 +6,11 @@ use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class StandaloneMariadb extends BaseModel
|
||||
{
|
||||
use HasFactory;
|
||||
use HasFactory,SoftDeletes;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $casts = [
|
||||
|
@ -5,10 +5,11 @@ namespace App\Models;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class StandaloneMongodb extends BaseModel
|
||||
{
|
||||
use HasFactory;
|
||||
use HasFactory, SoftDeletes;
|
||||
protected $guarded = [];
|
||||
|
||||
protected static function booted()
|
||||
|
@ -5,10 +5,11 @@ namespace App\Models;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class StandaloneMysql extends BaseModel
|
||||
{
|
||||
use HasFactory;
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $casts = [
|
||||
|
@ -5,10 +5,11 @@ namespace App\Models;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class StandalonePostgresql extends BaseModel
|
||||
{
|
||||
use HasFactory;
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $guarded = [];
|
||||
protected $casts = [
|
||||
|
@ -5,10 +5,11 @@ namespace App\Models;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class StandaloneRedis extends BaseModel
|
||||
{
|
||||
use HasFactory;
|
||||
use HasFactory, SoftDeletes;
|
||||
protected $guarded = [];
|
||||
|
||||
protected static function booted()
|
||||
|
@ -98,7 +98,7 @@ class User extends Authenticatable implements SendsEmail
|
||||
}
|
||||
public function sendPasswordResetNotification($token): void
|
||||
{
|
||||
$this->notify(new TransactionalEmailsResetPassword($token));
|
||||
$this?->notify(new TransactionalEmailsResetPassword($token));
|
||||
}
|
||||
|
||||
public function isAdmin()
|
||||
|
@ -128,11 +128,6 @@ function allowedPathsForUnsubscribedAccounts()
|
||||
'logout',
|
||||
'waitlist',
|
||||
'force-password-reset',
|
||||
// 'livewire/message/force-password-reset',
|
||||
// 'livewire/message/check-license',
|
||||
// 'livewire/message/switch-team',
|
||||
// 'livewire/message/subscription.pricing-plans',
|
||||
// 'livewire/message/help',
|
||||
'livewire/update'
|
||||
];
|
||||
}
|
||||
@ -141,8 +136,6 @@ function allowedPathsForBoardingAccounts()
|
||||
return [
|
||||
...allowedPathsForUnsubscribedAccounts(),
|
||||
'boarding',
|
||||
// 'livewire/message/boarding.index',
|
||||
// 'livewire/message/activity-monitor',
|
||||
'livewire/update'
|
||||
];
|
||||
}
|
||||
@ -151,9 +144,6 @@ function allowedPathsForInvalidAccounts() {
|
||||
'logout',
|
||||
'verify',
|
||||
'force-password-reset',
|
||||
// 'livewire/message/force-password-reset',
|
||||
// 'livewire/message/verify-email',
|
||||
// 'livewire/message/help',
|
||||
'livewire/update'
|
||||
];
|
||||
}
|
||||
|
@ -3,11 +3,11 @@
|
||||
return [
|
||||
|
||||
// @see https://docs.sentry.io/product/sentry-basics/dsn-explainer/
|
||||
'dsn' => 'https://bea22abf110618b07252032aa2e07859@o1082494.ingest.sentry.io/4505347448045568',
|
||||
'dsn' => 'https://1bbc8f762199a52aee39196adb3e8d1a@o1082494.ingest.sentry.io/4505347448045568',
|
||||
|
||||
// The release version of your application
|
||||
// Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD'))
|
||||
'release' => '4.0.0-beta.161',
|
||||
'release' => '4.0.0-beta.162',
|
||||
// When left empty or `null` the Laravel environment will be used
|
||||
'environment' => config('app.env'),
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
<?php
|
||||
|
||||
return '4.0.0-beta.161';
|
||||
return '4.0.0-beta.162';
|
||||
|
@ -1,23 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\StandaloneMongodb>
|
||||
*/
|
||||
class StandaloneMongodbFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
}
|
71
database/migrations/2023_12_13_110214_add_soft_deletes.php
Normal file
71
database/migrations/2023_12_13_110214_add_soft_deletes.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('applications', function (Blueprint $table) {
|
||||
$table->softDeletes();
|
||||
});
|
||||
Schema::table('standalone_postgresqls', function (Blueprint $table) {
|
||||
$table->softDeletes();
|
||||
});
|
||||
Schema::table('standalone_redis', function (Blueprint $table) {
|
||||
$table->softDeletes();
|
||||
});
|
||||
Schema::table('standalone_mongodbs', function (Blueprint $table) {
|
||||
$table->softDeletes();
|
||||
});
|
||||
Schema::table('standalone_mysqls', function (Blueprint $table) {
|
||||
$table->softDeletes();
|
||||
});
|
||||
Schema::table('standalone_mariadbs', function (Blueprint $table) {
|
||||
$table->softDeletes();
|
||||
});
|
||||
Schema::table('service_applications', function (Blueprint $table) {
|
||||
$table->softDeletes();
|
||||
});
|
||||
Schema::table('service_databases', function (Blueprint $table) {
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('applications', function (Blueprint $table) {
|
||||
$table->dropSoftDeletes();
|
||||
});
|
||||
Schema::table('standalone_postgresqls', function (Blueprint $table) {
|
||||
$table->dropSoftDeletes();
|
||||
});
|
||||
Schema::table('standalone_redis', function (Blueprint $table) {
|
||||
$table->dropSoftDeletes();
|
||||
});
|
||||
Schema::table('standalone_mongodbs', function (Blueprint $table) {
|
||||
$table->dropSoftDeletes();
|
||||
});
|
||||
Schema::table('standalone_mysqls', function (Blueprint $table) {
|
||||
$table->dropSoftDeletes();
|
||||
});
|
||||
Schema::table('standalone_mariadbs', function (Blueprint $table) {
|
||||
$table->dropSoftDeletes();
|
||||
});
|
||||
Schema::table('service_applications', function (Blueprint $table) {
|
||||
$table->dropSoftDeletes();
|
||||
});
|
||||
Schema::table('service_databases', function (Blueprint $table) {
|
||||
$table->dropSoftDeletes();
|
||||
});
|
||||
|
||||
}
|
||||
};
|
@ -56,7 +56,7 @@ services:
|
||||
ports:
|
||||
- "${FORWARD_SOKETI_PORT:-6001}:6001"
|
||||
environment:
|
||||
SOKETI_DEBUG: "true"
|
||||
SOKETI_DEBUG: "false"
|
||||
SOKETI_DEFAULT_APP_ID: "${PUSHER_APP_ID:-coolify}"
|
||||
SOKETI_DEFAULT_APP_KEY: "${PUSHER_APP_KEY:-coolify}"
|
||||
SOKETI_DEFAULT_APP_SECRET: "${PUSHER_APP_SECRET:-coolify}"
|
||||
@ -70,6 +70,8 @@ services:
|
||||
volumes:
|
||||
- .:/var/www/html:cached
|
||||
command: sh -c "npm install && npm run dev"
|
||||
networks:
|
||||
- coolify
|
||||
testing-host:
|
||||
<<: *testing-host-base
|
||||
container_name: coolify-testing-host
|
||||
@ -77,15 +79,8 @@ services:
|
||||
- /:/host
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- /data/coolify/:/data/coolify
|
||||
# - coolify-data-dev:/data/coolify
|
||||
# remote-host:
|
||||
# <<: *testing-host-base
|
||||
# container_name: coolify-remote-host
|
||||
# volumes:
|
||||
# - /:/host
|
||||
# - /var/run/docker.sock:/var/run/docker.sock
|
||||
# - /data/coolify/:/data/coolify
|
||||
# # - coolify-data-dev:/data/coolify
|
||||
networks:
|
||||
- coolify
|
||||
mailpit:
|
||||
image: "axllent/mailpit:latest"
|
||||
container_name: coolify-mail
|
||||
@ -115,3 +110,9 @@ volumes:
|
||||
coolify-pg-data-dev:
|
||||
coolify-redis-data-dev:
|
||||
coolify-minio-data-dev:
|
||||
|
||||
|
||||
networks:
|
||||
coolify:
|
||||
name: coolify
|
||||
external: false
|
||||
|
@ -2,7 +2,7 @@ FROM serversideup/php:8.2-fpm-nginx
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
# https://github.com/cloudflare/cloudflared/releases
|
||||
ARG CLOUDFLARED_VERSION=2023.8.2
|
||||
ARG CLOUDFLARED_VERSION=2023.10.0
|
||||
|
||||
ARG POSTGRES_VERSION=15
|
||||
RUN apt-get update
|
||||
|
1
docker/dev-ssu/etc/s6-overlay/s6-rc.d/init-setup/type
Normal file
1
docker/dev-ssu/etc/s6-overlay/s6-rc.d/init-setup/type
Normal file
@ -0,0 +1 @@
|
||||
oneshot
|
5
docker/dev-ssu/etc/s6-overlay/s6-rc.d/init-setup/up
Normal file
5
docker/dev-ssu/etc/s6-overlay/s6-rc.d/init-setup/up
Normal file
@ -0,0 +1,5 @@
|
||||
#!/command/execlineb -P
|
||||
foreground { composer -d /var/www/html/ install }
|
||||
foreground { php /var/www/html/artisan migrate --step }
|
||||
foreground { php /var/www/html/artisan dev:init }
|
||||
|
@ -15,7 +15,7 @@ FROM serversideup/php:8.2-fpm-nginx
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
# https://github.com/cloudflare/cloudflared/releases
|
||||
ARG CLOUDFLARED_VERSION=2023.8.2
|
||||
ARG CLOUDFLARED_VERSION=2023.10.0
|
||||
ARG POSTGRES_VERSION=15
|
||||
|
||||
WORKDIR /var/www/html
|
||||
|
@ -1,3 +1,3 @@
|
||||
#!/command/execlineb -P
|
||||
s6-setuidgid webuser
|
||||
php /var/www/html/artisan app:init
|
||||
php /var/www/html/artisan app:init --cleanup
|
||||
|
@ -5,25 +5,32 @@
|
||||
html {
|
||||
@apply text-neutral-400;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply text-sm antialiased scrollbar;
|
||||
}
|
||||
|
||||
button[isError] {
|
||||
@apply bg-red-600 hover:bg-red-700;
|
||||
}
|
||||
|
||||
.scrollbar {
|
||||
@apply scrollbar-thumb-coollabs-100 scrollbar-track-coolgray-200 scrollbar-w-2;
|
||||
}
|
||||
|
||||
.main {
|
||||
@apply pt-4 pl-24 pr-10 mx-auto;
|
||||
}
|
||||
|
||||
.custom-modal {
|
||||
@apply flex flex-col gap-2 px-8 py-4 border bg-base-100 border-coolgray-200;
|
||||
}
|
||||
|
||||
.label-text,
|
||||
label {
|
||||
@apply text-neutral-400;
|
||||
}
|
||||
|
||||
.navbar-main {
|
||||
@apply flex items-end gap-6 py-2 border-b-2 border-solid border-coolgray-200;
|
||||
}
|
||||
@ -31,89 +38,117 @@ label {
|
||||
.loading {
|
||||
@apply w-4 text-warning;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@apply text-3xl font-bold text-white;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply text-2xl font-bold text-white;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@apply text-xl font-bold text-white;
|
||||
}
|
||||
|
||||
h4 {
|
||||
@apply text-base font-bold text-white;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply text-neutral-400 hover:text-white link link-hover hover:bg-transparent;
|
||||
}
|
||||
|
||||
.kbd-custom {
|
||||
@apply px-2 text-xs border border-dashed rounded border-neutral-700 text-warning;
|
||||
}
|
||||
|
||||
.icon {
|
||||
@apply w-6 h-6;
|
||||
}
|
||||
|
||||
.icon:hover {
|
||||
@apply text-white;
|
||||
}
|
||||
|
||||
.box {
|
||||
@apply flex p-2 transition-colors cursor-pointer min-h-[4rem] bg-coolgray-100 hover:bg-coollabs-100 hover:text-white hover:no-underline min-w-[24rem];
|
||||
}
|
||||
|
||||
.box-without-bg {
|
||||
@apply flex p-2 transition-colors h-16 min-h-full hover:text-white hover:no-underline min-h-[4rem];
|
||||
@apply flex p-2 transition-colors min-h-full hover:text-white hover:no-underline min-h-[4rem];
|
||||
}
|
||||
|
||||
.description {
|
||||
@apply pt-2 text-xs font-bold text-neutral-500 group-hover:text-white;
|
||||
}
|
||||
|
||||
.lds-heart {
|
||||
animation: lds-heart 1.2s infinite cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
}
|
||||
|
||||
@keyframes lds-heart {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
5% {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
39% {
|
||||
transform: scale(0.85);
|
||||
}
|
||||
|
||||
45% {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
60% {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
}
|
||||
|
||||
.bg-coollabs-gradient {
|
||||
@apply text-transparent text-white bg-gradient-to-r from-purple-500 via-pink-500 to-red-500;
|
||||
}
|
||||
|
||||
.text-helper {
|
||||
@apply inline-block font-bold text-warning;
|
||||
}
|
||||
|
||||
table {
|
||||
@apply min-w-full divide-y divide-coolgray-200;
|
||||
}
|
||||
|
||||
thead {
|
||||
@apply uppercase;
|
||||
}
|
||||
|
||||
tbody {
|
||||
@apply divide-y divide-coolgray-200;
|
||||
}
|
||||
|
||||
tr {
|
||||
@apply text-neutral-400;
|
||||
}
|
||||
|
||||
tr th {
|
||||
@apply px-3 py-3.5 text-left text-white;
|
||||
}
|
||||
|
||||
tr th:first-child {
|
||||
@apply py-3.5 pl-4 pr-3 sm:pl-6;
|
||||
}
|
||||
|
||||
tr td {
|
||||
@apply px-3 py-4 whitespace-nowrap;
|
||||
}
|
||||
|
||||
tr td:first-child {
|
||||
@apply pl-4 pr-3 font-bold sm:pl-6;
|
||||
}
|
||||
@ -121,12 +156,15 @@ tr td:first-child {
|
||||
.buyme {
|
||||
@apply block px-3 py-2 mt-10 text-sm font-semibold leading-6 text-center text-white rounded-md shadow-sm bg-coolgray-200 hover:bg-coolgray-300 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-coolgray-200 hover:no-underline;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
@apply pt-2 pb-10;
|
||||
}
|
||||
|
||||
.fullscreen {
|
||||
@apply fixed top-0 left-0 w-full h-full z-[9999] bg-coolgray-100 overflow-y-auto scrollbar pb-4 ;
|
||||
@apply fixed top-0 left-0 w-full h-full z-[9999] bg-coolgray-100 overflow-y-auto scrollbar pb-4;
|
||||
}
|
||||
|
||||
input.input-sm {
|
||||
@apply pr-10;
|
||||
}
|
||||
|
@ -24,20 +24,20 @@
|
||||
<form action="/login" method="POST" class="flex flex-col gap-2">
|
||||
@csrf
|
||||
@env('local')
|
||||
<x-forms.input value="test@example.com" type="email" name="email" required
|
||||
label="{{ __('input.email') }}" autofocus />
|
||||
<x-forms.input value="test@example.com" type="email" name="email" required
|
||||
label="{{ __('input.email') }}" autofocus />
|
||||
|
||||
<x-forms.input value="password" type="password" name="password" required
|
||||
label="{{ __('input.password') }}" />
|
||||
<a href="/forgot-password" class="text-xs">
|
||||
{{ __('auth.forgot_password') }}?
|
||||
</a>
|
||||
@else
|
||||
<x-forms.input type="email" name="email" required label="{{ __('input.email') }}" autofocus />
|
||||
<x-forms.input type="password" name="password" required label="{{ __('input.password') }}" />
|
||||
<a href="/forgot-password" class="text-xs">
|
||||
{{ __('auth.forgot_password') }}?
|
||||
</a>
|
||||
<x-forms.input value="password" type="password" name="password" required
|
||||
label="{{ __('input.password') }}" />
|
||||
<a href="/forgot-password" class="text-xs">
|
||||
{{ __('auth.forgot_password') }}?
|
||||
</a>
|
||||
@else
|
||||
<x-forms.input type="email" name="email" required label="{{ __('input.email') }}" autofocus />
|
||||
<x-forms.input type="password" name="password" required label="{{ __('input.password') }}" />
|
||||
<a href="/forgot-password" class="text-xs">
|
||||
{{ __('auth.forgot_password') }}?
|
||||
</a>
|
||||
@endenv
|
||||
<x-forms.button type="submit">{{ __('auth.login') }}</x-forms.button>
|
||||
@if (!$is_registration_enabled)
|
||||
|
@ -1,13 +1,13 @@
|
||||
<x-layout-simple>
|
||||
<div class="min-h-screen hero">
|
||||
<div>
|
||||
<div class="flex flex-col items-center pb-8">
|
||||
<div class="flex flex-col items-center ">
|
||||
<a href="{{ route('dashboard') }}">
|
||||
<div class="text-5xl font-bold tracking-tight text-center text-white">Coolify</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex items-center justify-center pb-4 text-center">
|
||||
<h2>{{ __('auth.reset_password') }}</h2>
|
||||
{{ __('auth.reset_password') }}
|
||||
</div>
|
||||
<div>
|
||||
<form action="/reset-password" method="POST" class="flex flex-col gap-2">
|
||||
|
@ -22,18 +22,20 @@
|
||||
</svg>
|
||||
</div>
|
||||
@endif
|
||||
<input {{ $attributes->merge(['class' => $defaultClass . ' pl-10']) }} @required($required)
|
||||
wire:model={{ $id }} wire:dirty.class.remove='text-white' wire:dirty.class="input-warning"
|
||||
wire:loading.attr="disabled" type="{{ $type }}" @readonly($readonly) @disabled($disabled)
|
||||
id="{{ $id }}" name="{{ $name }}" placeholder="{{ $attributes->get('placeholder') }}"
|
||||
<input value="{{ $value }}" {{ $attributes->merge(['class' => $defaultClass . ' pl-10']) }}
|
||||
@required($required) @if ($id !== 'null') wire:model={{ $id }} @endif
|
||||
wire:dirty.class.remove='text-white' wire:dirty.class="input-warning" wire:loading.attr="disabled"
|
||||
type="{{ $type }}" @readonly($readonly) @disabled($disabled) id="{{ $id }}"
|
||||
name="{{ $name }}" placeholder="{{ $attributes->get('placeholder') }}"
|
||||
aria-placeholder="{{ $attributes->get('placeholder') }}">
|
||||
|
||||
</div>
|
||||
@else
|
||||
<input {{ $attributes->merge(['class' => $defaultClass]) }} @required($required) @readonly($readonly)
|
||||
wire:model={{ $id }} wire:dirty.class.remove='text-white' wire:dirty.class="input-warning"
|
||||
<input @if ($value) value="{{ $value }}" @endif
|
||||
{{ $attributes->merge(['class' => $defaultClass]) }} @required($required) @readonly($readonly)
|
||||
@if ($id !== 'null') wire:model={{ $id }} @endif wire:dirty.class.remove='text-white' wire:dirty.class="input-warning"
|
||||
wire:loading.attr="disabled" type="{{ $type }}" @disabled($disabled)
|
||||
id="{{ $id }}" name="{{ $name }}" placeholder="{{ $attributes->get('placeholder') }}">
|
||||
@if ($id !== 'null') id={{ $id }} @endif name="{{ $name }}" placeholder="{{ $attributes->get('placeholder') }}">
|
||||
@endif
|
||||
@if (!$label && $helper)
|
||||
<x-helper :helper="$helper" />
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="navbar-main">
|
||||
<div class="navbar-main" x-data>
|
||||
<a wire:navigate class="{{ request()->routeIs('project.service.configuration') ? 'text-white' : '' }}"
|
||||
href="{{ route('project.service.configuration', $parameters) }}">
|
||||
<button>Configuration</button>
|
||||
@ -27,6 +27,16 @@
|
||||
</button>
|
||||
@endif
|
||||
@if (serviceStatus($service) === 'running')
|
||||
<button wire:click='restart' class="flex items-center gap-2 cursor-pointer hover:text-white text-neutral-400">
|
||||
<svg class="w-5 h-5 text-warning" 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="M19.933 13.041a8 8 0 1 1-9.925-8.788c3.899-1 7.935 1.007 9.425 4.747" />
|
||||
<path d="M20 4v5h-5" />
|
||||
</g>
|
||||
</svg>
|
||||
Pull Latest Images & Restart
|
||||
</button>
|
||||
<button wire:click='stop' 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-error" viewBox="0 0 24 24" stroke-width="2"
|
||||
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
@ -58,3 +68,11 @@
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@script
|
||||
<script>
|
||||
$wire.on('image-pulled', () => {
|
||||
startService.showModal();
|
||||
});
|
||||
</script>
|
||||
@endscript
|
||||
|
@ -1,6 +1,7 @@
|
||||
@extends('layouts.base')
|
||||
@section('body')
|
||||
@parent
|
||||
<x-navbar-subscription />
|
||||
<main>
|
||||
{{ $slot }}
|
||||
</main>
|
||||
|
@ -69,7 +69,7 @@
|
||||
@if ($servers->count() === 1)
|
||||
<div class="grid grid-cols-1 gap-2">
|
||||
@else
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
<div class="grid grid-cols-1 gap-2 xl:grid-cols-2">
|
||||
@endif
|
||||
@foreach ($servers as $server)
|
||||
<a wire:navigate href="{{ route('server.show', ['server_uuid' => data_get($server, 'uuid')]) }}"
|
||||
|
@ -1,12 +1,12 @@
|
||||
<div class="min-h-screen hero">
|
||||
<div class="w-96 min-w-fit">
|
||||
<div class="flex flex-col items-center pb-8">
|
||||
<div class="flex flex-col items-center">
|
||||
<a href="{{ route('dashboard') }}">
|
||||
<div class="text-5xl font-bold tracking-tight text-center text-white">Coolify</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex items-center justify-center pb-4 text-center">
|
||||
<h2>Set your initial password</h2>
|
||||
Set your initial password
|
||||
</div>
|
||||
<form class="flex flex-col gap-2" wire:submit='submit'>
|
||||
<x-forms.input id="email" type="email" placeholder="Email" readonly label="Email" />
|
||||
|
@ -1,36 +1,36 @@
|
||||
<form wire:submit='clone'>
|
||||
<div class="flex flex-col">
|
||||
<div class="flex gap-2">
|
||||
<h1>Clone</h1>
|
||||
<x-forms.button type="submit">Clone to a New Project</x-forms.button>
|
||||
</div>
|
||||
<div class="subtitle ">Quickly clone a project</div>
|
||||
<h1>Clone</h1>
|
||||
<div class="subtitle ">Quickly clone all resources to a new project</div>
|
||||
</div>
|
||||
<div class="flex items-end gap-2">
|
||||
<x-forms.input required id="newProjectName" label="New Project Name" />
|
||||
<x-forms.button type="submit">Clone</x-forms.button>
|
||||
</div>
|
||||
<x-forms.input required id="newProjectName" label="New Project Name" />
|
||||
<h3 class="pt-4 pb-2">Servers</h3>
|
||||
<div class="grid gap-2 lg:grid-cols-3">
|
||||
@foreach ($servers as $srv)
|
||||
<div wire:click="selectServer('{{ $srv->id }}')"
|
||||
class="cursor-pointer box-without-bg bg-coolgray-200 group"
|
||||
:class="'{{ $selectedServer === $srv->id }}' && 'bg-coollabs'">
|
||||
<div class="flex flex-col mx-6">
|
||||
<div :class="'{{ $selectedServer === $srv->id }}' && 'text-white'"> {{ $srv->name }}</div>
|
||||
@isset($selectedServer)
|
||||
<div :class="'{{ $selectedServer === $srv->id }}' && 'text-white pt-2 text-xs font-bold'">
|
||||
{{ $srv->description }}</div>
|
||||
@else
|
||||
<div class="description">
|
||||
{{ $srv->description }}</div>
|
||||
@endisset
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
@foreach ($servers->sortBy('id') as $server)
|
||||
<div class="p-4 border border-coolgray-500">
|
||||
<h3>{{ $server->name }}</h3>
|
||||
<h5>{{ $server->description }}</h5>
|
||||
<div class="pt-4 pb-2">Docker Networks</div>
|
||||
<div class="grid grid-cols-1 gap-2 pb-4 lg:grid-cols-4">
|
||||
@foreach ($server->destinations() as $destination)
|
||||
<div class="cursor-pointer box-without-bg bg-coolgray-200 group"
|
||||
:class="'{{ $selectedDestination === $destination->id }}' && 'bg-coollabs text-white'"
|
||||
wire:click="selectServer('{{ $server->id }}', '{{ $destination->id }}')">
|
||||
{{ $destination->name }}
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<h3 class="pt-4 pb-2">Resources</h3>
|
||||
<div class="grid grid-cols-1 gap-2">
|
||||
<div class="grid grid-cols-1 gap-2 p-4 border border-coolgray-500">
|
||||
@foreach ($environment->applications->sortBy('name') as $application)
|
||||
<div class="p-2 border border-coolgray-200">
|
||||
<div>
|
||||
<div class="flex flex-col">
|
||||
<div class="font-bold text-white">{{ $application->name }}</div>
|
||||
<div class="description">{{ $application->description }}</div>
|
||||
@ -38,7 +38,7 @@
|
||||
</div>
|
||||
@endforeach
|
||||
@foreach ($environment->databases()->sortBy('name') as $database)
|
||||
<div class="p-2 border border-coolgray-200">
|
||||
<div>
|
||||
<div class="flex flex-col">
|
||||
<div class="font-bold text-white">{{ $database->name }}</div>
|
||||
<div class="description">{{ $database->description }}</div>
|
||||
@ -46,7 +46,7 @@
|
||||
</div>
|
||||
@endforeach
|
||||
@foreach ($environment->services->sortBy('name') as $service)
|
||||
<div class="p-2 border border-coolgray-200">
|
||||
<div>
|
||||
<div class="flex flex-col">
|
||||
<div class="font-bold text-white">{{ $service->name }}</div>
|
||||
<div class="description">{{ $service->description }}</div>
|
||||
|
@ -44,26 +44,44 @@
|
||||
$application->status)->contains(['running']),
|
||||
'border-l border-dashed border-warning' => Str::of(
|
||||
$application->status)->contains(['starting']),
|
||||
'flex gap-2 box group',
|
||||
'flex gap-2 box-without-bg bg-coolgray-100 hover:text-neutral-300 group',
|
||||
])>
|
||||
<a wire:navigate class="flex flex-col flex-1 group-hover:text-white hover:no-underline"
|
||||
href="{{ route('project.service.show', [...$parameters, 'service_name' => $application->name]) }}">
|
||||
@if ($application->human_name)
|
||||
{{ Str::headline($application->human_name) }}
|
||||
@else
|
||||
{{ Str::headline($application->name) }}
|
||||
@endif
|
||||
@if ($application->configuration_required)
|
||||
<span class="text-xs text-error">(configuration required)</span>
|
||||
@endif
|
||||
@if ($application->description)
|
||||
<span class="text-xs">{{ Str::limit($application->description, 60) }}</span>
|
||||
@endif
|
||||
@if ($application->fqdn)
|
||||
<span class="text-xs">{{ Str::limit($application->fqdn, 60) }}</span>
|
||||
@endif
|
||||
<div class="text-xs">{{ $application->status }}</div>
|
||||
</a>
|
||||
<div class="flex flex-row w-full">
|
||||
<div class="flex flex-col flex-1">
|
||||
<div class="pb-2">
|
||||
@if ($application->human_name)
|
||||
{{ Str::headline($application->human_name) }}
|
||||
@else
|
||||
{{ Str::headline($application->name) }}
|
||||
@endif
|
||||
<span class="text-xs">({{ $application->image }})</span>
|
||||
</div>
|
||||
@if ($application->configuration_required)
|
||||
<span class="text-xs text-error">(configuration required)</span>
|
||||
@endif
|
||||
@if ($application->description)
|
||||
<span class="text-xs">{{ Str::limit($application->description, 60) }}</span>
|
||||
@endif
|
||||
@if ($application->fqdn)
|
||||
<span class="text-xs">{{ Str::limit($application->fqdn, 60) }}</span>
|
||||
@endif
|
||||
<div class="text-xs">{{ $application->status }}</div>
|
||||
</div>
|
||||
<div class="flex items-center px-4">
|
||||
<a wire:navigate
|
||||
class="flex flex-col flex-1 group-hover:text-white hover:no-underline"
|
||||
href="{{ route('project.service.show', [...$parameters, 'service_name' => $application->name]) }}">
|
||||
<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">
|
||||
<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>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
@foreach ($databases as $database)
|
||||
@ -74,23 +92,43 @@
|
||||
$database->status)->contains(['running']),
|
||||
'border-l border-dashed border-warning' => Str::of(
|
||||
$database->status)->contains(['restarting']),
|
||||
'flex gap-2 box group',
|
||||
'flex gap-2 box-without-bg bg-coolgray-100 hover:text-neutral-300 group',
|
||||
])>
|
||||
<a wire:navigate class="flex flex-col flex-1 group-hover:text-white hover:no-underline"
|
||||
href="{{ route('project.service.show', [...$parameters, 'service_name' => $database->name]) }}">
|
||||
@if ($database->human_name)
|
||||
{{ Str::headline($database->human_name) }}
|
||||
@else
|
||||
{{ Str::headline($database->name) }}
|
||||
@endif
|
||||
@if ($database->configuration_required)
|
||||
<span class="text-xs text-error">(configuration required)</span>
|
||||
@endif
|
||||
@if ($database->description)
|
||||
<span class="text-xs">{{ Str::limit($database->description, 60) }}</span>
|
||||
@endif
|
||||
<div class="text-xs">{{ $database->status }}</div>
|
||||
</a>
|
||||
|
||||
|
||||
<div class="flex flex-row w-full">
|
||||
<div class="flex flex-col flex-1">
|
||||
<div class="pb-2">
|
||||
@if ($database->human_name)
|
||||
{{ Str::headline($database->human_name) }}
|
||||
@else
|
||||
{{ Str::headline($database->name) }}
|
||||
@endif
|
||||
<span class="text-xs">({{ $database->image }})</span>
|
||||
</div>
|
||||
@if ($database->configuration_required)
|
||||
<span class="text-xs text-error">(configuration required)</span>
|
||||
@endif
|
||||
@if ($database->description)
|
||||
<span class="text-xs">{{ Str::limit($database->description, 60) }}</span>
|
||||
@endif
|
||||
<div class="text-xs">{{ $database->status }}</div>
|
||||
</div>
|
||||
<div class="flex items-center px-4">
|
||||
<a wire:navigate
|
||||
class="flex flex-col flex-1 group-hover:text-white hover:no-underline"
|
||||
href="{{ route('project.service.show', [...$parameters, 'service_name' => $database->name]) }}">
|
||||
<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">
|
||||
<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>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
@ -18,8 +18,12 @@
|
||||
<td>{{ $invite->email }}</td>
|
||||
<td>{{ $invite->via }}</td>
|
||||
<td>{{ $invite->role }}</td>
|
||||
<td x-on:click="copyToClipboard('{{ $invite->link }}')">
|
||||
<x-forms.button>Copy Invitation Link</x-forms.button>
|
||||
<td class="flex gap-2" x-data="checkProtocol">
|
||||
<template x-if="isHttps">
|
||||
<x-forms.button x-on:click="copyToClipboard('{{ $invite->link }}')">Copy Invitation
|
||||
Link</x-forms.button>
|
||||
</template>
|
||||
<x-forms.input id="null" type="password" value="{{ $invite->link }}" />
|
||||
</td>
|
||||
<td>
|
||||
<x-forms.button wire:click.prevent='deleteInvitation({{ $invite->id }})'>Revoke
|
||||
@ -31,6 +35,15 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@script
|
||||
<script>
|
||||
Alpine.data('checkProtocol', () => {
|
||||
return {
|
||||
isHttps: window.location.protocol === 'https:'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@endscript
|
||||
|
@ -240,3 +240,10 @@ Route::middleware(['auth'])->group(function () {
|
||||
]);
|
||||
})->name('destination.show');
|
||||
});
|
||||
|
||||
Route::any('/{any}', function () {
|
||||
if (auth()->user()) {
|
||||
return redirect('/');
|
||||
}
|
||||
return redirect('/login');
|
||||
})->where('any', '.*');
|
||||
|
71
scripts/run
71
scripts/run
@ -20,47 +20,50 @@ function help {
|
||||
compgen -A function | cat -n
|
||||
}
|
||||
|
||||
function setup:dev {
|
||||
docker exec coolify bash -c "composer install"
|
||||
docker exec coolify bash -c "php artisan key:generate"
|
||||
docker exec coolify bash -c "php artisan migrate:fresh --seed"
|
||||
sudo chmod -R o+rwx .
|
||||
}
|
||||
function sync:v3 {
|
||||
if [ -z "$1" ]; then
|
||||
echo -e "Please provide a version.\n\nExample: run sync:v3 3.12.32"
|
||||
exit 1
|
||||
fi
|
||||
skopeo copy --all docker://ghcr.io/coollabsio/coolify:$1 docker://coollabsio/coolify:$1
|
||||
}
|
||||
# function dev:init {
|
||||
# docker exec coolify bash -c "php artisan migrate --seed"
|
||||
# echo "Need to update privileges on a few files. I need your password for that."
|
||||
# sudo chmod -R o+rwx .
|
||||
# }
|
||||
|
||||
# function sync:v3 {
|
||||
# if [ -z "$1" ]; then
|
||||
# echo -e "Please provide a version.\n\nExample: run sync:v3 3.12.32"
|
||||
# exit 1
|
||||
# fi
|
||||
# skopeo copy --all docker://ghcr.io/coollabsio/coolify:$1 docker://coollabsio/coolify:$1
|
||||
# }
|
||||
function sync:bunny {
|
||||
php artisan sync:bunny --env=secrets
|
||||
}
|
||||
|
||||
function queue {
|
||||
bash spin exec -u webuser coolify php artisan queue:listen
|
||||
}
|
||||
# function queue {
|
||||
# bash spin exec -u webuser coolify php artisan queue:listen
|
||||
# }
|
||||
|
||||
function horizon {
|
||||
bash spin exec -u webuser coolify php artisan horizon -vvv
|
||||
}
|
||||
# function horizon {
|
||||
# bash spin exec -u webuser coolify php artisan horizon -vvv
|
||||
# }
|
||||
|
||||
function schedule {
|
||||
bash spin exec -u webuser coolify php artisan schedule:work
|
||||
}
|
||||
# function schedule {
|
||||
# bash spin exec -u webuser coolify php artisan schedule:work
|
||||
# }
|
||||
|
||||
function schedule:run {
|
||||
bash spin exec -u webuser coolify php artisan schedule:run
|
||||
}
|
||||
# function schedule:run {
|
||||
# bash spin exec -u webuser coolify php artisan schedule:run
|
||||
# }
|
||||
|
||||
|
||||
function db {
|
||||
bash spin exec -u webuser coolify php artisan db
|
||||
}
|
||||
# function db {
|
||||
# bash spin exec -u webuser coolify php artisan db
|
||||
# }
|
||||
# function db:seed {
|
||||
# bash spin exec -u webuser coolify php artisan migrate --seed
|
||||
# }
|
||||
|
||||
function db:migrate {
|
||||
bash spin exec -u webuser coolify php artisan migrate
|
||||
}
|
||||
# function db:migrate {
|
||||
# bash spin exec -u webuser coolify php artisan migrate --step
|
||||
# }
|
||||
|
||||
function db:reset {
|
||||
bash spin exec -u webuser coolify php artisan migrate:fresh --seed
|
||||
@ -98,9 +101,9 @@ function tinker {
|
||||
}
|
||||
|
||||
|
||||
function build:helper {
|
||||
act -W .github/workflows/coolify-helper.yml --secret-file .env.secrets
|
||||
}
|
||||
# function build:helper {
|
||||
# act -W .github/workflows/coolify-helper.yml --secret-file .env.secrets
|
||||
# }
|
||||
function default {
|
||||
help
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
"version": "3.12.36"
|
||||
},
|
||||
"v4": {
|
||||
"version": "4.0.0-beta.161"
|
||||
"version": "4.0.0-beta.162"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user