diff --git a/.dockerignore b/.dockerignore index 090a80fbb..d6abd1451 100644 --- a/.dockerignore +++ b/.dockerignore @@ -20,3 +20,5 @@ yarn-error.log /.npm /.bash_history /_data +.rnd +/.ssh diff --git a/.env.development.example b/.env.development.example index e73b8c1dd..43d3a10fd 100644 --- a/.env.development.example +++ b/.env.development.example @@ -14,3 +14,9 @@ APP_URL=http://localhost APP_PORT=8000 DUSK_DRIVER_URL=http://selenium:4444 + +## For Andras only +# To purge cache +BUNNY_API_KEY= +# To upload assets +BUNNY_STORAGE_API_KEY= diff --git a/.env.secrets.example b/.env.secrets.example deleted file mode 100644 index a114f6982..000000000 --- a/.env.secrets.example +++ /dev/null @@ -1,6 +0,0 @@ -# Secrets related to pushing to GH, Sync files to BunnyCDN etc. Only for maintainers. -# Not related to Coolify, but to how we publish new versions. - -GITHUB_TOKEN= -BUNNY_API_KEY= -BUNNY_STORAGE_API_KEY= diff --git a/.gitignore b/.gitignore index e1b6b679e..d75e3c80f 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,5 @@ _ide_helper.php .gitignore .phpstorm.meta.php _ide_helper_models.php +.rnd +/.ssh diff --git a/app/Actions/Database/StartPostgresql.php b/app/Actions/Database/StartPostgresql.php index 1b5d22f44..ba427a93d 100644 --- a/app/Actions/Database/StartPostgresql.php +++ b/app/Actions/Database/StartPostgresql.php @@ -17,7 +17,7 @@ class StartPostgresql public function __invoke(Server $server, StandalonePostgresql $database) { $this->database = $database; - $container_name = generate_container_name($this->database->uuid); + $container_name = $this->database->uuid; $this->configuration_dir = database_configuration_dir() . '/' . $container_name; $this->commands = [ @@ -36,7 +36,7 @@ class StartPostgresql 'image' => $this->database->image, 'container_name' => $container_name, 'environment' => $environment_variables, - 'restart' => 'always', + 'restart' => RESTART_MODE, 'networks' => [ $this->database->destination->network, ], diff --git a/app/Actions/License/CheckResaleLicense.php b/app/Actions/License/CheckResaleLicense.php index e7dcecdb6..54f97c834 100644 --- a/app/Actions/License/CheckResaleLicense.php +++ b/app/Actions/License/CheckResaleLicense.php @@ -14,14 +14,14 @@ class CheckResaleLicense $settings->update([ 'is_resale_license_active' => false, ]); - if (is_dev()) { + if (isDev()) { return; } if (!$settings->resale_license) { return; } $base_url = config('coolify.license_url'); - if (is_dev()) { + if (isDev()) { $base_url = 'http://host.docker.internal:8787'; } $instance_id = config('app.id'); diff --git a/app/Actions/Server/InstallDocker.php b/app/Actions/Server/InstallDocker.php index 7c58445dc..a26fadc05 100644 --- a/app/Actions/Server/InstallDocker.php +++ b/app/Actions/Server/InstallDocker.php @@ -12,29 +12,38 @@ class InstallDocker { $dockerVersion = '23.0'; $config = base64_encode('{ "live-restore": true }'); - $activity = remote_process([ - "echo ####### Installing Prerequisites...", - "command -v jq >/dev/null || apt-get update", - "command -v jq >/dev/null || apt install -y jq", - "echo ####### Installing/updating Docker Engine...", - "curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh", - "echo ####### Configuring Docker Engine (merging existing configuration with the required)...", - "test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json \"/etc/docker/daemon.json.original-`date +\"%Y%m%d-%H%M%S\"`\" || echo '{$config}' | base64 -d > /etc/docker/daemon.json", - "echo '{$config}' | base64 -d > /etc/docker/daemon.json.coolify", - "cat <<< $(jq . /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json.coolify", - "cat <<< $(jq -s '.[0] * .[1]' /etc/docker/daemon.json /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json", - "echo ####### Restarting Docker Engine...", - "systemctl restart docker", - "echo ####### Creating default network...", - "docker network create --attachable coolify", - "echo ####### Done!" - ], $server); - StandaloneDocker::create([ - 'name' => 'coolify', - 'network' => 'coolify', - 'server_id' => $server->id, - 'team_id' => $team->id - ]); + if (isDev()) { + $activity = remote_process([ + "echo ####### Installing Prerequisites...", + "echo ####### Installing/updating Docker Engine...", + "echo ####### Configuring Docker Engine (merging existing configuration with the required)...", + "echo ####### Restarting Docker Engine...", + ], $server); + } else { + $activity = remote_process([ + "echo ####### Installing Prerequisites...", + "command -v jq >/dev/null || apt-get update", + "command -v jq >/dev/null || apt install -y jq", + "echo ####### Installing/updating Docker Engine...", + "curl https://releases.rancher.com/install-docker/{$dockerVersion}.sh | sh", + "echo ####### Configuring Docker Engine (merging existing configuration with the required)...", + "test -s /etc/docker/daemon.json && cp /etc/docker/daemon.json \"/etc/docker/daemon.json.original-`date +\"%Y%m%d-%H%M%S\"`\" || echo '{$config}' | base64 -d > /etc/docker/daemon.json", + "echo '{$config}' | base64 -d > /etc/docker/daemon.json.coolify", + "cat <<< $(jq . /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json.coolify", + "cat <<< $(jq -s '.[0] * .[1]' /etc/docker/daemon.json /etc/docker/daemon.json.coolify) > /etc/docker/daemon.json", + "echo ####### Restarting Docker Engine...", + "systemctl restart docker", + "echo ####### Creating default network...", + "docker network create --attachable coolify", + "echo ####### Done!" + ], $server); + StandaloneDocker::create([ + 'name' => 'coolify', + 'network' => 'coolify', + 'server_id' => $server->id, + ]); + } + return $activity; } diff --git a/app/Actions/Server/UpdateCoolify.php b/app/Actions/Server/UpdateCoolify.php index 8f81852d8..0be05894f 100644 --- a/app/Actions/Server/UpdateCoolify.php +++ b/app/Actions/Server/UpdateCoolify.php @@ -17,9 +17,6 @@ class UpdateCoolify $settings = InstanceSettings::get(); ray('Running InstanceAutoUpdateJob'); $localhost_name = 'localhost'; - if (is_dev()) { - $localhost_name = 'testing-local-docker-container'; - } $this->server = Server::where('name', $localhost_name)->firstOrFail(); $this->latest_version = get_latest_version_of_coolify(); $this->current_version = config('version'); @@ -32,27 +29,28 @@ class UpdateCoolify $this->update(); } else { if (!$settings->is_auto_update_enabled) { - throw new \Exception('Auto update is disabled'); + return 'Auto update is disabled'; } if ($this->latest_version === $this->current_version) { - throw new \Exception('Already on latest version'); + return 'Already on latest version'; } if (version_compare($this->latest_version, $this->current_version, '<')) { - throw new \Exception('Latest version is lower than current version?!'); + return 'Latest version is lower than current version?!'; } $this->update(); } - return; - } catch (\Exception $e) { + send_internal_notification('InstanceAutoUpdateJob done to version: ' . $this->latest_version . ' from version: ' . $this->current_version); + } catch (\Exception $th) { ray('InstanceAutoUpdateJob failed'); - ray($e->getMessage()); - return; + ray($th->getMessage()); + send_internal_notification('InstanceAutoUpdateJob failed: ' . $th->getMessage()); + throw $th; } } private function update() { - if (is_dev()) { + if (isDev()) { ray("Running update on local docker container. Updating to $this->latest_version"); remote_process([ "sleep 10" diff --git a/app/Console/Commands/InviteFromWaitlist.php b/app/Console/Commands/InviteFromWaitlist.php index 4adc60693..2794b7441 100644 --- a/app/Console/Commands/InviteFromWaitlist.php +++ b/app/Console/Commands/InviteFromWaitlist.php @@ -19,21 +19,29 @@ class InviteFromWaitlist extends Command * * @var string */ - protected $signature = 'app:invite-from-waitlist'; + protected $signature = 'app:invite-from-waitlist {email?}'; /** * The console command description. * * @var string */ - protected $description = 'Send invitation to the next user in the waitlist'; + protected $description = 'Send invitation to the next user (or by email) in the waitlist'; /** * Execute the console command. */ public function handle() { - $this->next_patient = Waitlist::orderBy('created_at', 'asc')->where('verified', true)->first(); + if ($this->argument('email')) { + $this->next_patient = Waitlist::where('email', $this->argument('email'))->first(); + if (!$this->next_patient) { + $this->error("{$this->argument('email')} not found in the waitlist."); + return; + } + } else { + $this->next_patient = Waitlist::orderBy('created_at', 'asc')->where('verified', true)->first(); + } if ($this->next_patient) { $this->register_user(); $this->remove_from_waitlist(); diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 047f037d7..0dc99ba8a 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -18,7 +18,7 @@ class Kernel extends ConsoleKernel protected function schedule(Schedule $schedule): void { // $schedule->call(fn() => $this->check_scheduled_backups($schedule))->everyTenSeconds(); - if (is_dev()) { + if (isDev()) { $schedule->command('horizon:snapshot')->everyMinute(); $schedule->job(new ResourceStatusJob)->everyMinute(); $schedule->job(new ProxyCheckJob)->everyFiveMinutes(); diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index b08c07012..9d9dabab6 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -45,7 +45,7 @@ class Handler extends ExceptionHandler { $this->reportable(function (Throwable $e) { $this->settings = InstanceSettings::get(); - if ($this->settings->do_not_track || is_dev()) { + if ($this->settings->do_not_track || isDev()) { return; } Integration::captureUnhandledException($e); diff --git a/app/Http/Controllers/ApplicationController.php b/app/Http/Controllers/ApplicationController.php index 9b87492e4..aa2787de7 100644 --- a/app/Http/Controllers/ApplicationController.php +++ b/app/Http/Controllers/ApplicationController.php @@ -12,7 +12,7 @@ class ApplicationController extends Controller public function configuration() { - $project = auth()->user()->currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); + $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); if (!$project) { return redirect()->route('dashboard'); } @@ -29,7 +29,7 @@ class ApplicationController extends Controller public function deployments() { - $project = auth()->user()->currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); + $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); if (!$project) { return redirect()->route('dashboard'); } @@ -49,7 +49,7 @@ class ApplicationController extends Controller { $deploymentUuid = request()->route('deployment_uuid'); - $project = auth()->user()->currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); + $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); if (!$project) { return redirect()->route('dashboard'); } diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 84da5de2e..4fd997055 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -30,7 +30,7 @@ class Controller extends BaseController if (!is_cloud()) { abort(404); } - return view('subscription', [ + return view('subscription.show', [ 'settings' => InstanceSettings::get(), ]); } @@ -58,7 +58,6 @@ class Controller extends BaseController $resources += $project->applications->count(); $resources += $project->postgresqls->count(); } - return view('dashboard', [ 'servers' => $servers->count(), 'projects' => $projects->count(), @@ -66,10 +65,17 @@ class Controller extends BaseController 's3s' => $s3s, ]); } + public function boarding() { + if (currentTeam()->boarding || isDev()) { + return view('boarding'); + } else { + return redirect()->route('dashboard'); + } + } public function settings() { - if (is_instance_admin()) { + if (isInstanceAdmin()) { $settings = InstanceSettings::get(); $database = StandalonePostgresql::whereName('coolify-db')->first(); if ($database) { @@ -89,7 +95,7 @@ class Controller extends BaseController { $invitations = []; if (auth()->user()->isAdminFromSession()) { - $invitations = TeamInvitation::whereTeamId(auth()->user()->currentTeam()->id)->get(); + $invitations = TeamInvitation::whereTeamId(currentTeam()->id)->get(); } return view('team.show', [ 'invitations' => $invitations, @@ -116,7 +122,7 @@ class Controller extends BaseController { $invitations = []; if (auth()->user()->isAdminFromSession()) { - $invitations = TeamInvitation::whereTeamId(auth()->user()->currentTeam()->id)->get(); + $invitations = TeamInvitation::whereTeamId(currentTeam()->id)->get(); } return view('team.members', [ 'invitations' => $invitations, diff --git a/app/Http/Controllers/DatabaseController.php b/app/Http/Controllers/DatabaseController.php index bb6baf5ec..958d6d5ab 100644 --- a/app/Http/Controllers/DatabaseController.php +++ b/app/Http/Controllers/DatabaseController.php @@ -11,7 +11,7 @@ class DatabaseController extends Controller public function configuration() { - $project = auth()->user()->currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); + $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); if (!$project) { return redirect()->route('dashboard'); } @@ -29,7 +29,7 @@ class DatabaseController extends Controller public function executions() { $backup_uuid = request()->route('backup_uuid'); - $project = auth()->user()->currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); + $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); if (!$project) { return redirect()->route('dashboard'); } @@ -50,13 +50,13 @@ class DatabaseController extends Controller 'database' => $database, 'backup' => $backup, 'executions' => $executions, - 's3s' => auth()->user()->currentTeam()->s3s, + 's3s' => currentTeam()->s3s, ]); } public function backups() { - $project = auth()->user()->currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); + $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); if (!$project) { return redirect()->route('dashboard'); } @@ -70,7 +70,7 @@ class DatabaseController extends Controller } return view('project.database.backups.all', [ 'database' => $database, - 's3s' => auth()->user()->currentTeam()->s3s, + 's3s' => currentTeam()->s3s, ]); } } diff --git a/app/Http/Controllers/MagicController.php b/app/Http/Controllers/MagicController.php index 515bc14d1..d7635fda0 100644 --- a/app/Http/Controllers/MagicController.php +++ b/app/Http/Controllers/MagicController.php @@ -41,7 +41,7 @@ class MagicController extends Controller { $project = Project::firstOrCreate( ['name' => request()->query('name') ?? generate_random_name()], - ['team_id' => auth()->user()->currentTeam()->id] + ['team_id' => currentTeam()->id] ); return response()->json([ 'project_uuid' => $project->uuid @@ -68,7 +68,7 @@ class MagicController extends Controller ], ); auth()->user()->teams()->attach($team, ['role' => 'admin']); - session(['currentTeam' => $team]); + refreshSession(); return redirect(request()->header('Referer')); } } diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php index 2ad941e92..476d54437 100644 --- a/app/Http/Controllers/ProjectController.php +++ b/app/Http/Controllers/ProjectController.php @@ -18,7 +18,7 @@ class ProjectController extends Controller public function edit() { $projectUuid = request()->route('project_uuid'); - $teamId = auth()->user()->currentTeam()->id; + $teamId = currentTeam()->id; $project = Project::where('team_id', $teamId)->where('uuid', $projectUuid)->first(); if (!$project) { return redirect()->route('dashboard'); @@ -29,7 +29,7 @@ class ProjectController extends Controller public function show() { $projectUuid = request()->route('project_uuid'); - $teamId = auth()->user()->currentTeam()->id; + $teamId = currentTeam()->id; $project = Project::where('team_id', $teamId)->where('uuid', $projectUuid)->first(); if (!$project) { @@ -44,7 +44,7 @@ class ProjectController extends Controller $type = request()->query('type'); $destination_uuid = request()->query('destination'); - $project = auth()->user()->currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); + $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); if (!$project) { return redirect()->route('dashboard'); } @@ -67,7 +67,7 @@ class ProjectController extends Controller public function resources() { - $project = auth()->user()->currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); + $project = currentTeam()->load(['projects'])->projects->where('uuid', request()->route('project_uuid'))->first(); if (!$project) { return redirect()->route('dashboard'); } diff --git a/app/Http/Controllers/ServerController.php b/app/Http/Controllers/ServerController.php index e7f9feb57..54b8c7a8c 100644 --- a/app/Http/Controllers/ServerController.php +++ b/app/Http/Controllers/ServerController.php @@ -12,16 +12,16 @@ class ServerController extends Controller public function new_server() { - if (!is_cloud() || is_instance_admin()) { + if (!is_cloud() || isInstanceAdmin()) { return view('server.create', [ 'limit_reached' => false, 'private_keys' => PrivateKey::ownedByCurrentTeam()->get(), ]); } - $servers = auth()->user()->currentTeam()->servers->count(); - $subscription = auth()->user()->currentTeam()?->subscription->type(); - $limits = config('constants.limits.server')[strtolower($subscription)]; - $limit_reached = true ?? $servers >= $limits[$subscription]; + $servers = currentTeam()->servers->count(); + $subscription = currentTeam()?->subscription->type(); + $your_limit = config('constants.limits.server')[strtolower($subscription)]; + $limit_reached = $servers >= $your_limit; return view('server.create', [ 'limit_reached' => $limit_reached, diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 57b5edfec..eb93b6d35 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -39,6 +39,7 @@ class Kernel extends HttpKernel \Illuminate\Routing\Middleware\SubstituteBindings::class, \App\Http\Middleware\CheckForcePasswordReset::class, \App\Http\Middleware\SubscriptionValid::class, + \App\Http\Middleware\IsBoardingFlow::class, ], diff --git a/app/Http/Livewire/Boarding.php b/app/Http/Livewire/Boarding.php new file mode 100644 index 000000000..7ec11a3bc --- /dev/null +++ b/app/Http/Livewire/Boarding.php @@ -0,0 +1,181 @@ +privateKeyName = generate_random_name(); + $this->remoteServerName = generate_random_name(); + if (isDev()) { + $this->privateKey = '-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevAAAAJi/QySHv0Mk +hwAAAAtzc2gtZWQyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevA +AAAECBQw4jg1WRT2IGHMncCiZhURCts2s24HoDS0thHnnRKVuGmoeGq/pojrsyP1pszcNV +uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== +-----END OPENSSH PRIVATE KEY-----'; + $this->privateKeyDescription = 'Created by Coolify'; + $this->remoteServerDescription = 'Created by Coolify'; + $this->remoteServerHost = 'coolify-testing-host'; + } + } + public function restartBoarding() + { + if ($this->createdServer) { + $this->createdServer->delete(); + } + if ($this->createdPrivateKey) { + $this->createdPrivateKey->delete(); + } + return redirect()->route('boarding'); + } + public function skipBoarding() + { + currentTeam()->update([ + 'show_boarding' => false + ]); + refreshSession(); + return redirect()->route('dashboard'); + } + public function setServer(string $type) + { + if ($type === 'localhost') { + $this->createdServer = Server::find(0); + if (!$this->createdServer) { + return $this->emit('error', 'Localhost server is not found. Something went wrong during installation. Please try to reinstall or contact support.'); + } + $this->currentState = 'select-proxy'; + } elseif ($type === 'remote') { + $this->currentState = 'private-key'; + } + } + public function setPrivateKey(string $type) + { + $this->privateKeyType = $type; + if ($type === 'create' && !isDev()) { + $this->createNewPrivateKey(); + } + $this->currentState = 'create-private-key'; + } + public function savePrivateKey() + { + $this->validate([ + 'privateKeyName' => 'required', + 'privateKey' => 'required', + ]); + $this->currentState = 'create-server'; + } + public function saveServer() + { + $this->validate([ + 'remoteServerName' => 'required', + 'remoteServerHost' => 'required', + 'remoteServerPort' => 'required', + 'remoteServerUser' => 'required', + ]); + $this->privateKey = formatPrivateKey($this->privateKey); + $this->createdPrivateKey = PrivateKey::create([ + 'name' => $this->privateKeyName, + 'description' => $this->privateKeyDescription, + 'private_key' => $this->privateKey, + 'team_id' => currentTeam()->id + ]); + $this->createdServer = Server::create([ + 'name' => $this->remoteServerName, + 'ip' => $this->remoteServerHost, + 'port' => $this->remoteServerPort, + 'user' => $this->remoteServerUser, + 'description' => $this->remoteServerDescription, + 'private_key_id' => $this->createdPrivateKey->id, + 'team_id' => currentTeam()->id + ]); + try { + ['uptime' => $uptime, 'dockerVersion' => $dockerVersion] = validateServer($this->createdServer); + if (!$uptime) { + $this->createdServer->delete(); + $this->createdPrivateKey->delete(); + throw new \Exception('Server is not reachable.'); + } else { + $this->createdServer->settings->update([ + 'is_reachable' => true, + ]); + $this->emit('success', 'Server is reachable.'); + } + if ($dockerVersion) { + $this->emit('error', 'Docker is not installed on the server.'); + $this->currentState = 'install-docker'; + return; + } + } catch (\Exception $e) { + return general_error_handler(customErrorMessage: "Server is not reachable. Reason: {$e->getMessage()}", that: $this); + } + } + public function installDocker() + { + $activity = resolve(InstallDocker::class)($this->createdServer, currentTeam()); + $this->emit('newMonitorActivity', $activity->id); + $this->currentState = 'select-proxy'; + } + public function selectProxy(string|null $proxyType = null) + { + if (!$proxyType) { + return $this->currentState = 'create-project'; + } + $this->createdServer->proxy->type = $proxyType; + $this->createdServer->proxy->status = 'exited'; + $this->createdServer->save(); + $this->currentState = 'create-project'; + } + public function createNewProject() + { + $this->createdProject = Project::create([ + 'name' => "My first project", + 'team_id' => currentTeam()->id + ]); + $this->currentState = 'create-resource'; + } + public function showNewResource() + { + $this->skipBoarding(); + return redirect()->route( + 'project.resources.new', + [ + 'project_uuid' => $this->createdProject->uuid, + 'environment_name' => 'production', + + ] + ); + } + private function createNewPrivateKey() + { + $this->privateKeyName = generate_random_name(); + $this->privateKeyDescription = 'Created by Coolify'; + ['private' => $this->privateKey, 'public'=> $this->publicKey] = generateSSHKey(); + } +} diff --git a/app/Http/Livewire/Destination/New/StandaloneDocker.php b/app/Http/Livewire/Destination/New/StandaloneDocker.php index fa884ec37..1af7bc2df 100644 --- a/app/Http/Livewire/Destination/New/StandaloneDocker.php +++ b/app/Http/Livewire/Destination/New/StandaloneDocker.php @@ -60,20 +60,19 @@ class StandaloneDocker extends Component $found = $this->server->standaloneDockers()->where('network', $this->network)->first(); if ($found) { $this->createNetworkAndAttachToProxy(); - $this->addError('network', 'Network already added to this server.'); + $this->emit('error', 'Network already added to this server.'); return; } else { $docker = ModelsStandaloneDocker::create([ 'name' => $this->name, 'network' => $this->network, 'server_id' => $this->server_id, - 'team_id' => auth()->user()->currentTeam()->id ]); } $this->createNetworkAndAttachToProxy(); return redirect()->route('destination.show', $docker->uuid); } catch (\Exception $e) { - return general_error_handler(err: $e); + return general_error_handler(err: $e, that: $this); } } diff --git a/app/Http/Livewire/Notifications/DiscordSettings.php b/app/Http/Livewire/Notifications/DiscordSettings.php index 03cac4753..f9de6d662 100644 --- a/app/Http/Livewire/Notifications/DiscordSettings.php +++ b/app/Http/Livewire/Notifications/DiscordSettings.php @@ -41,10 +41,9 @@ class DiscordSettings extends Component public function saveModel() { - ray($this->model); $this->model->save(); if (is_a($this->model, Team::class)) { - session(['currentTeam' => $this->model]); + refreshSession(); } $this->emit('success', 'Settings saved.'); } diff --git a/app/Http/Livewire/Notifications/EmailSettings.php b/app/Http/Livewire/Notifications/EmailSettings.php index eceb4e88b..a2887f989 100644 --- a/app/Http/Livewire/Notifications/EmailSettings.php +++ b/app/Http/Livewire/Notifications/EmailSettings.php @@ -59,7 +59,7 @@ class EmailSettings extends Component { $settings = InstanceSettings::get(); if ($settings->smtp_enabled) { - $team = auth()->user()->currentTeam(); + $team = currentTeam(); $team->update([ 'smtp_enabled' => $settings->smtp_enabled, 'smtp_from_address' => $settings->smtp_from_address, @@ -74,7 +74,7 @@ class EmailSettings extends Component ]); $this->decrypt(); if (is_a($team, Team::class)) { - session(['currentTeam' => $team]); + refreshSession(); } $this->model = $team; $this->emit('success', 'Settings saved.'); @@ -119,7 +119,7 @@ class EmailSettings extends Component $this->model->save(); $this->decrypt(); if (is_a($this->model, Team::class)) { - session(['currentTeam' => $this->model]); + refreshSession(); } $this->emit('success', 'Settings saved.'); } diff --git a/app/Http/Livewire/PrivateKey/Change.php b/app/Http/Livewire/PrivateKey/Change.php index 91d42bd7a..bd9332ca2 100644 --- a/app/Http/Livewire/PrivateKey/Change.php +++ b/app/Http/Livewire/PrivateKey/Change.php @@ -26,7 +26,7 @@ class Change extends Component try { if ($this->private_key->isEmpty()) { $this->private_key->delete(); - auth()->user()->currentTeam()->privateKeys = PrivateKey::where('team_id', auth()->user()->currentTeam()->id)->get(); + currentTeam()->privateKeys = PrivateKey::where('team_id', currentTeam()->id)->get(); return redirect()->route('private-key.all'); } $this->emit('error', 'This private key is in use and cannot be deleted. Please delete all servers, applications, and GitHub/GitLab apps that use this private key before deleting it.'); @@ -38,10 +38,7 @@ class Change extends Component public function changePrivateKey() { try { - $this->private_key->private_key = trim($this->private_key->private_key); - if (!str_ends_with($this->private_key->private_key, "\n")) { - $this->private_key->private_key .= "\n"; - } + $this->private_key->private_key = formatPrivateKey($this->private_key->private_key); $this->private_key->save(); refresh_server_connection($this->private_key); } catch (\Exception $e) { diff --git a/app/Http/Livewire/PrivateKey/Create.php b/app/Http/Livewire/PrivateKey/Create.php index 82218f4a5..d8d3cc41a 100644 --- a/app/Http/Livewire/PrivateKey/Create.php +++ b/app/Http/Livewire/PrivateKey/Create.php @@ -32,7 +32,7 @@ class Create extends Component 'name' => $this->name, 'description' => $this->description, 'private_key' => $this->value, - 'team_id' => auth()->user()->currentTeam()->id + 'team_id' => currentTeam()->id ]); if ($this->from === 'server') { return redirect()->route('server.create'); diff --git a/app/Http/Livewire/Project/AddEmpty.php b/app/Http/Livewire/Project/AddEmpty.php index 1024bc66f..fc20f9850 100644 --- a/app/Http/Livewire/Project/AddEmpty.php +++ b/app/Http/Livewire/Project/AddEmpty.php @@ -25,7 +25,7 @@ class AddEmpty extends Component $project = Project::create([ 'name' => $this->name, 'description' => $this->description, - 'team_id' => auth()->user()->currentTeam()->id, + 'team_id' => currentTeam()->id, ]); return redirect()->route('project.show', $project->uuid); } catch (\Exception $e) { diff --git a/app/Http/Livewire/Project/Application/General.php b/app/Http/Livewire/Project/Application/General.php index 04be74140..80054d5db 100644 --- a/app/Http/Livewire/Project/Application/General.php +++ b/app/Http/Livewire/Project/Application/General.php @@ -136,15 +136,20 @@ class General extends Component public function submit() { + ray($this->application); try { $this->validate(); - - $domains = Str::of($this->application->fqdn)->trim()->explode(',')->map(function ($domain) { - return Str::of($domain)->trim()->lower(); - }); - $port = get_port_from_dockerfile($this->application->dockerfile); - if ($port) { - $this->application->ports_exposes = $port; + if (data_get($this->application,'fqdn')) { + $domains = Str::of($this->application->fqdn)->trim()->explode(',')->map(function ($domain) { + return Str::of($domain)->trim()->lower(); + }); + $this->application->fqdn = $domains->implode(','); + } + if ($this->application->dockerfile) { + $port = get_port_from_dockerfile($this->application->dockerfile); + if ($port) { + $this->application->ports_exposes = $port; + } } if ($this->application->base_directory && $this->application->base_directory !== '/') { $this->application->base_directory = rtrim($this->application->base_directory, '/'); @@ -152,7 +157,6 @@ class General extends Component if ($this->application->publish_directory && $this->application->publish_directory !== '/') { $this->application->publish_directory = rtrim($this->application->publish_directory, '/'); } - $this->application->fqdn = data_get($domains->implode(','), '', null); $this->application->save(); $this->emit('success', 'Application settings updated!'); } catch (\Exception $e) { diff --git a/app/Http/Livewire/Project/Application/Heading.php b/app/Http/Livewire/Project/Application/Heading.php index fdbe5f71e..5b9560007 100644 --- a/app/Http/Livewire/Project/Application/Heading.php +++ b/app/Http/Livewire/Project/Application/Heading.php @@ -2,7 +2,7 @@ namespace App\Http\Livewire\Project\Application; -use App\Jobs\ContainerStatusJob; +use App\Jobs\ApplicationContainerStatusJob; use App\Models\Application; use App\Notifications\Application\StatusChanged; use Livewire\Component; @@ -22,9 +22,8 @@ class Heading extends Component public function check_status() { - dispatch_sync(new ContainerStatusJob( - resource: $this->application, - container_name: generate_container_name($this->application->uuid), + dispatch_sync(new ApplicationContainerStatusJob( + application: $this->application, )); $this->application->refresh(); } @@ -58,12 +57,21 @@ class Heading extends Component public function stop() { - remote_process( - ["docker rm -f {$this->application->uuid}"], - $this->application->destination->server - ); - $this->application->status = 'stopped'; - $this->application->save(); - $this->application->environment->project->team->notify(new StatusChanged($this->application)); + $containers = getCurrentApplicationContainerStatus($this->application->destination->server, $this->application->id); + if ($containers->count() === 0) { + return; + } + foreach ($containers as $container) { + $containerName = data_get($container, 'Names'); + if ($containerName) { + remote_process( + ["docker rm -f {$containerName}"], + $this->application->destination->server + ); + $this->application->status = 'stopped'; + $this->application->save(); + // $this->application->environment->project->team->notify(new StatusChanged($this->application)); + } + } } } diff --git a/app/Http/Livewire/Project/Application/Previews.php b/app/Http/Livewire/Project/Application/Previews.php index b3582fbef..ad3ca5ad9 100644 --- a/app/Http/Livewire/Project/Application/Previews.php +++ b/app/Http/Livewire/Project/Application/Previews.php @@ -2,7 +2,7 @@ namespace App\Http\Livewire\Project\Application; -use App\Jobs\ContainerStatusJob; +use App\Jobs\ApplicationContainerStatusJob; use App\Models\Application; use App\Models\ApplicationPreview; use Illuminate\Support\Collection; @@ -25,10 +25,9 @@ class Previews extends Component public function loadStatus($pull_request_id) { - dispatch(new ContainerStatusJob( - resource: $this->application, - container_name: generate_container_name($this->application->uuid, $pull_request_id), - pull_request_id: $pull_request_id + dispatch(new ApplicationContainerStatusJob( + application: $this->application, + pullRequestId: $pull_request_id )); } @@ -82,7 +81,7 @@ class Previews extends Component public function stop(int $pull_request_id) { try { - $container_name = generate_container_name($this->application->uuid, $pull_request_id); + $container_name = generateApplicationContainerName($this->application->uuid, $pull_request_id); ray('Stopping container: ' . $container_name); instant_remote_process(["docker rm -f $container_name"], $this->application->destination->server, throwError: false); diff --git a/app/Http/Livewire/Project/Application/Source.php b/app/Http/Livewire/Project/Application/Source.php index 5affe9e45..801d533aa 100644 --- a/app/Http/Livewire/Project/Application/Source.php +++ b/app/Http/Livewire/Project/Application/Source.php @@ -29,7 +29,7 @@ class Source extends Component private function get_private_keys() { - $this->private_keys = PrivateKey::whereTeamId(auth()->user()->currentTeam()->id)->get()->reject(function ($key) { + $this->private_keys = PrivateKey::whereTeamId(currentTeam()->id)->get()->reject(function ($key) { return $key->id == $this->application->private_key_id; }); } diff --git a/app/Http/Livewire/Project/Database/CreateScheduledBackup.php b/app/Http/Livewire/Project/Database/CreateScheduledBackup.php index e3ea97735..9297f65f6 100644 --- a/app/Http/Livewire/Project/Database/CreateScheduledBackup.php +++ b/app/Http/Livewire/Project/Database/CreateScheduledBackup.php @@ -39,7 +39,7 @@ class CreateScheduledBackup extends Component 's3_storage_id' => $this->s3_storage_id, 'database_id' => $this->database->id, 'database_type' => $this->database->getMorphClass(), - 'team_id' => auth()->user()->currentTeam()->id, + 'team_id' => currentTeam()->id, ]); $this->emit('refreshScheduledBackups'); } catch (\Exception $e) { diff --git a/app/Http/Livewire/Project/Database/Heading.php b/app/Http/Livewire/Project/Database/Heading.php index 59f4955a6..347c439a6 100644 --- a/app/Http/Livewire/Project/Database/Heading.php +++ b/app/Http/Livewire/Project/Database/Heading.php @@ -3,7 +3,7 @@ namespace App\Http\Livewire\Project\Database; use App\Actions\Database\StartPostgresql; -use App\Jobs\ContainerStatusJob; +use App\Jobs\DatabaseContainerStatusJob; use App\Notifications\Application\StatusChanged; use Livewire\Component; @@ -25,9 +25,8 @@ class Heading extends Component public function check_status() { - dispatch_sync(new ContainerStatusJob( - resource: $this->database, - container_name: generate_container_name($this->database->uuid), + dispatch_sync(new DatabaseContainerStatusJob( + database: $this->database, )); $this->database->refresh(); } @@ -45,7 +44,7 @@ class Heading extends Component ); $this->database->status = 'stopped'; $this->database->save(); - $this->database->environment->project->team->notify(new StatusChanged($this->database)); + // $this->database->environment->project->team->notify(new StatusChanged($this->database)); } public function start() diff --git a/app/Http/Livewire/Project/New/EmptyProject.php b/app/Http/Livewire/Project/New/EmptyProject.php index e77ab983e..9dba4338b 100644 --- a/app/Http/Livewire/Project/New/EmptyProject.php +++ b/app/Http/Livewire/Project/New/EmptyProject.php @@ -11,7 +11,7 @@ class EmptyProject extends Component { $project = Project::create([ 'name' => generate_random_name(), - 'team_id' => auth()->user()->currentTeam()->id, + 'team_id' => currentTeam()->id, ]); return redirect()->route('project.show', ['project_uuid' => $project->uuid, 'environment_name' => 'production']); } diff --git a/app/Http/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php b/app/Http/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php index a1f275f32..d18fe2541 100644 --- a/app/Http/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php +++ b/app/Http/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php @@ -43,19 +43,19 @@ class GithubPrivateRepositoryDeployKey extends Component 'publish_directory' => 'Publish directory', ]; private object $repository_url_parsed; - private GithubApp|GitlabApp $git_source; + private GithubApp|GitlabApp|null $git_source = null; private string $git_host; private string $git_repository; private string $git_branch; public function mount() { - if (is_dev()) { + if (isDev()) { $this->repository_url = 'https://github.com/coollabsio/coolify-examples'; } $this->parameters = get_route_parameters(); $this->query = request()->query(); - $this->private_keys = PrivateKey::where('team_id', auth()->user()->currentTeam()->id)->where('id', '!=', 0)->get(); + $this->private_keys = PrivateKey::where('team_id', currentTeam()->id)->where('id', '!=', 0)->get(); } public function instantSave() diff --git a/app/Http/Livewire/Project/New/PublicGitRepository.php b/app/Http/Livewire/Project/New/PublicGitRepository.php index 7fd15cd34..6229a7fc6 100644 --- a/app/Http/Livewire/Project/New/PublicGitRepository.php +++ b/app/Http/Livewire/Project/New/PublicGitRepository.php @@ -45,7 +45,7 @@ class PublicGitRepository extends Component public function mount() { - if (is_dev()) { + if (isDev()) { $this->repository_url = 'https://github.com/coollabsio/coolify-examples'; $this->port = 3000; } diff --git a/app/Http/Livewire/Project/New/Select.php b/app/Http/Livewire/Project/New/Select.php index f95599154..b3109ef8c 100644 --- a/app/Http/Livewire/Project/New/Select.php +++ b/app/Http/Livewire/Project/New/Select.php @@ -3,6 +3,7 @@ namespace App\Http\Livewire\Project\New; use App\Models\Server; +use Countable; use Livewire\Component; class Select extends Component @@ -11,7 +12,7 @@ class Select extends Component public string $type; public string $server_id; public string $destination_uuid; - public $servers = []; + public Countable|array|Server $servers; public $destinations = []; public array $parameters; @@ -23,6 +24,13 @@ class Select extends Component public function set_type(string $type) { $this->type = $type; + if (count($this->servers) === 1) { + $server = $this->servers->first(); + $this->set_server($server); + if (count($server->destinations()) === 1) { + $this->set_destination($server->destinations()->first()->uuid); + } + } $this->current_step = 'servers'; } @@ -46,6 +54,6 @@ class Select extends Component public function load_servers() { - $this->servers = Server::ownedByCurrentTeam()->get(); + $this->servers = Server::isUsable()->get(); } } diff --git a/app/Http/Livewire/Project/New/SimpleDockerfile.php b/app/Http/Livewire/Project/New/SimpleDockerfile.php index bbc82fcd5..e755c8c0f 100644 --- a/app/Http/Livewire/Project/New/SimpleDockerfile.php +++ b/app/Http/Livewire/Project/New/SimpleDockerfile.php @@ -19,7 +19,7 @@ class SimpleDockerfile extends Component { $this->parameters = get_route_parameters(); $this->query = request()->query(); - if (is_dev()) { + if (isDev()) { $this->dockerfile = 'FROM nginx EXPOSE 80 CMD ["nginx", "-g", "daemon off;"] @@ -59,6 +59,10 @@ CMD ["nginx", "-g", "daemon off;"] 'source_id' => 0, 'source_type' => GithubApp::class ]); + $application->update([ + 'name' => 'dockerfile-' . $application->id + ]); + redirect()->route('project.application.configuration', [ 'application_uuid' => $application->uuid, 'environment_name' => $environment->name, diff --git a/app/Http/Livewire/Server/Form.php b/app/Http/Livewire/Server/Form.php index 4dcebc101..290c06dd7 100644 --- a/app/Http/Livewire/Server/Form.php +++ b/app/Http/Livewire/Server/Form.php @@ -42,7 +42,7 @@ class Form extends Component public function installDocker() { - $activity = resolve(InstallDocker::class)($this->server, auth()->user()->currentTeam()); + $activity = resolve(InstallDocker::class)($this->server, currentTeam()); $this->emit('newMonitorActivity', $activity->id); } diff --git a/app/Http/Livewire/Server/New/ByIp.php b/app/Http/Livewire/Server/New/ByIp.php index c4f340413..e6813a55f 100644 --- a/app/Http/Livewire/Server/New/ByIp.php +++ b/app/Http/Livewire/Server/New/ByIp.php @@ -65,7 +65,7 @@ class ByIp extends Component 'ip' => $this->ip, 'user' => $this->user, 'port' => $this->port, - 'team_id' => auth()->user()->currentTeam()->id, + 'team_id' => currentTeam()->id, 'private_key_id' => $this->private_key_id, ]); $server->settings->is_part_of_swarm = $this->is_part_of_swarm; diff --git a/app/Http/Livewire/Server/Proxy.php b/app/Http/Livewire/Server/Proxy.php index 1f51d1b1c..944b5e898 100644 --- a/app/Http/Livewire/Server/Proxy.php +++ b/app/Http/Livewire/Server/Proxy.php @@ -35,7 +35,7 @@ class Proxy extends Component $this->emit('proxyStatusUpdated'); } - public function select_proxy(string $proxy_type) + public function select_proxy(ProxyTypes $proxy_type) { $this->server->proxy->type = $proxy_type; $this->server->proxy->status = 'exited'; diff --git a/app/Http/Livewire/Server/Proxy/Deploy.php b/app/Http/Livewire/Server/Proxy/Deploy.php index 9ec3898f1..f78763479 100644 --- a/app/Http/Livewire/Server/Proxy/Deploy.php +++ b/app/Http/Livewire/Server/Proxy/Deploy.php @@ -17,7 +17,7 @@ class Deploy extends Component $this->server->proxy->last_applied_settings && $this->server->proxy->last_saved_settings !== $this->server->proxy->last_applied_settings ) { - $this->emit('saveConfiguration', $server); + $this->emit('saveConfiguration', $this->server); } $activity = resolve(StartProxy::class)($this->server); $this->emit('newMonitorActivity', $activity->id); diff --git a/app/Http/Livewire/Settings/Backup.php b/app/Http/Livewire/Settings/Backup.php index fcb105fd4..263db027d 100644 --- a/app/Http/Livewire/Settings/Backup.php +++ b/app/Http/Livewire/Settings/Backup.php @@ -65,7 +65,7 @@ class Backup extends Component 'frequency' => '0 0 * * *', 'database_id' => $this->database->id, 'database_type' => 'App\Models\StandalonePostgresql', - 'team_id' => auth()->user()->currentTeam()->id, + 'team_id' => currentTeam()->id, ]); $this->database->refresh(); $this->backup->refresh(); diff --git a/app/Http/Livewire/Source/Github/Create.php b/app/Http/Livewire/Source/Github/Create.php index 862964c45..4d6f8b26b 100644 --- a/app/Http/Livewire/Source/Github/Create.php +++ b/app/Http/Livewire/Source/Github/Create.php @@ -40,7 +40,7 @@ class Create extends Component 'custom_user' => $this->custom_user, 'custom_port' => $this->custom_port, 'is_system_wide' => $this->is_system_wide, - 'team_id' => auth()->user()->currentTeam()->id, + 'team_id' => currentTeam()->id, ]); redirect()->route('source.github.show', ['github_app_uuid' => $github_app->uuid]); } catch (\Exception $e) { diff --git a/app/Http/Livewire/Subscription/Actions.php b/app/Http/Livewire/Subscription/Actions.php index 588a0521c..7de0ec728 100644 --- a/app/Http/Livewire/Subscription/Actions.php +++ b/app/Http/Livewire/Subscription/Actions.php @@ -10,14 +10,14 @@ class Actions extends Component public function cancel() { try { - $subscription_id = auth()->user()->currentTeam()->subscription->lemon_subscription_id; + $subscription_id = currentTeam()->subscription->lemon_subscription_id; if (!$subscription_id) { throw new \Exception('No subscription found'); } $response = Http::withHeaders([ 'Accept' => 'application/vnd.api+json', 'Content-Type' => 'application/vnd.api+json', - 'Authorization' => 'Bearer ' . config('coolify.lemon_squeezy_api_key'), + 'Authorization' => 'Bearer ' . config('subscription.lemon_squeezy_api_key'), ])->delete('https://api.lemonsqueezy.com/v1/subscriptions/' . $subscription_id); $json = $response->json(); if ($response->failed()) { @@ -37,14 +37,14 @@ class Actions extends Component public function resume() { try { - $subscription_id = auth()->user()->currentTeam()->subscription->lemon_subscription_id; + $subscription_id = currentTeam()->subscription->lemon_subscription_id; if (!$subscription_id) { throw new \Exception('No subscription found'); } $response = Http::withHeaders([ 'Accept' => 'application/vnd.api+json', 'Content-Type' => 'application/vnd.api+json', - 'Authorization' => 'Bearer ' . config('coolify.lemon_squeezy_api_key'), + 'Authorization' => 'Bearer ' . config('subscription.lemon_squeezy_api_key'), ])->patch('https://api.lemonsqueezy.com/v1/subscriptions/' . $subscription_id, [ 'data' => [ 'type' => 'subscriptions', @@ -69,4 +69,8 @@ class Actions extends Component return general_error_handler($e, $this); } } + public function stripeCustomerPortal() { + $session = getStripeCustomerPortalSession(currentTeam()); + redirect($session->url); + } } diff --git a/app/Http/Livewire/Subscription/PricingPlans.php b/app/Http/Livewire/Subscription/PricingPlans.php new file mode 100644 index 000000000..fcf24237f --- /dev/null +++ b/app/Http/Livewire/Subscription/PricingPlans.php @@ -0,0 +1,66 @@ +emit('error', 'Price ID not found! Please contact the administrator.'); + return; + } + $payload = [ + 'client_reference_id' => auth()->user()->id . ':' . currentTeam()->id, + 'line_items' => [[ + 'price' => $priceId, + 'quantity' => 1, + ]], + 'tax_id_collection' => [ + 'enabled' => true, + ], + 'mode' => 'subscription', + 'success_url' => route('subscription.success'), + 'cancel_url' => route('subscription.show',['cancelled' => true]), + ]; + $customer = currentTeam()->subscription?->stripe_customer_id ?? null; + if ($customer) { + $payload['customer'] = $customer; + $payload['customer_update'] = [ + 'name' => 'auto' + ]; + } else { + $payload['customer_email'] = auth()->user()->email; + } + $session = Session::create($payload); + return redirect($session->url, 303); + } +} diff --git a/app/Http/Livewire/Team/Create.php b/app/Http/Livewire/Team/Create.php index 6c1c71580..bd2c24313 100644 --- a/app/Http/Livewire/Team/Create.php +++ b/app/Http/Livewire/Team/Create.php @@ -29,7 +29,7 @@ class Create extends Component 'personal_team' => false, ]); auth()->user()->teams()->attach($team, ['role' => 'admin']); - session(['currentTeam' => $team]); + refreshSession(); return redirect()->route('team.show'); } catch (\Throwable $th) { return general_error_handler($th, $this); diff --git a/app/Http/Livewire/Team/Delete.php b/app/Http/Livewire/Team/Delete.php index d14e82ccc..5e206704b 100644 --- a/app/Http/Livewire/Team/Delete.php +++ b/app/Http/Livewire/Team/Delete.php @@ -9,7 +9,7 @@ class Delete extends Component { public function delete() { - $currentTeam = auth()->user()->currentTeam(); + $currentTeam = currentTeam(); $currentTeam->delete(); $team = auth()->user()->teams()->first(); @@ -24,7 +24,7 @@ class Delete extends Component } }); - session(['currentTeam' => $team]); + refreshSession(); return redirect()->route('team.show'); } } diff --git a/app/Http/Livewire/Team/Form.php b/app/Http/Livewire/Team/Form.php index eedd60c83..caa6d2c9e 100644 --- a/app/Http/Livewire/Team/Form.php +++ b/app/Http/Livewire/Team/Form.php @@ -19,7 +19,7 @@ class Form extends Component public function mount() { - $this->team = auth()->user()->currentTeam(); + $this->team = currentTeam(); } public function submit() @@ -27,7 +27,7 @@ class Form extends Component $this->validate(); try { $this->team->save(); - session(['currentTeam' => $this->team]); + refreshSession(); $this->emit('reloadWindow'); } catch (\Throwable $th) { return general_error_handler($th, $this); diff --git a/app/Http/Livewire/Team/Invitations.php b/app/Http/Livewire/Team/Invitations.php index ba0b654aa..ba6c1e91f 100644 --- a/app/Http/Livewire/Team/Invitations.php +++ b/app/Http/Livewire/Team/Invitations.php @@ -18,6 +18,6 @@ class Invitations extends Component public function refreshInvitations() { - $this->invitations = TeamInvitation::whereTeamId(auth()->user()->currentTeam()->id)->get(); + $this->invitations = TeamInvitation::whereTeamId(currentTeam()->id)->get(); } } diff --git a/app/Http/Livewire/Team/InviteLink.php b/app/Http/Livewire/Team/InviteLink.php index ddbe75182..b554e6575 100644 --- a/app/Http/Livewire/Team/InviteLink.php +++ b/app/Http/Livewire/Team/InviteLink.php @@ -15,7 +15,7 @@ class InviteLink extends Component public function mount() { - $this->email = is_dev() ? 'test3@example.com' : ''; + $this->email = isDev() ? 'test3@example.com' : ''; } public function viaEmail() @@ -35,9 +35,9 @@ class InviteLink extends Component return general_error_handler(that: $this, customErrorMessage: "$this->email must be registered first (or activate transactional emails to invite via email)."); } - $member_emails = auth()->user()->currentTeam()->members()->get()->pluck('email'); + $member_emails = currentTeam()->members()->get()->pluck('email'); if ($member_emails->contains($this->email)) { - return general_error_handler(that: $this, customErrorMessage: "$this->email is already a member of " . auth()->user()->currentTeam()->name . "."); + return general_error_handler(that: $this, customErrorMessage: "$this->email is already a member of " . currentTeam()->name . "."); } $invitation = TeamInvitation::whereEmail($this->email); @@ -53,7 +53,7 @@ class InviteLink extends Component } TeamInvitation::firstOrCreate([ - 'team_id' => auth()->user()->currentTeam()->id, + 'team_id' => currentTeam()->id, 'uuid' => $uuid, 'email' => $this->email, 'role' => $this->role, diff --git a/app/Http/Livewire/Team/Member.php b/app/Http/Livewire/Team/Member.php index 237d38818..df8fcd7af 100644 --- a/app/Http/Livewire/Team/Member.php +++ b/app/Http/Livewire/Team/Member.php @@ -11,19 +11,19 @@ class Member extends Component public function makeAdmin() { - $this->member->teams()->updateExistingPivot(auth()->user()->currentTeam()->id, ['role' => 'admin']); + $this->member->teams()->updateExistingPivot(currentTeam()->id, ['role' => 'admin']); $this->emit('reloadWindow'); } public function makeReadonly() { - $this->member->teams()->updateExistingPivot(auth()->user()->currentTeam()->id, ['role' => 'member']); + $this->member->teams()->updateExistingPivot(currentTeam()->id, ['role' => 'member']); $this->emit('reloadWindow'); } public function remove() { - $this->member->teams()->detach(auth()->user()->currentTeam()); + $this->member->teams()->detach(currentTeam()); $this->emit('reloadWindow'); } } diff --git a/app/Http/Livewire/Team/Storage/Create.php b/app/Http/Livewire/Team/Storage/Create.php index ed7d277fb..e1330784a 100644 --- a/app/Http/Livewire/Team/Storage/Create.php +++ b/app/Http/Livewire/Team/Storage/Create.php @@ -36,7 +36,7 @@ class Create extends Component public function mount() { - if (is_dev()) { + if (isDev()) { $this->name = 'Local MinIO'; $this->description = 'Local MinIO'; $this->key = 'minioadmin'; @@ -62,7 +62,7 @@ class Create extends Component } else { $this->storage->endpoint = $this->endpoint; } - $this->storage->team_id = auth()->user()->currentTeam()->id; + $this->storage->team_id = currentTeam()->id; $this->storage->testConnection(); $this->emit('success', 'Connection is working. Tested with "ListObjectsV2" action.'); $this->storage->save(); diff --git a/app/Http/Livewire/Upgrade.php b/app/Http/Livewire/Upgrade.php index f46e37ff7..e79c73fb7 100644 --- a/app/Http/Livewire/Upgrade.php +++ b/app/Http/Livewire/Upgrade.php @@ -18,7 +18,7 @@ class Upgrade extends Component $this->latestVersion = get_latest_version_of_coolify(); $currentVersion = config('version'); version_compare($currentVersion, $this->latestVersion, '<') ? $this->isUpgradeAvailable = true : $this->isUpgradeAvailable = false; - if (is_dev()) { + if (isDev()) { $this->isUpgradeAvailable = true; } $settings = InstanceSettings::get(); diff --git a/app/Http/Livewire/Waitlist.php b/app/Http/Livewire/Waitlist.php index de633ceac..d9b3b92ab 100644 --- a/app/Http/Livewire/Waitlist.php +++ b/app/Http/Livewire/Waitlist.php @@ -17,7 +17,7 @@ class Waitlist extends Component ]; public function mount() { - if (is_dev()) { + if (isDev()) { $this->email = 'waitlist@example.com'; } } diff --git a/app/Http/Middleware/IsBoardingFlow.php b/app/Http/Middleware/IsBoardingFlow.php new file mode 100644 index 000000000..83662a073 --- /dev/null +++ b/app/Http/Middleware/IsBoardingFlow.php @@ -0,0 +1,24 @@ +path(), allowedPathsForBoardingAccounts())) { + return redirect('boarding'); + } + return $next($request); + } +} diff --git a/app/Http/Middleware/SubscriptionValid.php b/app/Http/Middleware/SubscriptionValid.php index f84e6eede..824133f5a 100644 --- a/app/Http/Middleware/SubscriptionValid.php +++ b/app/Http/Middleware/SubscriptionValid.php @@ -10,6 +10,9 @@ class SubscriptionValid { public function handle(Request $request, Closure $next): Response { + if (isInstanceAdmin()) { + return $next($request); + } if (!auth()->user() || !is_cloud()) { if ($request->path() === 'subscription') { return redirect('/'); @@ -17,32 +20,17 @@ class SubscriptionValid return $next($request); } } - $is_instance_admin = is_instance_admin(); - if ($is_instance_admin) { - return $next($request); - } - - if (is_subscription_active() && $request->path() === 'subscription') { + if (isSubscriptionActive() && $request->path() === 'subscription') { + // ray('active subscription Middleware'); return redirect('/'); } - if (is_subscription_in_grace_period()) { + if (isSubscriptionOnGracePeriod()) { + // ray('is_subscription_in_grace_period Middleware'); return $next($request); } - if (!is_subscription_active() && !is_subscription_in_grace_period()) { - ray('SubscriptionValid Middleware'); - - $allowed_paths = [ - 'subscription', - 'login', - 'register', - 'waitlist', - 'force-password-reset', - 'logout', - 'livewire/message/force-password-reset', - 'livewire/message/check-license', - 'livewire/message/switch-team', - ]; - if (!in_array($request->path(), $allowed_paths)) { + if (!isSubscriptionActive() && !isSubscriptionOnGracePeriod()) { + // ray('SubscriptionValid Middleware'); + if (!in_array($request->path(), allowedPathsForUnsubscribedAccounts())) { return redirect('subscription'); } else { return $next($request); diff --git a/app/Jobs/ApplicationContainerStatusJob.php b/app/Jobs/ApplicationContainerStatusJob.php new file mode 100644 index 000000000..f7496770d --- /dev/null +++ b/app/Jobs/ApplicationContainerStatusJob.php @@ -0,0 +1,54 @@ +containerName = generateApplicationContainerName($application->uuid, $pullRequestId); + } + + public function uniqueId(): string + { + return $this->containerName; + } + + public function handle(): void + { + try { + $status = getApplicationContainerStatus(application: $this->application); + if ($this->application->status === 'running' && $status !== 'running') { + // $this->application->environment->project->team->notify(new StatusChanged($this->application)); + } + + if ($this->pullRequestId !== 0) { + $preview = ApplicationPreview::findPreviewByApplicationAndPullId($this->application->id, $this->pullRequestId); + $preview->status = $status; + $preview->save(); + } else { + $this->application->status = $status; + $this->application->save(); + } + } catch (\Exception $th) { + ray($th->getMessage()); + throw $th; + } + } +} diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index fc0a3a1b7..5f24d4ab8 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -50,6 +50,7 @@ class ApplicationDeploymentJob implements ShouldQueue private ApplicationPreview|null $preview = null; private string $container_name; + private string|null $currently_running_container_name = null; private string $workdir; private string $configuration_dir; private string $build_workdir; @@ -86,7 +87,7 @@ class ApplicationDeploymentJob implements ShouldQueue $this->build_workdir = "{$this->workdir}" . rtrim($this->application->base_directory, '/'); $this->is_debug_enabled = $this->application->settings->is_debug_enabled; - $this->container_name = generate_container_name($this->application->uuid, $this->pull_request_id); + $this->container_name = generateApplicationContainerName($this->application->uuid, $this->pull_request_id); $this->private_key_location = save_private_key_for_server($this->server); $this->saved_outputs = collect(); @@ -113,6 +114,10 @@ class ApplicationDeploymentJob implements ShouldQueue public function handle(): void { // ray()->measure(); + $containers = getCurrentApplicationContainerStatus($this->server, $this->application->id); + if ($containers->count() > 0) { + $this->currently_running_container_name = data_get($containers[0], 'Names'); + } $this->application_deployment_queue->update([ 'status' => ApplicationDeploymentStatus::IN_PROGRESS->value, ]); @@ -131,6 +136,7 @@ class ApplicationDeploymentJob implements ShouldQueue } catch (Exception $e) { ray($e); $this->fail($e); + throw $e; } finally { if (isset($this->docker_compose_base64)) { $readme = generate_readme_file($this->application->name, $this->application_deployment_queue->updated_at); @@ -175,9 +181,9 @@ class ApplicationDeploymentJob implements ShouldQueue $this->generate_build_env_variables(); $this->add_build_env_variables_to_dockerfile(); $this->build_image(); - $this->stop_running_container(); - $this->start_by_compose_file(); + $this->rolling_update(); } + private function deploy() { $this->execute_remote_command( @@ -206,8 +212,7 @@ class ApplicationDeploymentJob implements ShouldQueue "echo 'Docker Image found locally with the same Git Commit SHA {$this->application->uuid}:{$this->commit}. Build step skipped...'" ]); $this->generate_compose_file(); - $this->stop_running_container(); - $this->start_by_compose_file(); + $this->rolling_update(); return; } } @@ -219,8 +224,54 @@ class ApplicationDeploymentJob implements ShouldQueue $this->generate_build_env_variables(); $this->add_build_env_variables_to_dockerfile(); $this->build_image(); - $this->stop_running_container(); + $this->rolling_update(); + } + + private function rolling_update() + { $this->start_by_compose_file(); + $this->health_check(); + $this->stop_running_container(); + } + private function health_check() + { + ray('New container name: ',$this->container_name); + if ($this->container_name) { + $counter = 0; + $this->execute_remote_command( + [ + "echo 'Waiting for health check to pass on the new version of your application.'" + ], + ); + while ($counter < $this->application->health_check_retries) { + $this->execute_remote_command( + [ + "echo 'Attempt {$counter} of {$this->application->health_check_retries}'" + ], + [ + "docker inspect --format='{{json .State.Health.Status}}' {$this->container_name}", + "hidden" => true, + "save" => "health_check" + ], + + ); + $this->execute_remote_command( + [ + "echo 'New application version health check status: {$this->saved_outputs->get('health_check')}'" + ], + ); + if (Str::of($this->saved_outputs->get('health_check'))->contains('healthy')) { + $this->execute_remote_command( + [ + "echo 'Rolling update completed.'" + ], + ); + break; + } + $counter++; + sleep($this->application->health_check_interval); + } + } } private function deploy_pull_request() { @@ -241,8 +292,7 @@ class ApplicationDeploymentJob implements ShouldQueue // $this->generate_build_env_variables(); // $this->add_build_env_variables_to_dockerfile(); $this->build_image(); - $this->stop_running_container(); - $this->start_by_compose_file(); + $this->rolling_update(); } private function prepare_builder_image() @@ -409,7 +459,7 @@ class ApplicationDeploymentJob implements ShouldQueue $this->container_name => [ 'image' => $this->production_image_name, 'container_name' => $this->container_name, - 'restart' => 'always', + 'restart' => RESTART_MODE, 'environment' => $environment_variables, 'labels' => $this->set_labels_for_applications(), 'expose' => $ports, @@ -539,8 +589,8 @@ class ApplicationDeploymentJob implements ShouldQueue $schema = $url->getScheme(); $slug = Str::slug($host . $path); - $http_label = "{$this->application->uuid}-{$slug}-http"; - $https_label = "{$this->application->uuid}-{$slug}-https"; + $http_label = "{$this->container_name}-{$slug}-http"; + $https_label = "{$this->container_name}-{$slug}-https"; if ($schema === 'https') { // Set labels for https @@ -647,23 +697,22 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf"); private function stop_running_container() { - $this->execute_remote_command( - ["echo -n 'Removing old running application.'"], - [$this->execute_in_builder("docker rm -f $this->container_name >/dev/null 2>&1"), "hidden" => true], - ); + if ($this->currently_running_container_name) { + $this->execute_remote_command( + ["echo -n 'Removing old application version.'"], + [$this->execute_in_builder("docker rm -f $this->currently_running_container_name >/dev/null 2>&1"), "hidden" => true], + ); + } } private function start_by_compose_file() { $this->execute_remote_command( - ["echo -n 'Starting new application... '"], + ["echo -n 'Rolling update started.'"], [$this->execute_in_builder("docker compose --project-directory {$this->workdir} up -d >/dev/null"), "hidden" => true], - ["echo 'Done. 🎉'"], ); } - - private function generate_build_env_variables() { $this->build_args = collect(["--build-arg SOURCE_COMMIT={$this->commit}"]); diff --git a/app/Jobs/CheckResaleLicenseJob.php b/app/Jobs/CheckResaleLicenseJob.php index f14214733..30f9ff198 100644 --- a/app/Jobs/CheckResaleLicenseJob.php +++ b/app/Jobs/CheckResaleLicenseJob.php @@ -22,7 +22,9 @@ class CheckResaleLicenseJob implements ShouldQueue try { resolve(CheckResaleLicense::class)(); } catch (\Throwable $th) { + send_internal_notification('CheckResaleLicenseJob failed with: ' . $th->getMessage()); ray($th); + throw $th; } } } diff --git a/app/Jobs/CleanupInstanceStuffsJob.php b/app/Jobs/CleanupInstanceStuffsJob.php index e67a562bd..b133279ea 100644 --- a/app/Jobs/CleanupInstanceStuffsJob.php +++ b/app/Jobs/CleanupInstanceStuffsJob.php @@ -31,6 +31,7 @@ class CleanupInstanceStuffsJob implements ShouldQueue, ShouldBeUnique } catch (\Exception $e) { send_internal_notification('CleanupInstanceStuffsJob failed with error: ' . $e->getMessage()); ray($e->getMessage()); + throw $e; } } diff --git a/app/Jobs/ContainerStatusJob.php b/app/Jobs/ContainerStatusJob.php deleted file mode 100644 index f2be8b4d7..000000000 --- a/app/Jobs/ContainerStatusJob.php +++ /dev/null @@ -1,54 +0,0 @@ -resource = $resource; - $this->container_name = $container_name; - $this->pull_request_id = $pull_request_id; - } - - public function uniqueId(): string - { - return $this->container_name; - } - - public function handle(): void - { - try { - $status = get_container_status(server: $this->resource->destination->server, container_id: $this->container_name, throwError: false); - if ($this->resource->status === 'running' && $status !== 'running') { - $this->resource->environment->project->team->notify(new StatusChanged($this->resource)); - } - - if ($this->pull_request_id) { - $preview = ApplicationPreview::findPreviewByApplicationAndPullId($this->resource->id, $this->pull_request_id); - $preview->status = $status; - $preview->save(); - } else { - $this->resource->status = $status; - $this->resource->save(); - } - } catch (\Exception $e) { - ray($e->getMessage()); - } - } -} diff --git a/app/Jobs/CoolifyTask.php b/app/Jobs/CoolifyTask.php index e3dd9640f..176841b20 100755 --- a/app/Jobs/CoolifyTask.php +++ b/app/Jobs/CoolifyTask.php @@ -28,7 +28,6 @@ class CoolifyTask implements ShouldQueue */ public function handle(): void { - $remote_process = resolve(RunRemoteProcess::class, [ 'activity' => $this->activity, 'ignore_errors' => $this->ignore_errors, diff --git a/app/Jobs/DatabaseBackupJob.php b/app/Jobs/DatabaseBackupJob.php index eed6c5de8..6e1ee2f0c 100644 --- a/app/Jobs/DatabaseBackupJob.php +++ b/app/Jobs/DatabaseBackupJob.php @@ -64,35 +64,42 @@ class DatabaseBackupJob implements ShouldQueue public function handle(): void { - if ($this->database_status !== 'running') { - ray('database not running'); - return; - } - $this->container_name = $this->database->uuid; - $this->backup_dir = backup_dir() . "/databases/" . Str::of($this->team->name)->slug() . '-' . $this->team->id . '/' . $this->container_name; + try { + if ($this->database_status !== 'running') { + ray('database not running'); + return; + } + $this->container_name = $this->database->uuid; + $this->backup_dir = backup_dir() . "/databases/" . Str::of($this->team->name)->slug() . '-' . $this->team->id . '/' . $this->container_name; - if ($this->database->name === 'coolify-db') { - $this->container_name = "coolify-db"; - $ip = Str::slug($this->server->ip); - $this->backup_dir = backup_dir() . "/coolify" . "/coolify-db-$ip"; - } - $this->backup_file = "/dumpall-" . Carbon::now()->timestamp . ".sql"; - $this->backup_location = $this->backup_dir . $this->backup_file; + if ($this->database->name === 'coolify-db') { + $this->container_name = "coolify-db"; + $ip = Str::slug($this->server->ip); + $this->backup_dir = backup_dir() . "/coolify" . "/coolify-db-$ip"; + } + $this->backup_file = "/dumpall-" . Carbon::now()->timestamp . ".sql"; + $this->backup_location = $this->backup_dir . $this->backup_file; - $this->backup_log = ScheduledDatabaseBackupExecution::create([ - 'filename' => $this->backup_location, - 'scheduled_database_backup_id' => $this->backup->id, - ]); - if ($this->database_type === 'standalone-postgresql') { - $this->backup_standalone_postgresql(); + $this->backup_log = ScheduledDatabaseBackupExecution::create([ + 'filename' => $this->backup_location, + 'scheduled_database_backup_id' => $this->backup->id, + ]); + if ($this->database_type === 'standalone-postgresql') { + $this->backup_standalone_postgresql(); + } + $this->calculate_size(); + $this->remove_old_backups(); + if ($this->backup->save_s3) { + $this->upload_to_s3(); + } + $this->save_backup_logs(); + // TODO: Notify user + } catch (\Throwable $th) { + ray($th->getMessage()); + send_internal_notification('DatabaseBackupJob failed with: ' . $th->getMessage()); + throw $th; } - $this->calculate_size(); - $this->remove_old_backups(); - if ($this->backup->save_s3) { - $this->upload_to_s3(); - } - $this->save_backup_logs(); - // TODO: Notify user + } private function backup_standalone_postgresql(): void diff --git a/app/Jobs/DatabaseContainerStatusJob.php b/app/Jobs/DatabaseContainerStatusJob.php new file mode 100644 index 000000000..1630545c5 --- /dev/null +++ b/app/Jobs/DatabaseContainerStatusJob.php @@ -0,0 +1,56 @@ +containerName = $database->uuid; + } + + public function uniqueId(): string + { + return $this->containerName; + } + + public function handle(): void + { + try { + $status = getContainerStatus( + server: $this->database->destination->server, + container_id: $this->containerName, + throwError: false + ); + + if ($this->database->status === 'running' && $status !== 'running') { + if (data_get($this->database, 'environment.project.team')) { + // $this->database->environment->project->team->notify(new StatusChanged($this->database)); + } + } + if ($this->database->status !== $status) { + $this->database->status = $status; + $this->database->save(); + } + } catch (\Exception $e) { + send_internal_notification('DatabaseContainerStatusJob failed with: ' . $e->getMessage()); + ray($e->getMessage()); + throw $e; + } + } +} diff --git a/app/Jobs/DockerCleanupJob.php b/app/Jobs/DockerCleanupJob.php index 52279c241..7a8571c00 100644 --- a/app/Jobs/DockerCleanupJob.php +++ b/app/Jobs/DockerCleanupJob.php @@ -32,7 +32,7 @@ class DockerCleanupJob implements ShouldQueue try { $servers = Server::all(); foreach ($servers as $server) { - if (is_dev()) { + if (isDev()) { $docker_root_filesystem = "/"; } else { $docker_root_filesystem = instant_remote_process(['stat --printf=%m $(docker info --format "{{json .DockerRootDir}}" |sed \'s/"//g\')'], $server); @@ -49,14 +49,17 @@ class DockerCleanupJob implements ShouldQueue } } } catch (\Exception $e) { + send_internal_notification('DockerCleanupJob failed with: ' . $e->getMessage()); ray($e->getMessage()); + throw $e; } } private function get_disk_usage(Server $server, string $docker_root_filesystem) { - $disk_usage = json_decode(instant_remote_process(['df -hP | awk \'BEGIN {printf"{\"disks\":["}{if($1=="Filesystem")next;if(a)printf",";printf"{\"mount\":\""$6"\",\"size\":\""$2"\",\"used\":\""$3"\",\"avail\":\""$4"\",\"use%\":\""$5"\"}";a++;}END{print"]}";}\''], $server), true); + $disk_usage = json_decode(instant_remote_process(['df -hP | awk \'BEGIN {printf"{\\"disks\\":["}{if($1=="Filesystem")next;if(a)printf",";printf"{\\"mount\\":\\""$6"\\",\\"size\\":\\""$2"\\",\\"used\\":\\""$3"\\",\\"avail\\":\\""$4"\\",\\"use%\\":\\""$5"\\"}";a++;}END{print"]}";}\''], $server), true); $mount_point = collect(data_get($disk_usage, 'disks'))->where('mount', $docker_root_filesystem)->first(); + ray($mount_point); return Str::of(data_get($mount_point, 'use%'))->trim()->replace('%', '')->value(); } } diff --git a/app/Jobs/ProxyCheckJob.php b/app/Jobs/ProxyCheckJob.php index 7c498107d..9d5559053 100755 --- a/app/Jobs/ProxyCheckJob.php +++ b/app/Jobs/ProxyCheckJob.php @@ -22,9 +22,14 @@ class ProxyCheckJob implements ShouldQueue { try { $container_name = 'coolify-proxy'; - $servers = Server::isUsable()->whereNotNull('proxy')->get(); + $servers = Server::all(); foreach ($servers as $server) { - $status = get_container_status(server: $server, container_id: $container_name); + if ( + $server->settings->is_reachable === false || $server->settings->is_usable === false + ) { + continue; + } + $status = getContainerStatus(server: $server, container_id: $container_name); if ($status === 'running') { continue; } @@ -33,7 +38,8 @@ class ProxyCheckJob implements ShouldQueue } } catch (\Throwable $th) { ray($th->getMessage()); - //throw $th; + send_internal_notification('ProxyCheckJob failed with: ' . $th->getMessage()); + throw $th; } } } diff --git a/app/Jobs/ProxyContainerStatusJob.php b/app/Jobs/ProxyContainerStatusJob.php index 68372895a..aa6056434 100644 --- a/app/Jobs/ProxyContainerStatusJob.php +++ b/app/Jobs/ProxyContainerStatusJob.php @@ -39,9 +39,9 @@ class ProxyContainerStatusJob implements ShouldQueue, ShouldBeUnique public function handle(): void { try { - $container = get_container_status(server: $this->server, all_data: true, container_id: 'coolify-proxy', throwError: true); - $status = $container['State']['Status']; - if ($this->server->proxy->status !== $status) { + $container = getContainerStatus(server: $this->server, all_data: true, container_id: 'coolify-proxy', throwError: true); + $status = data_get($container, 'State.Status'); + if (data_get($this->server,'proxy.status') !== $status) { $this->server->proxy->status = $status; if ($this->server->proxy->status === 'running') { $traefik = $container['Config']['Labels']['org.opencontainers.image.title']; @@ -57,6 +57,9 @@ class ProxyContainerStatusJob implements ShouldQueue, ShouldBeUnique $this->server->proxy->status = 'exited'; $this->server->save(); } + send_internal_notification('ProxyContainerStatusJob failed with: ' . $e->getMessage()); + ray($e->getMessage()); + throw $e; } } } diff --git a/app/Jobs/ProxyStartJob.php b/app/Jobs/ProxyStartJob.php index 5119210fc..4e2331a68 100755 --- a/app/Jobs/ProxyStartJob.php +++ b/app/Jobs/ProxyStartJob.php @@ -23,14 +23,15 @@ class ProxyStartJob implements ShouldQueue try { $container_name = 'coolify-proxy'; ray('Starting proxy for server: ' . $this->server->name); - $status = get_container_status(server: $this->server, container_id: $container_name); + $status = getContainerStatus(server: $this->server, container_id: $container_name); if ($status === 'running') { return; } resolve(StartProxy::class)($this->server); } catch (\Throwable $th) { + send_internal_notification('ProxyStartJob failed with: ' . $th->getMessage()); ray($th->getMessage()); - //throw $th; + throw $th; } } } diff --git a/app/Jobs/ResourceStatusJob.php b/app/Jobs/ResourceStatusJob.php index 5ac162d3f..efd6d1445 100644 --- a/app/Jobs/ResourceStatusJob.php +++ b/app/Jobs/ResourceStatusJob.php @@ -3,6 +3,7 @@ namespace App\Jobs; use App\Models\Application; +use App\Models\StandalonePostgresql; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldQueue; @@ -15,23 +16,31 @@ class ResourceStatusJob implements ShouldQueue, ShouldBeUnique use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; public $applications; + public $postgresqls; public function __construct() { $this->applications = Application::all(); + $this->postgresqls = StandalonePostgresql::all(); } public function handle(): void { try { foreach ($this->applications as $application) { - dispatch(new ContainerStatusJob( - resource: $application, - container_name: generate_container_name($application->uuid), + dispatch(new ApplicationContainerStatusJob( + application: $application, )); } - } catch (\Exception $e) { - ray($e->getMessage()); + foreach ($this->postgresqls as $postgresql) { + dispatch(new DatabaseContainerStatusJob( + database: $postgresql, + )); + } + } catch (\Exception $th) { + send_internal_notification('ResourceStatusJob failed with: ' . $th->getMessage()); + ray($th); + throw $th; } } } diff --git a/app/Jobs/SubscriptionInvoiceFailedJob.php b/app/Jobs/SubscriptionInvoiceFailedJob.php new file mode 100755 index 000000000..d6750f953 --- /dev/null +++ b/app/Jobs/SubscriptionInvoiceFailedJob.php @@ -0,0 +1,43 @@ +team); + $mail = new MailMessage(); + $mail->view('emails.subscription-invoice-failed', [ + 'stripeCustomerPortal' => $session->url, + ]); + $mail->subject('Your last payment was failed for Coolify Cloud.'); + $this->team->members()->each(function ($member) use ($mail) { + ray($member); + if ($member->isAdmin()) { + send_user_an_email($mail, $member->email); + } + }); + } catch (\Throwable $th) { + send_internal_notification('SubscriptionInvoiceFailedJob failed with: ' . $th->getMessage()); + ray($th->getMessage()); + throw $th; + } + } +} diff --git a/app/Models/EnvironmentVariable.php b/app/Models/EnvironmentVariable.php index bb4a5ca47..62a08c87f 100644 --- a/app/Models/EnvironmentVariable.php +++ b/app/Models/EnvironmentVariable.php @@ -41,7 +41,7 @@ class EnvironmentVariable extends Model private function get_environment_variables(string $environment_variable): string|null { - // $team_id = auth()->user()->currentTeam()->id; + // $team_id = currentTeam()->id; if (str_contains(trim($environment_variable), '{{') && str_contains(trim($environment_variable), '}}')) { $environment_variable = preg_replace('/\s+/', '', $environment_variable); $environment_variable = str_replace('{{', '', $environment_variable); diff --git a/app/Models/GithubApp.php b/app/Models/GithubApp.php index 74462e8d7..c4cd3568d 100644 --- a/app/Models/GithubApp.php +++ b/app/Models/GithubApp.php @@ -19,12 +19,12 @@ class GithubApp extends BaseModel static public function public() { - return GithubApp::whereTeamId(auth()->user()->currentTeam()->id)->whereisPublic(true)->whereNotNull('app_id')->get(); + return GithubApp::whereTeamId(currentTeam()->id)->whereisPublic(true)->whereNotNull('app_id')->get(); } static public function private() { - return GithubApp::whereTeamId(auth()->user()->currentTeam()->id)->whereisPublic(false)->whereNotNull('app_id')->get(); + return GithubApp::whereTeamId(currentTeam()->id)->whereisPublic(false)->whereNotNull('app_id')->get(); } protected static function booted(): void diff --git a/app/Models/PrivateKey.php b/app/Models/PrivateKey.php index 4d59e9ca8..d4fd0bfa2 100644 --- a/app/Models/PrivateKey.php +++ b/app/Models/PrivateKey.php @@ -16,7 +16,7 @@ class PrivateKey extends BaseModel static public function ownedByCurrentTeam(array $select = ['*']) { $selectArray = collect($select)->concat(['id']); - return PrivateKey::whereTeamId(auth()->user()->currentTeam()->id)->select($selectArray->all()); + return PrivateKey::whereTeamId(currentTeam()->id)->select($selectArray->all()); } public function isEmpty() diff --git a/app/Models/Project.php b/app/Models/Project.php index a50dde167..9bcd2a0fe 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -4,16 +4,11 @@ namespace App\Models; class Project extends BaseModel { - protected $fillable = [ - 'name', - 'description', - 'team_id', - 'project_id' - ]; + protected $guarded = []; static public function ownedByCurrentTeam() { - return Project::whereTeamId(auth()->user()->currentTeam()->id)->orderBy('name'); + return Project::whereTeamId(currentTeam()->id)->orderBy('name'); } protected static function booted() diff --git a/app/Models/S3Storage.php b/app/Models/S3Storage.php index 5cd2f1318..cbcdb97a9 100644 --- a/app/Models/S3Storage.php +++ b/app/Models/S3Storage.php @@ -17,7 +17,7 @@ class S3Storage extends BaseModel static public function ownedByCurrentTeam(array $select = ['*']) { $selectArray = collect($select)->concat(['id']); - return S3Storage::whereTeamId(auth()->user()->currentTeam()->id)->select($selectArray->all())->orderBy('name'); + return S3Storage::whereTeamId(currentTeam()->id)->select($selectArray->all())->orderBy('name'); } public function awsUrl() diff --git a/app/Models/Server.php b/app/Models/Server.php index 91adb6b9b..764c58246 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -10,21 +10,40 @@ class Server extends BaseModel { use SchemalessAttributesTrait; + protected static function booted() + { + static::created(function ($server) { + ServerSetting::create([ + 'server_id' => $server->id, + ]); + if ($server->id === 0) { + StandaloneDocker::create([ + 'id' => 0, + 'name' => 'coolify', + 'network' => 'coolify', + 'server_id' => $server->id, + ]); + } else { + StandaloneDocker::create([ + 'name' => 'coolify', + 'network' => 'coolify', + 'server_id' => $server->id, + ]); + } + + }); + static::deleting(function ($server) { + $server->settings()->delete(); + }); + } + public $casts = [ 'proxy' => SchemalessAttributes::class, ]; protected $schemalessAttributes = [ 'proxy', ]; - protected $fillable = [ - 'name', - 'ip', - 'user', - 'port', - 'team_id', - 'private_key_id', - 'proxy', - ]; + protected $guarded = []; static public function isReachable() { @@ -33,8 +52,9 @@ class Server extends BaseModel static public function ownedByCurrentTeam(array $select = ['*']) { + $teamId = currentTeam()->id; $selectArray = collect($select)->concat(['id']); - return Server::whereTeamId(auth()->user()->currentTeam()->id)->with('settings')->select($selectArray->all())->orderBy('name'); + return Server::whereTeamId($teamId)->with('settings')->select($selectArray->all())->orderBy('name'); } static public function isUsable() @@ -50,17 +70,7 @@ class Server extends BaseModel return $standaloneDocker->concat($swarmDocker); } - protected static function booted() - { - static::created(function ($server) { - ServerSetting::create([ - 'server_id' => $server->id, - ]); - }); - static::deleting(function ($server) { - $server->settings()->delete(); - }); - } + public function settings() { diff --git a/app/Models/Subscription.php b/app/Models/Subscription.php index 4b78c37a9..e0a52d5b1 100644 --- a/app/Models/Subscription.php +++ b/app/Models/Subscription.php @@ -14,9 +14,9 @@ class Subscription extends Model } public function type() { - $basic = explode(',', config('coolify.lemon_squeezy_basic_plan_ids')); - $pro = explode(',', config('coolify.lemon_squeezy_pro_plan_ids')); - $ultimate = explode(',', config('coolify.lemon_squeezy_ultimate_plan_ids')); + $basic = explode(',', config('subscription.lemon_squeezy_basic_plan_ids')); + $pro = explode(',', config('subscription.lemon_squeezy_pro_plan_ids')); + $ultimate = explode(',', config('subscription.lemon_squeezy_ultimate_plan_ids')); $subscription = $this->lemon_variant_id; if (in_array($subscription, $basic)) { diff --git a/app/Models/User.php b/app/Models/User.php index b048ef9e6..13140f133 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -22,6 +22,7 @@ class User extends Authenticatable implements SendsEmail protected $casts = [ 'email_verified_at' => 'datetime', 'force_password_reset' => 'boolean', + 'show_boarding' => 'boolean', ]; protected static function boot() @@ -102,7 +103,7 @@ class User extends Authenticatable implements SendsEmail public function otherTeams() { - $team_id = auth()->user()->currentTeam()->id; + $team_id = currentTeam()->id; return auth()->user()->teams->filter(function ($team) use ($team_id) { return $team->id != $team_id; }); @@ -113,13 +114,6 @@ class User extends Authenticatable implements SendsEmail if ($this->teams()->where('team_id', 0)->first()) { return 'admin'; } - return $this->teams()->where('team_id', auth()->user()->currentTeam()->id)->first()->pivot->role; - } - - public function resources() - { - $team_id = auth()->user()->currentTeam()->id; - $data = Application::where('team_id', $team_id)->get(); - return $data; + return $this->teams()->where('team_id', currentTeam()->id)->first()->pivot->role; } } diff --git a/app/Models/Webhook.php b/app/Models/Webhook.php index 308f17370..e259d16c1 100644 --- a/app/Models/Webhook.php +++ b/app/Models/Webhook.php @@ -8,6 +8,7 @@ class Webhook extends Model { protected $guarded = []; protected $casts = [ + 'type' => 'string', 'payload' => 'encrypted', ]; } diff --git a/app/Notifications/Channels/DiscordChannel.php b/app/Notifications/Channels/DiscordChannel.php index 7032a0796..6c361f89e 100644 --- a/app/Notifications/Channels/DiscordChannel.php +++ b/app/Notifications/Channels/DiscordChannel.php @@ -14,6 +14,9 @@ class DiscordChannel { $message = $notification->toDiscord($notifiable); $webhookUrl = $notifiable->routeNotificationForDiscord(); + if (!$webhookUrl) { + return; + } dispatch(new SendMessageToDiscordJob($message, $webhookUrl)); } } diff --git a/app/Notifications/Channels/EmailChannel.php b/app/Notifications/Channels/EmailChannel.php index 638939589..b412bebd6 100644 --- a/app/Notifications/Channels/EmailChannel.php +++ b/app/Notifications/Channels/EmailChannel.php @@ -6,13 +6,13 @@ use Exception; use Illuminate\Mail\Message; use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Mail; +use Illuminate\Support\Str; class EmailChannel { public function send(SendsEmail $notifiable, Notification $notification): void { $this->bootConfigs($notifiable); - ray($notification); $recepients = $notifiable->getRecepients($notification); if (count($recepients) === 0) { @@ -39,16 +39,21 @@ class EmailChannel $password = data_get($notifiable, 'smtp_password'); if ($password) $password = decrypt($password); - config()->set('mail.default', 'smtp'); - config()->set('mail.mailers.smtp', [ - "transport" => "smtp", - "host" => data_get($notifiable, 'smtp_host'), - "port" => data_get($notifiable, 'smtp_port'), - "encryption" => data_get($notifiable, 'smtp_encryption'), - "username" => data_get($notifiable, 'smtp_username'), - "password" => $password, - "timeout" => data_get($notifiable, 'smtp_timeout'), - "local_domain" => null, - ]); + if (Str::contains(data_get($notifiable, 'smtp_host'),'resend.com')) { + config()->set('mail.default', 'resend'); + config()->set('resend.api_key', $password); + } else { + config()->set('mail.default', 'smtp'); + config()->set('mail.mailers.smtp', [ + "transport" => "smtp", + "host" => data_get($notifiable, 'smtp_host'), + "port" => data_get($notifiable, 'smtp_port'), + "encryption" => data_get($notifiable, 'smtp_encryption'), + "username" => data_get($notifiable, 'smtp_username'), + "password" => $password, + "timeout" => data_get($notifiable, 'smtp_timeout'), + "local_domain" => null, + ]); + } } } diff --git a/bootstrap/helpers/constants.php b/bootstrap/helpers/constants.php index 0e42b311e..cea780c24 100644 --- a/bootstrap/helpers/constants.php +++ b/bootstrap/helpers/constants.php @@ -9,3 +9,4 @@ const VALID_CRON_STRINGS = [ 'monthly' => '0 0 1 * *', 'yearly' => '0 0 1 1 *', ]; +const RESTART_MODE = 'unless-stopped'; diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php index 7b78abe8a..8ee0765a5 100644 --- a/bootstrap/helpers/docker.php +++ b/bootstrap/helpers/docker.php @@ -1,14 +1,27 @@ reject(fn ($line) => empty($line)) ->map(fn ($outputLine) => json_decode($outputLine, true, flags: JSON_THROW_ON_ERROR)); } @@ -44,9 +57,20 @@ function format_docker_envs_to_json($rawOutput) } } -function get_container_status(Server $server, string $container_id, bool $all_data = false, bool $throwError = false) +function getApplicationContainerStatus(Application $application) { + $server = $application->destination->server; + $id = $application->id; + + $containers = getCurrentApplicationContainerStatus($server, $id); + if ($containers->count() > 0) { + $status = data_get($containers[0], 'State', 'exited'); + return $status; + } + return 'exited'; +} +function getContainerStatus(Server $server, string $container_id, bool $all_data = false, bool $throwError = false) { - check_server_connection($server); + // check_server_connection($server); $container = instant_remote_process(["docker inspect --format '{{json .}}' {$container_id}"], $server, $throwError); if (!$container) { return 'exited'; @@ -58,12 +82,13 @@ function get_container_status(Server $server, string $container_id, bool $all_da return data_get($container[0], 'State.Status', 'exited'); } -function generate_container_name(string $uuid, int $pull_request_id = 0) +function generateApplicationContainerName(string $uuid, int $pull_request_id = 0) { - if ($pull_request_id !== 0) { - return $uuid . '-pr-' . $pull_request_id; + $now = now()->format('YmdHis'); + if ($pull_request_id !== 0 && $pull_request_id !== null) { + return $uuid . '-pr-' . $pull_request_id . '-' . $now; } else { - return $uuid; + return $uuid . '-' . $now; } } function get_port_from_dockerfile($dockerfile): int diff --git a/bootstrap/helpers/proxy.php b/bootstrap/helpers/proxy.php index 86c9ca528..164c46e94 100644 --- a/bootstrap/helpers/proxy.php +++ b/bootstrap/helpers/proxy.php @@ -32,7 +32,7 @@ function generate_default_proxy_configuration(Server $server) "traefik" => [ "container_name" => "coolify-proxy", "image" => "traefik:v2.10", - "restart" => "always", + "restart" => RESTART_MODE, "extra_hosts" => [ "host.docker.internal:host-gateway", ], @@ -82,7 +82,7 @@ function generate_default_proxy_configuration(Server $server) ], ], ]; - if (is_dev()) { + if (isDev()) { $config['services']['traefik']['command'][] = "--log.level=debug"; } return Yaml::dump($config, 4, 2); diff --git a/bootstrap/helpers/remoteProcess.php b/bootstrap/helpers/remoteProcess.php index ecc679b3f..86e11bcf3 100644 --- a/bootstrap/helpers/remoteProcess.php +++ b/bootstrap/helpers/remoteProcess.php @@ -66,8 +66,6 @@ function get_private_key_for_server(Server $server) function save_private_key_for_server(Server $server) { if (data_get($server, 'privateKey.private_key') === null) { - $server->settings->is_reachable = false; - $server->settings->save(); throw new \Exception("Server {$server->name} does not have a private key"); } $temp_file = "id.root@{$server->ip}"; @@ -159,8 +157,8 @@ function refresh_server_connection(PrivateKey $private_key) // Delete the old ssh mux file to force a new one to be created Storage::disk('ssh-mux')->delete($server->muxFilename()); // check if user is authenticated - if (auth()?->user()?->currentTeam()->id) { - auth()->user()->currentTeam()->privateKeys = PrivateKey::where('team_id', auth()->user()->currentTeam()->id)->get(); + if (currentTeam()->id) { + currentTeam()->privateKeys = PrivateKey::where('team_id', currentTeam()->id)->get(); } } } diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 4b470cb48..f1670723e 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -14,6 +14,7 @@ use Illuminate\Support\Str; use Nubs\RandomNameGenerator\All; use Poliander\Cron\CronExpression; use Visus\Cuid2\Cuid2; +use phpseclib3\Crypt\RSA; function application_configuration_dir(): string { @@ -35,11 +36,25 @@ function generate_readme_file(string $name, string $updated_at): string return "Resource name: $name\nLatest Deployment Date: $updated_at"; } -function is_instance_admin() +function isInstanceAdmin() { - return auth()->user()?->isInstanceAdmin(); + return auth()?->user()?->isInstanceAdmin() ?? false; } +function currentTeam() +{ + return auth()?->user()?->currentTeam() ?? null; +} + +function showBoarding(): bool +{ + return currentTeam()->show_boarding ?? false; +} +function refreshSession(): void +{ + $team = currentTeam(); + session(['currentTeam' => $team]); +} function general_error_handler(Throwable | null $err = null, $that = null, $isJson = false, $customErrorMessage = null): mixed { try { @@ -97,7 +112,21 @@ function generate_random_name(): string $cuid = new Cuid2(7); return Str::kebab("{$generator->getName()}-$cuid"); } - +function generateSSHKey() +{ + $key = RSA::createKey(); + return [ + 'private' => $key->toString('PKCS1'), + 'public' => $key->getPublicKey()->toString('OpenSSH',['comment' => 'coolify-generated-ssh-key']) + ]; +} +function formatPrivateKey(string $privateKey) { + $privateKey = trim($privateKey); + if (!str_ends_with($privateKey, "\n")) { + $privateKey .= "\n"; + } + return $privateKey; +} function generate_application_name(string $git_repository, string $git_branch): string { $cuid = new Cuid2(7); @@ -134,7 +163,7 @@ function set_transanctional_email_settings(InstanceSettings | null $settings = n function base_ip(): string { - if (is_dev()) { + if (isDev()) { return "localhost"; } $settings = InstanceSettings::get(); @@ -159,12 +188,12 @@ function base_url(bool $withPort = true): string $port = config('app.port'); if ($settings->public_ipv4) { if ($withPort) { - if (is_dev()) { + if (isDev()) { return "http://localhost:$port"; } return "http://$settings->public_ipv4:$port"; } - if (is_dev()) { + if (isDev()) { return "http://localhost"; } return "http://$settings->public_ipv4"; @@ -178,7 +207,7 @@ function base_url(bool $withPort = true): string return url('/'); } -function is_dev(): bool +function isDev(): bool { return config('app.env') === 'local'; } @@ -202,8 +231,9 @@ function validate_cron_expression($expression_to_validate): bool function send_internal_notification(string $message): void { try { + $baseUrl = base_url(false); $team = Team::find(0); - $team->notify(new GeneralNotification('👀 Internal notifications: ' . $message)); + $team->notify(new GeneralNotification("👀 Internal notifications from {$baseUrl}: " . $message)); } catch (\Throwable $th) { ray($th->getMessage()); } diff --git a/bootstrap/helpers/subscriptions.php b/bootstrap/helpers/subscriptions.php index 90d0947ad..ab266da2c 100644 --- a/bootstrap/helpers/subscriptions.php +++ b/bootstrap/helpers/subscriptions.php @@ -1,15 +1,17 @@ user()->id; - $team_id = auth()->user()->currentTeam()->id ?? null; + $team_id = currentTeam()->id ?? null; $email = auth()->user()->email ?? null; $name = auth()->user()->name ?? null; $url = "https://store.coollabs.io/checkout/buy/$checkout_id?"; @@ -30,53 +32,98 @@ function getSubscriptionLink($type) function getPaymentLink() { - return auth()->user()->currentTeam()->subscription->lemon_update_payment_menthod_url; + return currentTeam()->subscription->lemon_update_payment_menthod_url; } function getRenewDate() { - return Carbon::parse(auth()->user()->currentTeam()->subscription->lemon_renews_at)->format('Y-M-d H:i:s'); + return Carbon::parse(currentTeam()->subscription->lemon_renews_at)->format('Y-M-d H:i:s'); } function getEndDate() { - return Carbon::parse(auth()->user()->currentTeam()->subscription->lemon_renews_at)->format('Y-M-d H:i:s'); + return Carbon::parse(currentTeam()->subscription->lemon_renews_at)->format('Y-M-d H:i:s'); } -function is_subscription_active() +function isSubscriptionActive() { - $team = auth()->user()?->currentTeam(); - + $team = currentTeam(); if (!$team) { return false; } - if (is_instance_admin()) { - return true; - } $subscription = $team?->subscription; if (!$subscription) { return false; } - $is_active = $subscription->lemon_status === 'active'; + if (config('subscription.provider') === 'lemon') { + return $subscription->lemon_status === 'active'; + } + if (config('subscription.provider') === 'stripe') { + return $subscription->stripe_invoice_paid === true && $subscription->stripe_cancel_at_period_end === false; + } + return false; + // if (config('subscription.provider') === 'paddle') { + // return $subscription->paddle_status === 'active'; + // } - return $is_active; } -function is_subscription_in_grace_period() +function isSubscriptionOnGracePeriod() { - $team = auth()->user()?->currentTeam(); + + $team = currentTeam(); if (!$team) { return false; } - if (is_instance_admin()) { - return true; - } $subscription = $team?->subscription; if (!$subscription) { return false; } - $is_still_grace_period = $subscription->lemon_ends_at && - Carbon::parse($subscription->lemon_ends_at) > Carbon::now(); - - return $is_still_grace_period; + if (config('subscription.provider') === 'lemon') { + $is_still_grace_period = $subscription->lemon_ends_at && + Carbon::parse($subscription->lemon_ends_at) > Carbon::now(); + return $is_still_grace_period; + } + if (config('subscription.provider') === 'stripe') { + return $subscription->stripe_cancel_at_period_end; + } + return false; +} +function subscriptionProvider() +{ + return config('subscription.provider'); +} +function getStripeCustomerPortalSession(Team $team) +{ + Stripe::setApiKey(config('subscription.stripe_api_key')); + $return_url = route('team.show'); + $stripe_customer_id = $team->subscription->stripe_customer_id; + $session = \Stripe\BillingPortal\Session::create([ + 'customer' => $stripe_customer_id, + 'return_url' => $return_url, + ]); + return $session; +} +function allowedPathsForUnsubscribedAccounts() +{ + return [ + 'subscription', + 'login', + 'register', + 'waitlist', + 'force-password-reset', + 'logout', + 'livewire/message/force-password-reset', + 'livewire/message/check-license', + 'livewire/message/switch-team', + 'livewire/message/subscription.pricing-plans' + ]; +} +function allowedPathsForBoardingAccounts() +{ + return [ + ...allowedPathsForUnsubscribedAccounts(), + 'boarding', + 'livewire/message/boarding', + ]; } diff --git a/composer.json b/composer.json index 7fcdd9e26..bcb74242b 100644 --- a/composer.json +++ b/composer.json @@ -23,13 +23,16 @@ "livewire/livewire": "^v2.12.3", "masmerise/livewire-toaster": "^1.2", "nubs/random-name-generator": "^2.2", + "phpseclib/phpseclib": "~3.0", "poliander/cron": "^3.0", + "resend/resend-laravel": "^0.5.0", "sentry/sentry-laravel": "^3.4", "spatie/laravel-activitylog": "^4.7.3", "spatie/laravel-data": "^3.4.3", "spatie/laravel-ray": "^1.32.4", "spatie/laravel-schemaless-attributes": "^2.4", "spatie/url": "^2.2", + "stripe/stripe-php": "^12.0", "symfony/yaml": "^6.2", "visus/cuid2": "^2.0.0" }, diff --git a/composer.lock b/composer.lock index 45671727a..871f9f577 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0c023bed552776ee5e4eeda1ff0a5e19", + "content-hash": "da14dce99d76abcaaa6393166eda049a", "packages": [ { "name": "aws/aws-crt-php", @@ -62,16 +62,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.269.0", + "version": "3.279.6", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "6d759ef9f24f0c7f271baf8014f41fc0cfdfbf78" + "reference": "e83376ac8e88ed99c3d75d1309a2cb1148d31fd9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/6d759ef9f24f0c7f271baf8014f41fc0cfdfbf78", - "reference": "6d759ef9f24f0c7f271baf8014f41fc0cfdfbf78", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/e83376ac8e88ed99c3d75d1309a2cb1148d31fd9", + "reference": "e83376ac8e88ed99c3d75d1309a2cb1148d31fd9", "shasum": "" }, "require": { @@ -80,10 +80,11 @@ "ext-pcre": "*", "ext-simplexml": "*", "guzzlehttp/guzzle": "^6.5.8 || ^7.4.5", - "guzzlehttp/promises": "^1.4.0", + "guzzlehttp/promises": "^1.4.0 || ^2.0", "guzzlehttp/psr7": "^1.9.1 || ^2.4.5", "mtdowling/jmespath.php": "^2.6", - "php": ">=5.5" + "php": ">=7.2.5", + "psr/http-message": "^1.0 || ^2.0" }, "require-dev": { "andrewsville/php-token-reflection": "^1.4", @@ -98,9 +99,8 @@ "ext-sockets": "*", "nette/neon": "^2.3", "paragonie/random_compat": ">= 2", - "phpunit/phpunit": "^4.8.35 || ^5.6.3 || ^9.5", + "phpunit/phpunit": "^5.6.3 || ^8.5 || ^9.5", "psr/cache": "^1.0", - "psr/http-message": "^1.0", "psr/simple-cache": "^1.0", "sebastian/comparator": "^1.2.3 || ^4.0", "yoast/phpunit-polyfills": "^1.0" @@ -151,9 +151,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.269.0" + "source": "https://github.com/aws/aws-sdk-php/tree/3.279.6" }, - "time": "2023-04-26T18:21:04+00:00" + "time": "2023-08-24T18:07:59+00:00" }, { "name": "bacon/bacon-qr-code", @@ -603,16 +603,16 @@ }, { "name": "doctrine/dbal", - "version": "3.6.5", + "version": "3.6.6", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "96d5a70fd91efdcec81fc46316efc5bf3da17ddf" + "reference": "63646ffd71d1676d2f747f871be31b7e921c7864" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/96d5a70fd91efdcec81fc46316efc5bf3da17ddf", - "reference": "96d5a70fd91efdcec81fc46316efc5bf3da17ddf", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/63646ffd71d1676d2f747f871be31b7e921c7864", + "reference": "63646ffd71d1676d2f747f871be31b7e921c7864", "shasum": "" }, "require": { @@ -628,10 +628,11 @@ "doctrine/coding-standard": "12.0.0", "fig/log-test": "^1", "jetbrains/phpstorm-stubs": "2023.1", - "phpstan/phpstan": "1.10.21", + "phpstan/phpstan": "1.10.29", "phpstan/phpstan-strict-rules": "^1.5", "phpunit/phpunit": "9.6.9", "psalm/plugin-phpunit": "0.18.4", + "slevomat/coding-standard": "8.13.1", "squizlabs/php_codesniffer": "3.7.2", "symfony/cache": "^5.4|^6.0", "symfony/console": "^4.4|^5.4|^6.0", @@ -695,7 +696,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.6.5" + "source": "https://github.com/doctrine/dbal/tree/3.6.6" }, "funding": [ { @@ -711,7 +712,7 @@ "type": "tidelift" } ], - "time": "2023-07-17T09:15:50+00:00" + "time": "2023-08-17T05:38:17+00:00" }, { "name": "doctrine/deprecations", @@ -1021,16 +1022,16 @@ }, { "name": "dragonmantank/cron-expression", - "version": "v3.3.2", + "version": "v3.3.3", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8" + "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/782ca5968ab8b954773518e9e49a6f892a34b2a8", - "reference": "782ca5968ab8b954773518e9e49a6f892a34b2a8", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", + "reference": "adfb1f505deb6384dc8b39804c5065dd3c8c8c0a", "shasum": "" }, "require": { @@ -1070,7 +1071,7 @@ ], "support": { "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.2" + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.3.3" }, "funding": [ { @@ -1078,7 +1079,7 @@ "type": "github" } ], - "time": "2022-09-10T18:51:20+00:00" + "time": "2023-08-10T19:36:49+00:00" }, { "name": "egulias/email-validator", @@ -1408,29 +1409,33 @@ }, { "name": "guzzlehttp/promises", - "version": "1.5.3", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "67ab6e18aaa14d753cc148911d273f6e6cb6721e" + "reference": "111166291a0f8130081195ac4556a5587d7f1b5d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/67ab6e18aaa14d753cc148911d273f6e6cb6721e", - "reference": "67ab6e18aaa14d753cc148911d273f6e6cb6721e", + "url": "https://api.github.com/repos/guzzle/promises/zipball/111166291a0f8130081195ac4556a5587d7f1b5d", + "reference": "111166291a0f8130081195ac4556a5587d7f1b5d", "shasum": "" }, "require": { - "php": ">=5.5" + "php": "^7.2.5 || ^8.0" }, "require-dev": { - "symfony/phpunit-bridge": "^4.4 || ^5.1" + "bamarni/composer-bin-plugin": "^1.8.1", + "phpunit/phpunit": "^8.5.29 || ^9.5.23" }, "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { "GuzzleHttp\\Promise\\": "src/" } @@ -1467,7 +1472,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.5.3" + "source": "https://github.com/guzzle/promises/tree/2.0.1" }, "funding": [ { @@ -1483,7 +1488,7 @@ "type": "tidelift" } ], - "time": "2023-05-21T12:31:43+00:00" + "time": "2023-08-03T15:11:55+00:00" }, { "name": "guzzlehttp/psr7", @@ -1804,16 +1809,16 @@ }, { "name": "laravel/fortify", - "version": "v1.17.4", + "version": "v1.17.5", "source": { "type": "git", "url": "https://github.com/laravel/fortify.git", - "reference": "110dd0d09b70461d651218240120e24ba8d8cbe1" + "reference": "3d3ad9aaa46f686a5fe46a0af2ba907702019451" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/fortify/zipball/110dd0d09b70461d651218240120e24ba8d8cbe1", - "reference": "110dd0d09b70461d651218240120e24ba8d8cbe1", + "url": "https://api.github.com/repos/laravel/fortify/zipball/3d3ad9aaa46f686a5fe46a0af2ba907702019451", + "reference": "3d3ad9aaa46f686a5fe46a0af2ba907702019451", "shasum": "" }, "require": { @@ -1864,20 +1869,20 @@ "issues": "https://github.com/laravel/fortify/issues", "source": "https://github.com/laravel/fortify" }, - "time": "2023-06-18T09:17:00+00:00" + "time": "2023-08-02T13:57:32+00:00" }, { "name": "laravel/framework", - "version": "v10.18.0", + "version": "v10.20.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "9d41928900f7ecf409627a7d06c0a4dfecff2ea7" + "reference": "a655dca3fbe83897e22adff652b1878ba352d041" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/9d41928900f7ecf409627a7d06c0a4dfecff2ea7", - "reference": "9d41928900f7ecf409627a7d06c0a4dfecff2ea7", + "url": "https://api.github.com/repos/laravel/framework/zipball/a655dca3fbe83897e22adff652b1878ba352d041", + "reference": "a655dca3fbe83897e22adff652b1878ba352d041", "shasum": "" }, "require": { @@ -2064,7 +2069,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2023-08-08T14:30:38+00:00" + "time": "2023-08-22T13:37:09+00:00" }, { "name": "laravel/horizon", @@ -2146,16 +2151,16 @@ }, { "name": "laravel/prompts", - "version": "v0.1.4", + "version": "v0.1.6", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "1b3ab520a75eddefcda99f49fb551d231769b1fa" + "reference": "b514c5620e1b3b61221b0024dc88def26d9654f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/1b3ab520a75eddefcda99f49fb551d231769b1fa", - "reference": "1b3ab520a75eddefcda99f49fb551d231769b1fa", + "url": "https://api.github.com/repos/laravel/prompts/zipball/b514c5620e1b3b61221b0024dc88def26d9654f4", + "reference": "b514c5620e1b3b61221b0024dc88def26d9654f4", "shasum": "" }, "require": { @@ -2188,22 +2193,22 @@ ], "support": { "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.1.4" + "source": "https://github.com/laravel/prompts/tree/v0.1.6" }, - "time": "2023-08-07T13:14:59+00:00" + "time": "2023-08-18T13:32:23+00:00" }, { "name": "laravel/sanctum", - "version": "v3.2.5", + "version": "v3.2.6", "source": { "type": "git", "url": "https://github.com/laravel/sanctum.git", - "reference": "8ebda85d59d3c414863a7f4d816ef8302faad876" + "reference": "217e8a2bc5aa6a827ced97fcb76504029d3115d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sanctum/zipball/8ebda85d59d3c414863a7f4d816ef8302faad876", - "reference": "8ebda85d59d3c414863a7f4d816ef8302faad876", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/217e8a2bc5aa6a827ced97fcb76504029d3115d7", + "reference": "217e8a2bc5aa6a827ced97fcb76504029d3115d7", "shasum": "" }, "require": { @@ -2216,9 +2221,9 @@ }, "require-dev": { "mockery/mockery": "^1.0", - "orchestra/testbench": "^7.0|^8.0", + "orchestra/testbench": "^7.28.2|^8.8.3", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.6" }, "type": "library", "extra": { @@ -2256,7 +2261,7 @@ "issues": "https://github.com/laravel/sanctum/issues", "source": "https://github.com/laravel/sanctum" }, - "time": "2023-05-01T19:39:51+00:00" + "time": "2023-08-22T13:21:11+00:00" }, { "name": "laravel/serializable-closure", @@ -2983,16 +2988,16 @@ }, { "name": "livewire/livewire", - "version": "v2.12.5", + "version": "v2.12.6", "source": { "type": "git", "url": "https://github.com/livewire/livewire.git", - "reference": "96a249f5ab51d8377817d802f91d1e440869c1d6" + "reference": "7d3a57b3193299cf1a0639a3935c696f4da2cf92" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/livewire/livewire/zipball/96a249f5ab51d8377817d802f91d1e440869c1d6", - "reference": "96a249f5ab51d8377817d802f91d1e440869c1d6", + "url": "https://api.github.com/repos/livewire/livewire/zipball/7d3a57b3193299cf1a0639a3935c696f4da2cf92", + "reference": "7d3a57b3193299cf1a0639a3935c696f4da2cf92", "shasum": "" }, "require": { @@ -3044,7 +3049,7 @@ "description": "A front-end framework for Laravel.", "support": { "issues": "https://github.com/livewire/livewire/issues", - "source": "https://github.com/livewire/livewire/tree/v2.12.5" + "source": "https://github.com/livewire/livewire/tree/v2.12.6" }, "funding": [ { @@ -3052,7 +3057,7 @@ "type": "github" } ], - "time": "2023-08-02T06:31:31+00:00" + "time": "2023-08-11T04:02:34+00:00" }, { "name": "masmerise/livewire-toaster", @@ -3286,25 +3291,29 @@ }, { "name": "nesbot/carbon", - "version": "2.68.1", + "version": "2.69.0", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "4f991ed2a403c85efbc4f23eb4030063fdbe01da" + "reference": "4308217830e4ca445583a37d1bf4aff4153fa81c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/4f991ed2a403c85efbc4f23eb4030063fdbe01da", - "reference": "4f991ed2a403c85efbc4f23eb4030063fdbe01da", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/4308217830e4ca445583a37d1bf4aff4153fa81c", + "reference": "4308217830e4ca445583a37d1bf4aff4153fa81c", "shasum": "" }, "require": { "ext-json": "*", "php": "^7.1.8 || ^8.0", + "psr/clock": "^1.0", "symfony/polyfill-mbstring": "^1.0", "symfony/polyfill-php80": "^1.16", "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" }, + "provide": { + "psr/clock-implementation": "1.0" + }, "require-dev": { "doctrine/dbal": "^2.0 || ^3.1.4", "doctrine/orm": "^2.7", @@ -3384,7 +3393,7 @@ "type": "tidelift" } ], - "time": "2023-06-20T18:29:04+00:00" + "time": "2023-08-03T09:00:52+00:00" }, { "name": "nette/schema", @@ -3537,16 +3546,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.16.0", + "version": "v4.17.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "19526a33fb561ef417e822e85f08a00db4059c17" + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/19526a33fb561ef417e822e85f08a00db4059c17", - "reference": "19526a33fb561ef417e822e85f08a00db4059c17", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", "shasum": "" }, "require": { @@ -3587,9 +3596,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.16.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" }, - "time": "2023-06-25T14:52:30+00:00" + "time": "2023-08-13T19:53:39+00:00" }, { "name": "nubs/random-name-generator", @@ -3875,6 +3884,56 @@ }, "time": "2022-06-14T06:56:20+00:00" }, + { + "name": "paragonie/random_compat", + "version": "v9.99.100", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "shasum": "" + }, + "require": { + "php": ">= 7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" + }, { "name": "php-http/client-common", "version": "2.7.0", @@ -4315,16 +4374,16 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.7.2", + "version": "1.7.3", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "b2fe4d22a5426f38e014855322200b97b5362c0d" + "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/b2fe4d22a5426f38e014855322200b97b5362c0d", - "reference": "b2fe4d22a5426f38e014855322200b97b5362c0d", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", + "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", "shasum": "" }, "require": { @@ -4367,9 +4426,9 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.2" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3" }, - "time": "2023-05-30T18:13:47+00:00" + "time": "2023-08-12T11:01:26+00:00" }, { "name": "phpoption/phpoption", @@ -4446,6 +4505,116 @@ ], "time": "2023-02-25T19:38:58+00:00" }, + { + "name": "phpseclib/phpseclib", + "version": "3.0.21", + "source": { + "type": "git", + "url": "https://github.com/phpseclib/phpseclib.git", + "reference": "4580645d3fc05c189024eb3b834c6c1e4f0f30a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/4580645d3fc05c189024eb3b834c6c1e4f0f30a1", + "reference": "4580645d3fc05c189024eb3b834c6c1e4f0f30a1", + "shasum": "" + }, + "require": { + "paragonie/constant_time_encoding": "^1|^2", + "paragonie/random_compat": "^1.4|^2.0|^9.99.99", + "php": ">=5.6.1" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "suggest": { + "ext-dom": "Install the DOM extension to load XML formatted public keys.", + "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", + "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", + "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." + }, + "type": "library", + "autoload": { + "files": [ + "phpseclib/bootstrap.php" + ], + "psr-4": { + "phpseclib3\\": "phpseclib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jim Wigginton", + "email": "terrafrost@php.net", + "role": "Lead Developer" + }, + { + "name": "Patrick Monnerat", + "email": "pm@datasphere.ch", + "role": "Developer" + }, + { + "name": "Andreas Fischer", + "email": "bantu@phpbb.com", + "role": "Developer" + }, + { + "name": "Hans-Jürgen Petrich", + "email": "petrich@tronic-media.com", + "role": "Developer" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "role": "Developer" + } + ], + "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.", + "homepage": "http://phpseclib.sourceforge.net", + "keywords": [ + "BigInteger", + "aes", + "asn.1", + "asn1", + "blowfish", + "crypto", + "cryptography", + "encryption", + "rsa", + "security", + "sftp", + "signature", + "signing", + "ssh", + "twofish", + "x.509", + "x509" + ], + "support": { + "issues": "https://github.com/phpseclib/phpseclib/issues", + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.21" + }, + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "time": "2023-07-09T15:24:48+00:00" + }, { "name": "phpstan/phpdoc-parser", "version": "1.23.1", @@ -5404,6 +5573,131 @@ ], "time": "2023-04-15T23:01:58+00:00" }, + { + "name": "resend/resend-laravel", + "version": "v0.5.0", + "source": { + "type": "git", + "url": "https://github.com/resendlabs/resend-laravel.git", + "reference": "e598d1e25e49a7aa4c35f653d1d828f69ee4fc1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/resendlabs/resend-laravel/zipball/e598d1e25e49a7aa4c35f653d1d828f69ee4fc1d", + "reference": "e598d1e25e49a7aa4c35f653d1d828f69ee4fc1d", + "shasum": "" + }, + "require": { + "illuminate/support": "^9.21|^10.0", + "php": "^8.1", + "resend/resend-php": "^0.7.1", + "symfony/mailer": "^6.2" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.14", + "mockery/mockery": "^1.5", + "orchestra/testbench": "^7.22|^8.0", + "pestphp/pest": "^1.22" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + }, + "laravel": { + "providers": [ + "Resend\\Laravel\\ResendServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Resend\\Laravel\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Resend and contributors", + "homepage": "https://github.com/resendlabs/resend-laravel/contributors" + } + ], + "description": "Resend for Laravel", + "homepage": "https://resend.com/", + "keywords": [ + "api", + "client", + "laravel", + "php", + "resend", + "sdk" + ], + "support": { + "issues": "https://github.com/resendlabs/resend-laravel/issues", + "source": "https://github.com/resendlabs/resend-laravel/tree/v0.5.0" + }, + "time": "2023-07-15T17:56:14+00:00" + }, + { + "name": "resend/resend-php", + "version": "v0.7.1", + "source": { + "type": "git", + "url": "https://github.com/resendlabs/resend-php.git", + "reference": "3b2e2eae0ded191b2ea67d0ee2e87cdc442316a0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/resendlabs/resend-php/zipball/3b2e2eae0ded191b2ea67d0ee2e87cdc442316a0", + "reference": "3b2e2eae0ded191b2ea67d0ee2e87cdc442316a0", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^7.5", + "php": "^8.1.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.13", + "pestphp/pest": "^2.0", + "pestphp/pest-plugin-mock": "^2.0" + }, + "type": "library", + "autoload": { + "files": [ + "src/Resend.php" + ], + "psr-4": { + "Resend\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Resend and contributors", + "homepage": "https://github.com/resendlabs/resend-php/contributors" + } + ], + "description": "Resend PHP library.", + "homepage": "https://resend.com/", + "keywords": [ + "api", + "client", + "php", + "resend", + "sdk" + ], + "support": { + "issues": "https://github.com/resendlabs/resend-php/issues", + "source": "https://github.com/resendlabs/resend-php/tree/v0.7.1" + }, + "time": "2023-07-12T07:08:27+00:00" + }, { "name": "sentry/sdk", "version": "3.5.0", @@ -5807,16 +6101,16 @@ }, { "name": "spatie/laravel-data", - "version": "3.8.0", + "version": "3.8.1", "source": { "type": "git", "url": "https://github.com/spatie/laravel-data.git", - "reference": "0de2ecfc8fa98bcca6e2e041d22de8c74a19c45b" + "reference": "7ead3d8f761846185a94d06e584bfe17e43b9239" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-data/zipball/0de2ecfc8fa98bcca6e2e041d22de8c74a19c45b", - "reference": "0de2ecfc8fa98bcca6e2e041d22de8c74a19c45b", + "url": "https://api.github.com/repos/spatie/laravel-data/zipball/7ead3d8f761846185a94d06e584bfe17e43b9239", + "reference": "7ead3d8f761846185a94d06e584bfe17e43b9239", "shasum": "" }, "require": { @@ -5878,7 +6172,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-data/issues", - "source": "https://github.com/spatie/laravel-data/tree/3.8.0" + "source": "https://github.com/spatie/laravel-data/tree/3.8.1" }, "funding": [ { @@ -5886,20 +6180,20 @@ "type": "github" } ], - "time": "2023-08-09T14:09:06+00:00" + "time": "2023-08-11T11:59:07+00:00" }, { "name": "spatie/laravel-package-tools", - "version": "1.16.0", + "version": "1.16.1", "source": { "type": "git", "url": "https://github.com/spatie/laravel-package-tools.git", - "reference": "38fe533e93f86a1b2fb1000bf7df09c4748e6458" + "reference": "cc7c991555a37f9fa6b814aa03af73f88026a83d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/38fe533e93f86a1b2fb1000bf7df09c4748e6458", - "reference": "38fe533e93f86a1b2fb1000bf7df09c4748e6458", + "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/cc7c991555a37f9fa6b814aa03af73f88026a83d", + "reference": "cc7c991555a37f9fa6b814aa03af73f88026a83d", "shasum": "" }, "require": { @@ -5938,7 +6232,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-package-tools/issues", - "source": "https://github.com/spatie/laravel-package-tools/tree/1.16.0" + "source": "https://github.com/spatie/laravel-package-tools/tree/1.16.1" }, "funding": [ { @@ -5946,7 +6240,7 @@ "type": "github" } ], - "time": "2023-08-09T14:08:04+00:00" + "time": "2023-08-23T09:04:39+00:00" }, { "name": "spatie/laravel-ray", @@ -6297,6 +6591,67 @@ ], "time": "2023-04-27T11:07:22+00:00" }, + { + "name": "stripe/stripe-php", + "version": "v12.0.0", + "source": { + "type": "git", + "url": "https://github.com/stripe/stripe-php.git", + "reference": "732996be0714154716f19f73f956d77bafc99334" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/stripe/stripe-php/zipball/732996be0714154716f19f73f956d77bafc99334", + "reference": "732996be0714154716f19f73f956d77bafc99334", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "php": ">=5.6.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "3.5.0", + "php-coveralls/php-coveralls": "^2.5", + "phpstan/phpstan": "^1.2", + "phpunit/phpunit": "^5.7 || ^9.0", + "squizlabs/php_codesniffer": "^3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Stripe\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Stripe and contributors", + "homepage": "https://github.com/stripe/stripe-php/contributors" + } + ], + "description": "Stripe PHP Library", + "homepage": "https://stripe.com/", + "keywords": [ + "api", + "payment processing", + "stripe" + ], + "support": { + "issues": "https://github.com/stripe/stripe-php/issues", + "source": "https://github.com/stripe/stripe-php/tree/v12.0.0" + }, + "time": "2023-08-18T18:55:28+00:00" + }, { "name": "symfony/console", "version": "v6.3.2", @@ -10049,16 +10404,16 @@ }, { "name": "laravel/pint", - "version": "v1.10.6", + "version": "v1.11.0", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "d1915b6ecc6406c00472c6b9ae75b46aa153bbb2" + "reference": "88e835bf922b94017778bde89ef8f215e1ea40db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/d1915b6ecc6406c00472c6b9ae75b46aa153bbb2", - "reference": "d1915b6ecc6406c00472c6b9ae75b46aa153bbb2", + "url": "https://api.github.com/repos/laravel/pint/zipball/88e835bf922b94017778bde89ef8f215e1ea40db", + "reference": "88e835bf922b94017778bde89ef8f215e1ea40db", "shasum": "" }, "require": { @@ -10111,7 +10466,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2023-08-08T15:17:16+00:00" + "time": "2023-08-15T15:30:00+00:00" }, { "name": "mockery/mockery", @@ -10352,16 +10707,16 @@ }, { "name": "pestphp/pest", - "version": "v2.13.0", + "version": "v2.16.0", "source": { "type": "git", "url": "https://github.com/pestphp/pest.git", - "reference": "47f2ae32c14cd3f15520f5a12a3c36fdec25a2ee" + "reference": "cbd6a650576714c673dbb0575989663f7f5c8b6d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest/zipball/47f2ae32c14cd3f15520f5a12a3c36fdec25a2ee", - "reference": "47f2ae32c14cd3f15520f5a12a3c36fdec25a2ee", + "url": "https://api.github.com/repos/pestphp/pest/zipball/cbd6a650576714c673dbb0575989663f7f5c8b6d", + "reference": "cbd6a650576714c673dbb0575989663f7f5c8b6d", "shasum": "" }, "require": { @@ -10369,16 +10724,16 @@ "nunomaduro/collision": "^7.8.1", "nunomaduro/termwind": "^1.15.1", "pestphp/pest-plugin": "^2.0.1", - "pestphp/pest-plugin-arch": "^2.2.3", + "pestphp/pest-plugin-arch": "^2.3.1", "php": "^8.1.0", - "phpunit/phpunit": "^10.3.1" + "phpunit/phpunit": "^10.3.2" }, "conflict": { - "phpunit/phpunit": ">10.3.1", + "phpunit/phpunit": ">10.3.2", "webmozart/assert": "<1.11.0" }, "require-dev": { - "pestphp/pest-dev-tools": "^2.14.0", + "pestphp/pest-dev-tools": "^2.16.0", "pestphp/pest-plugin-type-coverage": "^2.0.0", "symfony/process": "^6.3.2" }, @@ -10438,7 +10793,7 @@ ], "support": { "issues": "https://github.com/pestphp/pest/issues", - "source": "https://github.com/pestphp/pest/tree/v2.13.0" + "source": "https://github.com/pestphp/pest/tree/v2.16.0" }, "funding": [ { @@ -10450,33 +10805,34 @@ "type": "github" } ], - "time": "2023-08-09T11:14:39+00:00" + "time": "2023-08-21T08:42:07+00:00" }, { "name": "pestphp/pest-plugin", - "version": "v2.0.1", + "version": "v2.1.1", "source": { "type": "git", "url": "https://github.com/pestphp/pest-plugin.git", - "reference": "e3a3da262b73bdcbf3fad4dc9846c3c4921f2147" + "reference": "e05d2859e08c2567ee38ce8b005d044e72648c0b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/e3a3da262b73bdcbf3fad4dc9846c3c4921f2147", - "reference": "e3a3da262b73bdcbf3fad4dc9846c3c4921f2147", + "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/e05d2859e08c2567ee38ce8b005d044e72648c0b", + "reference": "e05d2859e08c2567ee38ce8b005d044e72648c0b", "shasum": "" }, "require": { "composer-plugin-api": "^2.0.0", + "composer-runtime-api": "^2.2.2", "php": "^8.1" }, "conflict": { "pestphp/pest": "<2.2.3" }, "require-dev": { - "composer/composer": "^2.5.5", - "pestphp/pest": "^2.2.3", - "pestphp/pest-dev-tools": "^2.5.0" + "composer/composer": "^2.5.8", + "pestphp/pest": "^2.16.0", + "pestphp/pest-dev-tools": "^2.16.0" }, "type": "composer-plugin", "extra": { @@ -10503,7 +10859,7 @@ "unit" ], "support": { - "source": "https://github.com/pestphp/pest-plugin/tree/v2.0.1" + "source": "https://github.com/pestphp/pest-plugin/tree/v2.1.1" }, "funding": [ { @@ -10519,31 +10875,31 @@ "type": "patreon" } ], - "time": "2023-03-24T11:21:05+00:00" + "time": "2023-08-22T08:40:06+00:00" }, { "name": "pestphp/pest-plugin-arch", - "version": "v2.2.3", + "version": "v2.3.3", "source": { "type": "git", "url": "https://github.com/pestphp/pest-plugin-arch.git", - "reference": "f44834b728b44028fb7d99c4e3efc88b699728a8" + "reference": "b758990e83f89daba3c45672398579cf8692213f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/f44834b728b44028fb7d99c4e3efc88b699728a8", - "reference": "f44834b728b44028fb7d99c4e3efc88b699728a8", + "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/b758990e83f89daba3c45672398579cf8692213f", + "reference": "b758990e83f89daba3c45672398579cf8692213f", "shasum": "" }, "require": { - "nunomaduro/collision": "^7.7.0", + "nunomaduro/collision": "^7.8.1", "pestphp/pest-plugin": "^2.0.1", "php": "^8.1", - "ta-tikoma/phpunit-architecture-test": "^0.7.3" + "ta-tikoma/phpunit-architecture-test": "^0.7.4" }, "require-dev": { - "pestphp/pest": "^2.9.4", - "pestphp/pest-dev-tools": "^2.12.0" + "pestphp/pest": "^2.16.0", + "pestphp/pest-dev-tools": "^2.16.0" }, "type": "library", "autoload": { @@ -10571,7 +10927,7 @@ "unit" ], "support": { - "source": "https://github.com/pestphp/pest-plugin-arch/tree/v2.2.3" + "source": "https://github.com/pestphp/pest-plugin-arch/tree/v2.3.3" }, "funding": [ { @@ -10583,7 +10939,7 @@ "type": "github" } ], - "time": "2023-07-24T18:04:14+00:00" + "time": "2023-08-21T16:06:30+00:00" }, { "name": "phar-io/manifest", @@ -10821,16 +11177,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.28", + "version": "1.10.32", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "e4545b55904ebef470423d3ddddb74fa7325497a" + "reference": "c47e47d3ab03137c0e121e77c4d2cb58672f6d44" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e4545b55904ebef470423d3ddddb74fa7325497a", - "reference": "e4545b55904ebef470423d3ddddb74fa7325497a", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/c47e47d3ab03137c0e121e77c4d2cb58672f6d44", + "reference": "c47e47d3ab03137c0e121e77c4d2cb58672f6d44", "shasum": "" }, "require": { @@ -10879,7 +11235,7 @@ "type": "tidelift" } ], - "time": "2023-08-08T12:33:42+00:00" + "time": "2023-08-24T21:54:50+00:00" }, { "name": "phpunit/php-code-coverage", @@ -11203,16 +11559,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.3.1", + "version": "10.3.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "d442ce7c4104d5683c12e67e4dcb5058159e9804" + "reference": "0dafb1175c366dd274eaa9a625e914451506bcd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d442ce7c4104d5683c12e67e4dcb5058159e9804", - "reference": "d442ce7c4104d5683c12e67e4dcb5058159e9804", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0dafb1175c366dd274eaa9a625e914451506bcd1", + "reference": "0dafb1175c366dd274eaa9a625e914451506bcd1", "shasum": "" }, "require": { @@ -11284,7 +11640,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.1" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.3.2" }, "funding": [ { @@ -11300,7 +11656,7 @@ "type": "tidelift" } ], - "time": "2023-08-04T06:48:08+00:00" + "time": "2023-08-15T05:34:23+00:00" }, { "name": "sebastian/cli-parser", @@ -11471,16 +11827,16 @@ }, { "name": "sebastian/comparator", - "version": "5.0.0", + "version": "5.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "72f01e6586e0caf6af81297897bd112eb7e9627c" + "reference": "2db5010a484d53ebf536087a70b4a5423c102372" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/72f01e6586e0caf6af81297897bd112eb7e9627c", - "reference": "72f01e6586e0caf6af81297897bd112eb7e9627c", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372", + "reference": "2db5010a484d53ebf536087a70b4a5423c102372", "shasum": "" }, "require": { @@ -11491,7 +11847,7 @@ "sebastian/exporter": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^10.3" }, "type": "library", "extra": { @@ -11535,7 +11891,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.0" + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1" }, "funding": [ { @@ -11543,7 +11900,7 @@ "type": "github" } ], - "time": "2023-02-03T07:07:16+00:00" + "time": "2023-08-14T13:18:12+00:00" }, { "name": "sebastian/complexity", @@ -12330,16 +12687,16 @@ }, { "name": "spatie/ignition", - "version": "1.9.0", + "version": "1.10.1", "source": { "type": "git", "url": "https://github.com/spatie/ignition.git", - "reference": "de24ff1e01814d5043bd6eb4ab36a5a852a04973" + "reference": "d92b9a081e99261179b63a858c7a4b01541e7dd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/ignition/zipball/de24ff1e01814d5043bd6eb4ab36a5a852a04973", - "reference": "de24ff1e01814d5043bd6eb4ab36a5a852a04973", + "url": "https://api.github.com/repos/spatie/ignition/zipball/d92b9a081e99261179b63a858c7a4b01541e7dd1", + "reference": "d92b9a081e99261179b63a858c7a4b01541e7dd1", "shasum": "" }, "require": { @@ -12409,20 +12766,20 @@ "type": "github" } ], - "time": "2023-06-28T13:24:59+00:00" + "time": "2023-08-21T15:06:37+00:00" }, { "name": "spatie/laravel-ignition", - "version": "2.2.0", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-ignition.git", - "reference": "dd15fbe82ef5392798941efae93c49395a87d943" + "reference": "4ed813d16edb5a1ab0d7f4b1d116c37ee8cdf3c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/dd15fbe82ef5392798941efae93c49395a87d943", - "reference": "dd15fbe82ef5392798941efae93c49395a87d943", + "url": "https://api.github.com/repos/spatie/laravel-ignition/zipball/4ed813d16edb5a1ab0d7f4b1d116c37ee8cdf3c0", + "reference": "4ed813d16edb5a1ab0d7f4b1d116c37ee8cdf3c0", "shasum": "" }, "require": { @@ -12501,7 +12858,7 @@ "type": "github" } ], - "time": "2023-06-28T13:51:52+00:00" + "time": "2023-08-23T06:24:34+00:00" }, { "name": "ta-tikoma/phpunit-architecture-test", diff --git a/config/coolify.php b/config/coolify.php index 0276b9a78..f3485f0da 100644 --- a/config/coolify.php +++ b/config/coolify.php @@ -4,17 +4,6 @@ return [ 'self_hosted' => env('SELF_HOSTED', true), 'waitlist' => env('WAITLIST', false), 'license_url' => 'https://license.coolify.io', - 'lemon_squeezy_api_key' => env('LEMON_SQUEEZY_API_KEY', null), - 'lemon_squeezy_webhook_secret' => env('LEMON_SQUEEZY_WEBHOOK_SECRET', null), - 'lemon_squeezy_checkout_id_monthly_basic' => env('LEMON_SQUEEZY_CHECKOUT_ID_MONTHLY_BASIC', null), - 'lemon_squeezy_checkout_id_monthly_pro' => env('LEMON_SQUEEZY_CHECKOUT_ID_MONTHLY_PRO', null), - 'lemon_squeezy_checkout_id_monthly_ultimate' => env('LEMON_SQUEEZY_CHECKOUT_ID_MONTHLY_ULTIMATE', null), - 'lemon_squeezy_checkout_id_yearly_basic' => env('LEMON_SQUEEZY_CHECKOUT_ID_YEARLY_BASIC', null), - 'lemon_squeezy_checkout_id_yearly_pro' => env('LEMON_SQUEEZY_CHECKOUT_ID_YEARLY_PRO', null), - 'lemon_squeezy_checkout_id_yearly_ultimate' => env('LEMON_SQUEEZY_CHECKOUT_ID_YEARLY_ULTIMATE', null), - 'lemon_squeezy_basic_plan_ids' => env('LEMON_SQUEEZY_BASIC_PLAN_IDS', ""), - 'lemon_squeezy_pro_plan_ids' => env('LEMON_SQUEEZY_PRO_PLAN_IDS', ""), - 'lemon_squeezy_ultimate_plan_ids' => env('LEMON_SQUEEZY_ULTIMATE_PLAN_IDS', ""), 'mux_enabled' => env('MUX_ENABLED', true), 'dev_webhook' => env('SERVEO_URL'), 'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'), diff --git a/config/mail.php b/config/mail.php index 4c63daf4f..ec2125fab 100644 --- a/config/mail.php +++ b/config/mail.php @@ -44,7 +44,9 @@ return [ 'timeout' => null, 'local_domain' => env('MAIL_EHLO_DOMAIN'), ], - + 'resend'=> [ + 'transport' => 'resend' + ], 'ses' => [ 'transport' => 'ses', ], diff --git a/config/subscription.php b/config/subscription.php new file mode 100644 index 000000000..f646723f8 --- /dev/null +++ b/config/subscription.php @@ -0,0 +1,42 @@ + env('SUBSCRIPTION_PROVIDER', null), // stripe, paddle, lemon + // Stripe + 'stripe_api_key' => env('STRIPE_API_KEY', null), + 'stripe_webhook_secret' => env('STRIPE_WEBHOOK_SECRET', null), + 'stripe_price_id_basic_monthly' => env('STRIPE_PRICE_ID_BASIC_MONTHLY', null), + 'stripe_price_id_basic_yearly' => env('STRIPE_PRICE_ID_BASIC_YEARLY', null), + 'stripe_price_id_pro_monthly' => env('STRIPE_PRICE_ID_PRO_MONTHLY', null), + 'stripe_price_id_pro_yearly' => env('STRIPE_PRICE_ID_PRO_YEARLY', null), + 'stripe_price_id_ultimate_monthly' => env('STRIPE_PRICE_ID_ULTIMATE_MONTHLY', null), + 'stripe_price_id_ultimate_yearly' => env('STRIPE_PRICE_ID_ULTIMATE_YEARLY', null), + + + // Paddle + 'paddle_vendor_id' => env('PADDLE_VENDOR_ID', null), + 'paddle_vendor_auth_code' => env('PADDLE_VENDOR_AUTH_CODE', null), + 'paddle_webhook_secret' => env('PADDLE_WEBHOOK_SECRET', null), + 'paddle_public_key' => env('PADDLE_PUBLIC_KEY', null), + 'paddle_price_id_basic_monthly' => env('PADDLE_PRICE_ID_BASIC_MONTHLY', null), + 'paddle_price_id_basic_yearly' => env('PADDLE_PRICE_ID_BASIC_YEARLY', null), + 'paddle_price_id_pro_monthly' => env('PADDLE_PRICE_ID_PRO_MONTHLY', null), + 'paddle_price_id_pro_yearly' => env('PADDLE_PRICE_ID_PRO_YEARLY', null), + 'paddle_price_id_ultimate_monthly' => env('PADDLE_PRICE_ID_ULTIMATE_MONTHLY', null), + 'paddle_price_id_ultimate_yearly' => env('PADDLE_PRICE_ID_ULTIMATE_YEARLY', null), + + + + // Lemon + 'lemon_squeezy_api_key' => env('LEMON_SQUEEZY_API_KEY', null), + 'lemon_squeezy_webhook_secret' => env('LEMON_SQUEEZY_WEBHOOK_SECRET', null), + 'lemon_squeezy_checkout_id_basic_monthly' => env('LEMON_SQUEEZY_CHECKOUT_ID_BASIC_MONTHLY', null), + 'lemon_squeezy_checkout_id_basic_yearly' => env('LEMON_SQUEEZY_CHECKOUT_ID_BASIC_YEARLY', null), + 'lemon_squeezy_checkout_id_pro_monthly' => env('LEMON_SQUEEZY_CHECKOUT_ID_PRO_MONTHLY', null), + 'lemon_squeezy_checkout_id_pro_yearly' => env('LEMON_SQUEEZY_CHECKOUT_ID_PRO_YEARLY', null), + 'lemon_squeezy_checkout_id_ultimate_monthly' => env('LEMON_SQUEEZY_CHECKOUT_ID_ULTIMATE_MONTHLY', null), + 'lemon_squeezy_checkout_id_ultimate_yearly' => env('LEMON_SQUEEZY_CHECKOUT_ID_ULTIMATE_YEARLY', null), + 'lemon_squeezy_basic_plan_ids' => env('LEMON_SQUEEZY_BASIC_PLAN_IDS', ""), + 'lemon_squeezy_pro_plan_ids' => env('LEMON_SQUEEZY_PRO_PLAN_IDS', ""), + 'lemon_squeezy_ultimate_plan_ids' => env('LEMON_SQUEEZY_ULTIMATE_PLAN_IDS', ""), +]; diff --git a/config/version.php b/config/version.php index 7764582fa..7ea9c5731 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ boolean('show_boarding')->default(false); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('teams', function (Blueprint $table) { + $table->dropColumn('show_boarding'); + }); + } +}; diff --git a/database/migrations/2023_08_22_071049_update_webhooks_type.php b/database/migrations/2023_08_22_071049_update_webhooks_type.php new file mode 100644 index 000000000..7f60ca973 --- /dev/null +++ b/database/migrations/2023_08_22_071049_update_webhooks_type.php @@ -0,0 +1,29 @@ +string('type')->change(); + }); + DB::statement("ALTER TABLE webhooks DROP CONSTRAINT webhooks_type_check"); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('webhooks', function (Blueprint $table) { + $table->string('type')->change(); + }); + } +}; diff --git a/database/migrations/2023_08_22_071050_update_subscriptions_stripe.php b/database/migrations/2023_08_22_071050_update_subscriptions_stripe.php new file mode 100644 index 000000000..70462fe20 --- /dev/null +++ b/database/migrations/2023_08_22_071050_update_subscriptions_stripe.php @@ -0,0 +1,52 @@ +boolean('stripe_invoice_paid')->default(false); + $table->string('stripe_subscription_id')->nullable(); + $table->string('stripe_customer_id')->nullable(); + $table->boolean('stripe_cancel_at_period_end')->default(false); + $table->string('lemon_subscription_id')->nullable()->change(); + $table->string('lemon_order_id')->nullable()->change(); + $table->string('lemon_product_id')->nullable()->change(); + $table->string('lemon_variant_id')->nullable()->change(); + $table->string('lemon_variant_name')->nullable()->change(); + $table->string('lemon_customer_id')->nullable()->change(); + $table->string('lemon_status')->nullable()->change(); + $table->string('lemon_renews_at')->nullable()->change(); + $table->string('lemon_update_payment_menthod_url')->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('subscriptions', function (Blueprint $table) { + $table->dropColumn('stripe_invoice_paid'); + $table->dropColumn('stripe_subscription_id'); + $table->dropColumn('stripe_customer_id'); + $table->dropColumn('stripe_cancel_at_period_end'); + $table->string('lemon_subscription_id')->change(); + $table->string('lemon_order_id')->change(); + $table->string('lemon_product_id')->change(); + $table->string('lemon_variant_id')->change(); + $table->string('lemon_variant_name')->change(); + $table->string('lemon_customer_id')->change(); + $table->string('lemon_status')->change(); + $table->string('lemon_renews_at')->change(); + $table->string('lemon_update_payment_menthod_url')->change(); + }); + } +}; diff --git a/database/seeders/ApplicationSeeder.php b/database/seeders/ApplicationSeeder.php index a43e0f19a..2d2d00cb7 100644 --- a/database/seeders/ApplicationSeeder.php +++ b/database/seeders/ApplicationSeeder.php @@ -17,13 +17,12 @@ class ApplicationSeeder extends Seeder Application::create([ 'name' => 'coollabsio/coolify-examples:nodejs-fastify', 'description' => 'NodeJS Fastify Example', - 'fqdn' => 'http://foo.com', + 'fqdn' => 'http://nodejs.127.0.0.1.sslip.io', 'repository_project_id' => 603035348, 'git_repository' => 'coollabsio/coolify-examples', 'git_branch' => 'nodejs-fastify', 'build_pack' => 'nixpacks', 'ports_exposes' => '3000', - 'ports_mappings' => '3005:3000', 'environment_id' => 1, 'destination_id' => 0, 'destination_type' => StandaloneDocker::class, @@ -33,18 +32,36 @@ class ApplicationSeeder extends Seeder Application::create([ 'name' => 'coollabsio/coolify-examples:dockerfile', 'description' => 'Dockerfile Example', - 'fqdn' => 'http://foos.com', + 'fqdn' => 'http://dockerfile.127.0.0.1.sslip.io', 'repository_project_id' => 603035348, 'git_repository' => 'coollabsio/coolify-examples', 'git_branch' => 'dockerfile', 'build_pack' => 'dockerfile', 'ports_exposes' => '3000', - 'ports_mappings' => '3080:80', 'environment_id' => 1, 'destination_id' => 0, 'destination_type' => StandaloneDocker::class, 'source_id' => 0, 'source_type' => GithubApp::class ]); + Application::create([ + 'name' => 'pure-dockerfile', + 'description' => 'Pure Dockerfile Example', + 'fqdn' => 'http://pure-dockerfile.127.0.0.1.sslip.io', + 'git_repository' => 'coollabsio/coolify', + 'git_branch' => 'main', + 'git_commit_sha' => 'HEAD', + 'build_pack' => 'dockerfile', + 'ports_exposes' => '80', + 'environment_id' => 1, + 'destination_id' => 0, + 'destination_type' => StandaloneDocker::class, + 'source_id' => 0, + 'source_type' => GithubApp::class, + 'dockerfile' => 'FROM nginx +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] +' + ]); } } diff --git a/database/seeders/InstanceSettingsSeeder.php b/database/seeders/InstanceSettingsSeeder.php index a00d8715c..1be6462c9 100644 --- a/database/seeders/InstanceSettingsSeeder.php +++ b/database/seeders/InstanceSettingsSeeder.php @@ -16,6 +16,7 @@ class InstanceSettingsSeeder extends Seeder InstanceSettings::create([ 'id' => 0, 'is_registration_enabled' => true, + 'is_resale_license_active' => true, 'smtp_enabled' => true, 'smtp_host' => 'coolify-mail', 'smtp_port' => 1025, diff --git a/database/seeders/ScheduledDatabaseBackupExecutionSeeder.php b/database/seeders/ScheduledDatabaseBackupExecutionSeeder.php index e4b5780c9..7e4c33764 100644 --- a/database/seeders/ScheduledDatabaseBackupExecutionSeeder.php +++ b/database/seeders/ScheduledDatabaseBackupExecutionSeeder.php @@ -12,17 +12,17 @@ class ScheduledDatabaseBackupExecutionSeeder extends Seeder */ public function run(): void { - ScheduledDatabaseBackupExecution::create([ - 'status' => 'success', - 'message' => 'Backup created successfully.', - 'size' => '10243467789556', - 'scheduled_database_backup_id' => 1, - ]); - ScheduledDatabaseBackupExecution::create([ - 'status' => 'failed', - 'message' => 'Backup failed.', - 'size' => '10243456', - 'scheduled_database_backup_id' => 1, - ]); + // ScheduledDatabaseBackupExecution::create([ + // 'status' => 'success', + // 'message' => 'Backup created successfully.', + // 'size' => '10243467789556', + // 'scheduled_database_backup_id' => 1, + // ]); + // ScheduledDatabaseBackupExecution::create([ + // 'status' => 'failed', + // 'message' => 'Backup failed.', + // 'size' => '10243456', + // 'scheduled_database_backup_id' => 1, + // ]); } } diff --git a/database/seeders/ScheduledDatabaseBackupSeeder.php b/database/seeders/ScheduledDatabaseBackupSeeder.php index 96ca0c012..fefbada0d 100644 --- a/database/seeders/ScheduledDatabaseBackupSeeder.php +++ b/database/seeders/ScheduledDatabaseBackupSeeder.php @@ -12,22 +12,22 @@ class ScheduledDatabaseBackupSeeder extends Seeder */ public function run(): void { - ScheduledDatabaseBackup::create([ - 'enabled' => true, - 'frequency' => '* * * * *', - 'number_of_backups_locally' => 2, - 'database_id' => 1, - 'database_type' => 'App\Models\StandalonePostgresql', - 's3_storage_id' => 1, - 'team_id' => 0, - ]); - ScheduledDatabaseBackup::create([ - 'enabled' => true, - 'frequency' => '* * * * *', - 'number_of_backups_locally' => 3, - 'database_id' => 1, - 'database_type' => 'App\Models\StandalonePostgresql', - 'team_id' => 0, - ]); + // ScheduledDatabaseBackup::create([ + // 'enabled' => true, + // 'frequency' => '* * * * *', + // 'number_of_backups_locally' => 2, + // 'database_id' => 1, + // 'database_type' => 'App\Models\StandalonePostgresql', + // 's3_storage_id' => 1, + // 'team_id' => 0, + // ]); + // ScheduledDatabaseBackup::create([ + // 'enabled' => true, + // 'frequency' => '* * * * *', + // 'number_of_backups_locally' => 3, + // 'database_id' => 1, + // 'database_type' => 'App\Models\StandalonePostgresql', + // 'team_id' => 0, + // ]); } } diff --git a/database/seeders/ServerSeeder.php b/database/seeders/ServerSeeder.php index 77a4516e5..f201862f0 100644 --- a/database/seeders/ServerSeeder.php +++ b/database/seeders/ServerSeeder.php @@ -11,8 +11,8 @@ class ServerSeeder extends Seeder { Server::create([ 'id' => 0, - 'name' => "testing-local-docker-container", - 'description' => "This is a test docker container", + 'name' => "localhost", + 'description' => "This is a test docker container in development mode", 'ip' => "coolify-testing-host", 'team_id' => 0, 'private_key_id' => 0, diff --git a/database/seeders/StandaloneDockerSeeder.php b/database/seeders/StandaloneDockerSeeder.php index 9f67de710..1e9a749dd 100644 --- a/database/seeders/StandaloneDockerSeeder.php +++ b/database/seeders/StandaloneDockerSeeder.php @@ -13,11 +13,11 @@ class StandaloneDockerSeeder extends Seeder */ public function run(): void { - StandaloneDocker::create([ - 'id' => 0, - 'name' => 'Standalone Docker 1', - 'network' => 'coolify', - 'server_id' => 0, - ]); + // StandaloneDocker::create([ + // 'id' => 0, + // 'name' => 'Standalone Docker 1', + // 'network' => 'coolify', + // 'server_id' => 0, + // ]); } } diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 4a9e0c192..710f44f2c 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -34,13 +34,33 @@ services: - PHP_PM_MAX_SPARE_SERVERS=10 - SELF_HOSTED - WAITLIST + - SUBSCRIPTION_PROVIDER + - STRIPE_API_KEY + - STRIPE_WEBHOOK_SECRET + - STRIPE_PRICE_ID_BASIC_MONTHLY + - STRIPE_PRICE_ID_BASIC_YEARLY + - STRIPE_PRICE_ID_PRO_MONTHLY + - STRIPE_PRICE_ID_PRO_YEARLY + - STRIPE_PRICE_ID_ULTIMATE_MONTHLY + - STRIPE_PRICE_ID_ULTIMATE_YEARLY + - PADDLE_VENDOR_ID + - PADDLE_WEBHOOK_SECRET + - PADDLE_VENDOR_AUTH_CODE + - PADDLE_PUBLIC_KEY + - PADDLE_PRICE_ID_BASIC_MONTHLY + - PADDLE_PRICE_ID_BASIC_YEARLY + - PADDLE_PRICE_ID_PRO_MONTHLY + - PADDLE_PRICE_ID_PRO_YEARLY + - PADDLE_PRICE_ID_ULTIMATE_MONTHLY + - PADDLE_PRICE_ID_ULTIMATE_YEARLY + - LEMON_SQUEEZY_API_KEY - LEMON_SQUEEZY_WEBHOOK_SECRET - - LEMON_SQUEEZY_CHECKOUT_ID_MONTHLY_BASIC - - LEMON_SQUEEZY_CHECKOUT_ID_MONTHLY_PRO - - LEMON_SQUEEZY_CHECKOUT_ID_MONTHLY_ULTIMATE - - LEMON_SQUEEZY_CHECKOUT_ID_YEARLY_BASIC - - LEMON_SQUEEZY_CHECKOUT_ID_YEARLY_PRO - - LEMON_SQUEEZY_CHECKOUT_ID_YEARLY_ULTIMATE + - LEMON_SQUEEZY_CHECKOUT_ID_BASIC_MONTHLY + - LEMON_SQUEEZY_CHECKOUT_ID_BASIC_YEARLY + - LEMON_SQUEEZY_CHECKOUT_ID_PRO_MONTHLY + - LEMON_SQUEEZY_CHECKOUT_ID_PRO_YEARLY + - LEMON_SQUEEZY_CHECKOUT_ID_ULTIMATE_MONTHLY + - LEMON_SQUEEZY_CHECKOUT_ID_ULTIMATE_YEARLY - LEMON_SQUEEZY_BASIC_PLAN_IDS - LEMON_SQUEEZY_PRO_PLAN_IDS - LEMON_SQUEEZY_ULTIMATE_PLAN_IDS diff --git a/package.json b/package.json index ed81d6a26..81e0dd5a8 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "private": true, + "type": "module", "scripts": { "dev": "vite", "build": "vite build" @@ -20,4 +21,4 @@ "daisyui": "3.2.1", "tailwindcss-scrollbar": "0.1.0" } -} \ No newline at end of file +} diff --git a/postcss.config.js b/postcss.config.cjs similarity index 100% rename from postcss.config.js rename to postcss.config.cjs diff --git a/resources/css/app.css b/resources/css/app.css index b5f58fc74..7703ef608 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -53,7 +53,7 @@ a { @apply text-white; } .box { - @apply flex items-center justify-center p-2 transition-colors rounded min-h-12 bg-coolgray-200 hover:bg-coollabs-100 hover:text-white hover:no-underline; + @apply flex items-center p-2 transition-colors cursor-pointer min-h-16 bg-coolgray-200 hover:bg-coollabs-100 hover:text-white hover:no-underline min-w-[24rem]; } .lds-heart { diff --git a/resources/js/components/MagicBar.vue b/resources/js/components/MagicBar.vue index f0203b885..a0e8f762c 100644 --- a/resources/js/components/MagicBar.vue +++ b/resources/js/components/MagicBar.vue @@ -53,12 +53,12 @@ {{ sequenceState.sequence[sequenceState.currentActionIndex] }} name will be: - {{ search - }} + {{ search }} + {{ sequenceState.sequence[sequenceState.currentActionIndex] }} name will be: - randomly generated (type to change) + randomly generated (type to change) diff --git a/resources/views/boarding.blade.php b/resources/views/boarding.blade.php new file mode 100644 index 000000000..bdf229935 --- /dev/null +++ b/resources/views/boarding.blade.php @@ -0,0 +1,13 @@ + + + + + + + + + Close + + + + diff --git a/resources/views/components/applications/links.blade.php b/resources/views/components/applications/links.blade.php index 9d0a75c70..9e90c9b2b 100644 --- a/resources/views/components/applications/links.blade.php +++ b/resources/views/components/applications/links.blade.php @@ -55,7 +55,7 @@ @endif @if (data_get($application, 'ports_mappings_array')) @foreach ($application->ports_mappings_array as $port) - @if (is_dev()) + @if (isDev())
  • diff --git a/resources/views/components/boarding-step.blade.php b/resources/views/components/boarding-step.blade.php new file mode 100644 index 000000000..e9341cbd9 --- /dev/null +++ b/resources/views/components/boarding-step.blade.php @@ -0,0 +1,25 @@ +
    +
    +

    {{$title}}

    +
    + @isset($question) +

    + {{$question}} +

    + @endisset +
    + @if($actions) +
    + {{$actions}} +
    + @endif +
    + @if($explanation) +
    +

    Explanation

    +
    + {{$explanation}} +
    +
    + @endif +
    diff --git a/resources/views/components/forms/input.blade.php b/resources/views/components/forms/input.blade.php index a12366238..2abc8a91e 100644 --- a/resources/views/components/forms/input.blade.php +++ b/resources/views/components/forms/input.blade.php @@ -2,7 +2,7 @@ @if ($label)
    @if ($label) -
    diff --git a/resources/views/components/highlighted.blade.php b/resources/views/components/highlighted.blade.php new file mode 100644 index 000000000..2ca73724c --- /dev/null +++ b/resources/views/components/highlighted.blade.php @@ -0,0 +1 @@ +{{$text}} diff --git a/resources/views/components/layout-simple.blade.php b/resources/views/components/layout-simple.blade.php index 3cfe9bd8e..cf1328b32 100644 --- a/resources/views/components/layout-simple.blade.php +++ b/resources/views/components/layout-simple.blade.php @@ -26,7 +26,7 @@ @livewireScripts -
    +
    {{ $slot }}
    diff --git a/resources/views/components/layout-subscription.blade.php b/resources/views/components/layout-subscription.blade.php index f5103c863..a3b0bf806 100644 --- a/resources/views/components/layout-subscription.blade.php +++ b/resources/views/components/layout-subscription.blade.php @@ -9,7 +9,7 @@ @env('local') Coolify - localhost - @else +@else {{ $title ?? 'Coolify' }} @endenv @@ -26,7 +26,7 @@ @livewireScripts - @if (is_instance_admin() || is_subscription_in_grace_period()) + @if (isSubscriptionOnGracePeriod())
    @@ -68,6 +68,18 @@ window.location.reload(); } }) + Livewire.on('info', (message) => { + if (message) Toaster.info(message) + }) + Livewire.on('error', (message) => { + if (message) Toaster.error(message) + }) + Livewire.on('warning', (message) => { + if (message) Toaster.warning(message) + }) + Livewire.on('success', (message) => { + if (message) Toaster.success(message) + }) diff --git a/resources/views/components/navbar.blade.php b/resources/views/components/navbar.blade.php index 428ffdbfe..6ce580837 100644 --- a/resources/views/components/navbar.blade.php +++ b/resources/views/components/navbar.blade.php @@ -65,7 +65,7 @@
  • - @if (is_instance_admin()) + @if (isInstanceAdmin()) @endif
  • @@ -80,7 +80,7 @@
  • - @if (is_instance_admin()) + @if (isInstanceAdmin())
  • is('settings')) href="/settings" @endif> + Subscribe + + + Subscribe + + + + Subscribe + + + Subscribe + + + + Subscribe + + + Subscribe + + + + + + diff --git a/resources/views/components/pricing-plans.blade.php b/resources/views/components/pricing-plans.blade.php index c00958415..acdc1653b 100644 --- a/resources/views/components/pricing-plans.blade.php +++ b/resources/views/components/pricing-plans.blade.php @@ -5,15 +5,15 @@
    + class="grid grid-cols-2 p-1 text-xs font-semibold leading-5 text-center rounded-full gap-x-1 "> Payment frequency - -
    -
    Save 10% annually with the yearly plan. +
    Save 1 month annually with the yearly plans.
    -
    Congratulations! 🎉 You are saving money with this choice! +
    @@ -105,10 +105,9 @@ billed annually @if ($showSubscribeButtons) - Subscribe - Subscribe + @isset($basic) + {{ $basic }} + @endisset @endif

    Start self-hosting in the cloud @@ -168,10 +167,9 @@ billed annually @if ($showSubscribeButtons) - Subscribe - Subscribe + @isset($pro) + {{ $pro }} + @endisset @endif

    Scale your business or self-hosting environment.

    @@ -227,10 +225,9 @@ billed annually @if ($showSubscribeButtons) - Subscribe - Subscribe + @isset($ultimate) + {{ $ultimate }} + @endisset @endif

    Deploy complex infrastuctures and manage them easily in one place.

    @@ -274,3 +271,6 @@
    +@isset($other) + {{ $other }} +@endisset diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index 442740f97..4e83f41b3 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -1,6 +1,6 @@

    Dashboard

    -
    Something (more) useful will be here.
    +
    Something useful will be here.
    Servers
    @@ -22,7 +22,7 @@
    {{ $s3s->count() }}
    - @if (is_dev()) + @if (isDev()) {{-- --}} @endif
    diff --git a/resources/views/emails/subscription-invoice-failed.blade.php b/resources/views/emails/subscription-invoice-failed.blade.php new file mode 100644 index 000000000..9d04eebd4 --- /dev/null +++ b/resources/views/emails/subscription-invoice-failed.blade.php @@ -0,0 +1,4 @@ +Your last invoice has failed to be paid for Coolify Cloud. Please update payment details on your Stripe Customer Portal. +

    +Thanks,
    +Coolify Cloud diff --git a/resources/views/emails/waitlist-invitation.blade.php b/resources/views/emails/waitlist-invitation.blade.php index 8a7647f59..7c941d925 100644 --- a/resources/views/emails/waitlist-invitation.blade.php +++ b/resources/views/emails/waitlist-invitation.blade.php @@ -1,4 +1,5 @@ Congratulations!
    +Congratulations!

    You have been invited to join the Coolify Cloud. Login here
    diff --git a/resources/views/livewire/boarding.blade.php b/resources/views/livewire/boarding.blade.php new file mode 100644 index 000000000..c6482cbeb --- /dev/null +++ b/resources/views/livewire/boarding.blade.php @@ -0,0 +1,195 @@ +@php use App\Enums\ProxyTypes; @endphp +
    +
    +
    + @if ($currentState === 'welcome') +

    Welcome to Coolify

    +

    Let me help you to set the basics.

    +
    +
    Get Started +
    +
    + @endif + @if ($currentState === 'select-server') + + + Do you want to deploy your resources on your + or on a ? + + +
    Localhost +
    +
    Remote Server +
    +
    + +

    Servers are the main building blocks, as they will host your applications, databases, + services, called resources. Any CPU intensive process will use the server's CPU where you + are deploying your resources.

    +

    Localhost is the server where Coolify is running on. It is not recommended to use one server + for everyting.

    +

    Remote Server is a server reachable through SSH. It can be hosted at home, or from any cloud + provider.

    +
    +
    + @endif + @if ($currentState === 'private-key') + + + Do you have your own SSH Private Key? + + +
    Yes +
    +
    No (create one for me) +
    +
    + +

    SSH Keys are used to connect to a remote server through a secure shell, called SSH.

    +

    You can use your own ssh private key, or you can let Coolify to create one for you.

    +

    In both ways, you need to add the public version of your ssh private key to the remote + server's + ~/.ssh/authorized_keys file. +

    +
    +
    + @endif + @if ($currentState === 'create-private-key') + + + Please let me know your key details. + + +
    + + + + @if ($privateKeyType === 'create' && !isDev()) + Copy this to your server's ~/.ssh/authorized_keys file. + + @endif + Save + +
    + +

    Private Keys are used to connect to a remote server through a secure shell, called SSH.

    +

    You can use your own private key, or you can let Coolify to create one for you.

    +

    In both ways, you need to add the public version of your private key to the remote server's + ~/.ssh/authorized_keys file. +

    +
    +
    + @endif + @if ($currentState === 'create-server') + + + Please let me know your server details. + + +
    +
    + + +
    +
    + + + +
    + Save +
    +
    + +

    Username should be for now. We are working on to use + non-root users.

    +
    +
    + @endif + @if ($currentState === 'install-docker') + + + Could not find Docker Engine on your server. Do you want me to install it for you? + + +
    + Let's do + it!
    +
    + +

    This will install the latest Docker Engine on your server, configure a few things to be able + to run optimal.

    +
    +
    + @endif + @if ($currentState === 'select-proxy') + + + If you would like to attach any kind of domain to your resources, you need a proxy. + + + + Decide later + + + Traefik + v2 + + + Nginx + + + Caddy + + + +

    This will install the latest Docker Engine on your server, configure a few things to be able + to run optimal.

    +
    +
    + @endif + @if ($currentState === 'create-project') + + + I will create an initial project for you. You can change all the details later on. + + +
    Let's do it!
    +
    + +

    Projects are bound together several resources into one virtual group. There are no + limitations on the number of projects you could have.

    +

    Each project should have at least one environment. This helps you to create a production & + staging version of the same application, but grouped separately.

    +
    +
    + @endif + @if ($currentState === 'create-resource') + + + I will redirect you to the new resource page, where you can create your first resource. + + +
    Let's do + it!
    +
    + +

    A resource could be an application, a database or a service (like WordPress).

    +
    +
    + @endif + +
    +
    +
    diff --git a/resources/views/livewire/destination/show.blade.php b/resources/views/livewire/destination/show.blade.php index f39b2a3a8..1ab18826e 100644 --- a/resources/views/livewire/destination/show.blade.php +++ b/resources/views/livewire/destination/show.blade.php @@ -18,18 +18,23 @@
    N/A
    @endforelse
    -
    +
    @if (count($networks) > 0) -

    Found Destinations

    +

    Found Destinations

    @endif +
    @foreach ($networks as $network) + @endforeach
    +
    @else
    Server is not validated. Validate first.
    @endif diff --git a/resources/views/livewire/notifications/discord-settings.blade.php b/resources/views/livewire/notifications/discord-settings.blade.php index a5f7caf02..6fd5d8d31 100644 --- a/resources/views/livewire/notifications/discord-settings.blade.php +++ b/resources/views/livewire/notifications/discord-settings.blade.php @@ -22,7 +22,7 @@ @if (data_get($model, 'discord_enabled'))

    Subscribe to events

    - @if (is_dev()) + @if (isDev()) @endif

    General

    diff --git a/resources/views/livewire/notifications/email-settings.blade.php b/resources/views/livewire/notifications/email-settings.blade.php index c67a6098a..2de6ec223 100644 --- a/resources/views/livewire/notifications/email-settings.blade.php +++ b/resources/views/livewire/notifications/email-settings.blade.php @@ -16,7 +16,7 @@ Save - @if (is_instance_admin()) + @if (isInstanceAdmin()) Copy from Instance Settings @@ -59,7 +59,7 @@ @if (data_get($model, 'smtp_enabled'))

    Subscribe to events

    - @if (is_dev()) + @if (isDev()) @endif

    General

    diff --git a/resources/views/livewire/project/edit.blade.php b/resources/views/livewire/project/edit.blade.php index 1d212956e..dfcab8d22 100644 --- a/resources/views/livewire/project/edit.blade.php +++ b/resources/views/livewire/project/edit.blade.php @@ -3,9 +3,6 @@

    Project: {{ data_get($project, 'name') }}

    Save - @if ($project->applications->count() === 0) - - @endif
    Edit project details here.
    diff --git a/resources/views/livewire/project/new/github-private-repository.blade.php b/resources/views/livewire/project/new/github-private-repository.blade.php index c1e26dcb5..72950718b 100644 --- a/resources/views/livewire/project/new/github-private-repository.blade.php +++ b/resources/views/livewire/project/new/github-private-repository.blade.php @@ -1,8 +1,14 @@
    -

    Create a new Application

    +
    +

    Create a new Application

    + + + Add New GitHub App + + +
    Deploy any public or private git repositories through a GitHub App.
    - @if ($github_apps->count() > 0) -
    + @if ($github_apps->count() == 0) +
    @if ($current_step === 'github_apps')
    • Select a GitHub App
    • @@ -12,11 +18,13 @@ @foreach ($github_apps as $ghapp) @if ($selected_github_app_id == $ghapp->id)
      + wire:click.prevent="loadRepositories({{ $ghapp->id }})" + wire:key="{{ $ghapp->id }}">
      {{ $ghapp->name }}
      +
      {{ $ghapp->http_url }}
      @@ -25,12 +33,14 @@
      -
      +
      - {{ $ghapp->name }} + {{ data_get($ghapp, 'name') }}
      +
      + {{ data_get($ghapp, 'html_url') }}
      + class="">Loading...
      @endif @@ -65,7 +75,6 @@
      No repositories found. Check your GitHub App configuration.
      @endif @if ($branches->count() > 0) -

      Details

      @@ -104,11 +113,8 @@ @endif
      @else -
      -
      No Git App found.
      - - Add - +
      + No GitHub Application found. Please create a new GitHub Application.
      @endif
      diff --git a/resources/views/livewire/project/new/select.blade.php b/resources/views/livewire/project/new/select.blade.php index fa07a0ec1..bdb6ac4ad 100644 --- a/resources/views/livewire/project/new/select.blade.php +++ b/resources/views/livewire/project/new/select.blade.php @@ -10,7 +10,7 @@

    Applications

    -
    @@ -21,7 +21,7 @@
    -
    @@ -32,7 +32,7 @@
    -
    @@ -45,7 +45,7 @@
    -
    @@ -59,7 +59,7 @@

    Databases

    -
    @@ -79,9 +79,9 @@
  • Select a Server
  • Select a Destination
  • -
    - @foreach ($servers as $server) -
    + @forelse($servers as $server) +
    @@ -91,7 +91,15 @@ {{ $server->description }}
    - @endforeach + @empty +
    +
    No validated & reachable servers found. + Go to servers page +
    + + +
    + @endforelse
    @endif @if ($current_step === 'destinations') @@ -100,9 +108,9 @@
  • Select a Server
  • Select a Destination
  • -
    +
    @foreach ($destinations as $destination) -
    diff --git a/resources/views/livewire/server/form.blade.php b/resources/views/livewire/server/form.blade.php index b103bafe5..ae0ac48c7 100644 --- a/resources/views/livewire/server/form.blade.php +++ b/resources/views/livewire/server/form.blade.php @@ -18,11 +18,14 @@ @else Save @endif - - Validate Server - + @if (!$server->settings->is_reachable || !$server->settings->is_usable) + + Validate Server + + @endif +
    - @if (!$server->settings->is_reachable) + @if (!$server->settings->is_reachable || !$server->settings->is_usable) You can't use this server until it is validated. @else Server validated. @@ -90,7 +93,7 @@
    This will remove this server from Coolify. Beware! There is no coming back!
    - @if ($server->id !== 0 || is_dev()) + @if ($server->id !== 0 || isDev()) Delete diff --git a/resources/views/livewire/server/proxy/deploy.blade.php b/resources/views/livewire/server/proxy/deploy.blade.php index 36536f0a0..685cddbdf 100644 --- a/resources/views/livewire/server/proxy/deploy.blade.php +++ b/resources/views/livewire/server/proxy/deploy.blade.php @@ -9,9 +9,8 @@ -

    This will start the proxy on this server and stop any running process that is - using port 80 and - 443. +

    This will start the proxy on this server and + .
    Please think again.

    diff --git a/resources/views/livewire/server/proxy/status.blade.php b/resources/views/livewire/server/proxy/status.blade.php index 41fef8efc..9a024135e 100644 --- a/resources/views/livewire/server/proxy/status.blade.php +++ b/resources/views/livewire/server/proxy/status.blade.php @@ -1,4 +1,4 @@ -
    +
    @if ($server->proxy->status === 'running') @elseif ($server->proxy->status === 'restarting') diff --git a/resources/views/livewire/subscription/actions.blade.php b/resources/views/livewire/subscription/actions.blade.php index 334adcfe4..695ea2f78 100644 --- a/resources/views/livewire/subscription/actions.blade.php +++ b/resources/views/livewire/subscription/actions.blade.php @@ -1,31 +1,48 @@
    -
    Status: {{ auth()->user()->currentTeam()->subscription->lemon_status }}
    -
    Type: {{ auth()->user()->currentTeam()->subscription->lemon_variant_name }}
    - @if (auth()->user()->currentTeam()->subscription->lemon_status === 'cancelled') -
    Subscriptions ends at: {{ getRenewDate() }}
    -
    If you would like to change the subscription to a lower/higher plan, please - contact - us.
    - @else -
    Renews at: {{ getRenewDate() }}
    + @if (subscriptionProvider() === 'stripe') + @if (currentTeam()->subscription->stripe_cancel_at_period_end) +
    Subscription is active but on cancel period.
    + @else +
    Subscription is active. Last invoice is + {{ currentTeam()->subscription->stripe_invoice_paid ? 'paid' : 'not paid' }}.
    + @endif + + @if (currentTeam()->subscription->stripe_cancel_at_period_end) + Subscribe + again + @endif + Manage My Subscription @endif -
    -
    - @if (auth()->user()->currentTeam()->subscription->lemon_status === 'cancelled') - Resume Subscription + @if (subscriptionProvider() === 'lemon') +
    Status: {{ currentTeam()->subscription->lemon_status }}
    +
    Type: {{ currentTeam()->subscription->lemon_variant_name }}
    + @if (currentTeam()->subscription->lemon_status === 'cancelled') +
    Subscriptions ends at: {{ getRenewDate() }}
    +
    If you would like to change the subscription to a lower/higher plan, please + contact + us.
    + @else +
    Renews at: {{ getRenewDate() }}
    + @endif +
    +
    + @if (currentTeam()->subscription->lemon_status === 'cancelled') + Resume Subscription + + @else + Cancel Subscription + @endif +
    +
    + Update Payment + Details - @else - Cancel Subscription - @endif + Manage My + Subscription +
    - -
    + @endif +
    diff --git a/resources/views/livewire/subscription/pricing-plans.blade.php b/resources/views/livewire/subscription/pricing-plans.blade.php new file mode 100644 index 000000000..c19e7e075 --- /dev/null +++ b/resources/views/livewire/subscription/pricing-plans.blade.php @@ -0,0 +1,63 @@ + + @if (config('subscription.provider') === 'stripe') + + Subscribe + + + Subscribe + + + + Subscribe + + + Subscribe + + + + Subscribe + + + Subscribe + + + @endif + @if (config('subscription.provider') === 'paddle') + + @endif + @if (config('subscription.provider') === 'lemon') + + Subscribe + + + Subscribe + + + + Subscribe + + + Subscribe + + + + Subscribe + + + Subscribe + + + @endif + diff --git a/resources/views/livewire/team/delete.blade.php b/resources/views/livewire/team/delete.blade.php index b25b2e303..60a97b93f 100644 --- a/resources/views/livewire/team/delete.blade.php +++ b/resources/views/livewire/team/delete.blade.php @@ -11,11 +11,11 @@
    This is the default team. You can't delete it.
    @elseif(auth()->user()->teams()->get()->count() === 1)
    You can't delete your last team.
    - @elseif(auth()->user()->currentTeam()->subscription && - auth()->user()->currentTeam()->subscription?->lemon_status !== 'cancelled') + @elseif(currentTeam()->subscription && + currentTeam()->subscription?->lemon_status !== 'cancelled')
    Please cancel your subscription before delete this team (Manage My Subscription button).
    @else - @if (auth()->user()->currentTeam()->isEmpty()) + @if (currentTeam()->isEmpty())
    This will delete your team. Beware! There is no coming back!
    Delete @@ -25,25 +25,25 @@
    You need to delete the following resources to be able to delete the team:

    Projects:

      - @foreach (auth()->user()->currentTeam()->projects as $resource) + @foreach (currentTeam()->projects as $resource)
    • {{ $resource->name }}
    • @endforeach

    Servers:

      - @foreach (auth()->user()->currentTeam()->servers as $resource) + @foreach (currentTeam()->servers as $resource)
    • {{ $resource->name }}
    • @endforeach

    Private Keys:

      - @foreach (auth()->user()->currentTeam()->privateKeys as $resource) + @foreach (currentTeam()->privateKeys as $resource)
    • {{ $resource->name }}
    • @endforeach

    Sources:

      - @foreach (auth()->user()->currentTeam()->sources() as $resource) + @foreach (currentTeam()->sources() as $resource)
    • {{ $resource->name }}
    • @endforeach
    diff --git a/resources/views/project/resources.blade.php b/resources/views/project/resources.blade.php index 30e72bcc3..96bdf8aad 100644 --- a/resources/views/project/resources.blade.php +++ b/resources/views/project/resources.blade.php @@ -36,20 +36,20 @@ @endif
    @foreach ($environment->applications->sortBy('name') as $application) - -
    -
    {{ $application->name }}
    -
    {{ $application->description }}
    +
    +
    {{ $application->name }}
    +
    {{ $application->description }}
    @endforeach @foreach ($environment->databases->sortBy('name') as $databases) - -
    -
    {{ $databases->name }}
    -
    {{ $databases->description }}
    +
    +
    {{ $databases->name }}
    +
    {{ $databases->description }}
    @endforeach diff --git a/resources/views/project/show.blade.php b/resources/views/project/show.blade.php index dcd350419..bfda9146f 100644 --- a/resources/views/project/show.blade.php +++ b/resources/views/project/show.blade.php @@ -3,11 +3,14 @@

    Environments

    + Add + @if ($project->applications->count() === 0) + + @endif
    -
    {{ $project->name }}
    +
    {{ $project->name }}
    @forelse ($project->environments as $environment) - + {{ $environment->name }} @empty diff --git a/resources/views/server/all.blade.php b/resources/views/server/all.blade.php index afa223b91..048fb311c 100644 --- a/resources/views/server/all.blade.php +++ b/resources/views/server/all.blade.php @@ -1,5 +1,9 @@ -

    Servers

    +
    +

    Servers

    + + Add + +
    All Servers
    @forelse ($servers as $server) diff --git a/resources/views/subscription/cancel.blade.php b/resources/views/subscription/cancel.blade.php new file mode 100644 index 000000000..dc7e3b55b --- /dev/null +++ b/resources/views/subscription/cancel.blade.php @@ -0,0 +1,3 @@ + + Cancel + diff --git a/resources/views/subscription.blade.php b/resources/views/subscription/show.blade.php similarity index 65% rename from resources/views/subscription.blade.php rename to resources/views/subscription/show.blade.php index 382bf6cb0..669e94c0e 100644 --- a/resources/views/subscription.blade.php +++ b/resources/views/subscription/show.blade.php @@ -1,7 +1,7 @@ @if ($settings->is_resale_license_active)
    -
    +

    Subscription

    @@ -10,7 +10,12 @@ Currently active team: {{ session('currentTeam.name') }}
    - + @if(request()->query->get('cancelled')) +
    Something went wrong. Please try again.
    + @endif + @if (config('subscription.provider') !== null) + + @endif
    @else diff --git a/resources/views/subscription/success.blade.php b/resources/views/subscription/success.blade.php new file mode 100644 index 000000000..9cb5911cb --- /dev/null +++ b/resources/views/subscription/success.blade.php @@ -0,0 +1,3 @@ + + Success + diff --git a/resources/views/team/members.blade.php b/resources/views/team/members.blade.php index 9cb8fa2e8..5366e61f4 100644 --- a/resources/views/team/members.blade.php +++ b/resources/views/team/members.blade.php @@ -14,7 +14,7 @@ - @foreach (auth()->user()->currentTeam()->members->sortBy('name') as $member) + @foreach (currentTeam()->members->sortBy('name') as $member) @endforeach @@ -26,7 +26,7 @@

    Invite a new member

    @else

    Invite a new member

    - @if (is_instance_admin()) + @if (isInstanceAdmin())
    You need to configure Transactional Emails diff --git a/resources/views/team/show.blade.php b/resources/views/team/show.blade.php index 3817022ab..25a465452 100644 --- a/resources/views/team/show.blade.php +++ b/resources/views/team/show.blade.php @@ -6,12 +6,12 @@ @if (is_cloud())

    Subscription

    - @if (data_get(auth()->user()->currentTeam(), + @if (data_get(currentTeam(), 'subscription')) @else Subscribe Now + href="{{ route('subscription.show') }}">Subscribe Now @endif diff --git a/routes/web.php b/routes/web.php index 81dd776ec..ce98b65b7 100644 --- a/routes/web.php +++ b/routes/web.php @@ -93,10 +93,13 @@ Route::middleware(['auth'])->group(function () { Route::middleware(['auth'])->group(function () { Route::get('/', [Controller::class, 'dashboard'])->name('dashboard'); + Route::get('/boarding', [Controller::class, 'boarding'])->name('boarding'); Route::middleware(['throttle:force-password-reset'])->group(function() { Route::get('/force-password-reset', [Controller::class, 'force_passoword_reset'])->name('auth.force-password-reset'); }); - Route::get('/subscription', [Controller::class, 'subscription'])->name('subscription'); + Route::get('/subscription', [Controller::class, 'subscription'])->name('subscription.show'); + Route::get('/subscription/success', fn () => view('subscription.success'))->name('subscription.success'); + Route::get('/subscription/cancel', fn () => view('profile'))->name('subscription.cancel'); Route::get('/settings', [Controller::class, 'settings'])->name('settings.configuration'); Route::get('/settings/license', [Controller::class, 'license'])->name('settings.license'); Route::get('/profile', fn () => view('profile', ['request' => request()]))->name('profile'); @@ -126,7 +129,7 @@ Route::middleware(['auth'])->group(function () { Route::middleware(['auth'])->group(function () { Route::get('/source/new', fn () => view('source.new'))->name('source.new'); Route::get('/sources', function () { - $sources = auth()->user()->currentTeam()->sources(); + $sources = currentTeam()->sources(); return view('source.all', [ 'sources' => $sources, ]); diff --git a/routes/webhooks.php b/routes/webhooks.php index 77ef23c5d..026b2ebc3 100644 --- a/routes/webhooks.php +++ b/routes/webhooks.php @@ -1,5 +1,6 @@ id)->where('pull_request_id', $pull_request_id)->first(); if ($found) { $found->delete(); - $container_name = generate_container_name($application->uuid, $pull_request_id); + $container_name = generateApplicationContainerName($application->uuid, $pull_request_id); ray('Stopping container: ' . $container_name); remote_process(["docker rm -f $container_name"], $application->destination->server); return response('Preview Deployment closed.'); @@ -206,9 +207,133 @@ Route::get('/waitlist/cancel', function () { return redirect()->route('dashboard'); } })->name('webhooks.waitlist.cancel'); -Route::post('/payments/events', function () { + + +Route::post('/payments/stripe/events', function () { try { - $secret = config('coolify.lemon_squeezy_webhook_secret'); + $webhookSecret = config('subscription.stripe_webhook_secret'); + $signature = request()->header('Stripe-Signature'); + + $event = \Stripe\Webhook::constructEvent( + request()->getContent(), + $signature, + $webhookSecret + ); + $webhook = Webhook::create([ + 'type' => 'stripe', + 'payload' => request()->getContent() + ]); + $type = data_get($event, 'type'); + $data = data_get($event, 'data.object'); + switch ($type) { + case 'checkout.session.completed': + $clientReferenceId = data_get($data, 'client_reference_id'); + $userId = Str::before($clientReferenceId, ':'); + $teamId = Str::after($clientReferenceId, ':'); + $subscriptionId = data_get($data, 'subscription'); + $customerId = data_get($data, 'customer'); + $team = Team::find($teamId); + $found = $team->members->where('id', $userId)->first(); + if (!$found->isAdmin()) { + throw new Exception("User {$userId} is not an admin or owner of team {$team->id}."); + } + $subscription = Subscription::where('team_id', $teamId)->first(); + if ($subscription) { + $subscription->update([ + 'stripe_subscription_id' => $subscriptionId, + 'stripe_customer_id' => $customerId, + 'stripe_invoice_paid' => true, + ]); + } else { + Subscription::create([ + 'team_id' => $teamId, + 'stripe_subscription_id' => $subscriptionId, + 'stripe_customer_id' => $customerId, + 'stripe_invoice_paid' => true, + ]); + } + break; + case 'invoice.paid': + $subscriptionId = data_get($data, 'lines.data.0.subscription'); + $subscription = Subscription::where('stripe_subscription_id', $subscriptionId)->firstOrFail(); + $subscription->update([ + 'stripe_invoice_paid' => true, + ]); + break; + case 'invoice.payment_failed': + $customerId = data_get($data, 'customer'); + $subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail(); + SubscriptionInvoiceFailedJob::dispatch($subscription->team); + break; + case 'customer.subscription.updated': + $subscriptionId = data_get($data, 'items.data.0.subscription'); + $cancelAtPeriodEnd = data_get($data, 'cancel_at_period_end'); + $subscription = Subscription::where('stripe_subscription_id', $subscriptionId)->firstOrFail(); + $subscription->update([ + 'stripe_cancel_at_period_end' => $cancelAtPeriodEnd, + ]); + break; + case 'customer.subscription.deleted': + $subscriptionId = data_get($data, 'items.data.0.subscription'); + $subscription = Subscription::where('stripe_subscription_id', $subscriptionId)->firstOrFail(); + $subscription->update([ + 'stripe_cancel_at_period_end' => false, + 'stripe_invoice_paid' => false, + ]); + break; + default: + // Unhandled event type + } + } catch (Exception $e) { + ray($e->getMessage()); + send_internal_notification('Subscription webhook failed: ' . $e->getMessage()); + $webhook->update([ + 'status' => 'failed', + 'failure_reason' => $e->getMessage(), + ]); + return response($e->getMessage(), 400); + } +}); +Route::post('/payments/paddle/events', function () { + try { + $payload = request()->all(); + $signature = request()->header('Paddle-Signature'); + $ts = Str::of($signature)->after('ts=')->before(';'); + $h1 = Str::of($signature)->after('h1='); + $signedPayload = $ts->value . ':' . request()->getContent(); + $verify = hash_hmac('sha256', $signedPayload, config('subscription.paddle_webhook_secret')); + ray($verify, $h1->value, hash_equals($verify, $h1->value)); + if (!hash_equals($verify, $h1->value)) { + return response('Invalid signature.', 400); + } + $eventType = data_get($payload, 'event_type'); + ray($eventType); + $webhook = Webhook::create([ + 'type' => 'paddle', + 'payload' => $payload, + ]); + // TODO - Handle events + switch ($eventType) { + case 'subscription.activated': + } + ray('Subscription event: ' . $eventType); + $webhook->update([ + 'status' => 'success', + ]); + } catch (Exception $e) { + ray($e->getMessage()); + send_internal_notification('Subscription webhook failed: ' . $e->getMessage()); + $webhook->update([ + 'status' => 'failed', + 'failure_reason' => $e->getMessage(), + ]); + } finally { + return response('OK'); + } +}); +Route::post('/payments/lemon/events', function () { + try { + $secret = config('subscription.lemon_squeezy_webhook_secret'); $payload = request()->collect(); $hash = hash_hmac('sha256', $payload, $secret); $signature = request()->header('X-Signature'); diff --git a/scripts/run b/scripts/run index 2723f55c6..9c3a04eca 100755 --- a/scripts/run +++ b/scripts/run @@ -47,9 +47,6 @@ function schedule:run { bash spin exec -u webuser coolify php artisan schedule:run } -function db:reset { - bash spin exec -u webuser coolify php artisan migrate:fresh --seed -} function db { bash spin exec -u webuser coolify php artisan db @@ -59,6 +56,9 @@ function db:migrate { bash spin exec -u webuser coolify php artisan migrate } +function db:reset { + bash spin exec -u webuser coolify php artisan migrate:fresh --seed +} function db:reset-prod { bash spin exec -u webuser coolify php artisan migrate:fresh --force --seed --seeder=ProductionSeeder || php artisan migrate:fresh --force --seed --seeder=ProductionSeeder diff --git a/versions.json b/versions.json index 2efda3c64..5215932b7 100644 --- a/versions.json +++ b/versions.json @@ -4,7 +4,7 @@ "version": "3.12.36" }, "v4": { - "version": "4.0.0-beta.19" + "version": "4.0.0-beta.20" } } }