mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-14 06:17:36 +00:00
Merge remote-tracking branch 'origin/next' into env-var-descriptions
This commit is contained in:
commit
d9e39ba211
404 files changed
+29406
-26356
No files matched your search
@@ -86,8 +86,11 @@ function sharedDataApplications()
|
||||
'git_branch' => 'string',
|
||||
'build_pack' => Rule::enum(BuildPackTypes::class),
|
||||
'is_static' => 'boolean',
|
||||
'is_spa' => 'boolean',
|
||||
'is_auto_deploy_enabled' => 'boolean',
|
||||
'is_force_https_enabled' => 'boolean',
|
||||
'static_image' => Rule::enum(StaticImageTypes::class),
|
||||
'domains' => 'string',
|
||||
'domains' => 'string|nullable',
|
||||
'redirect' => Rule::enum(RedirectTypes::class),
|
||||
'git_commit_sha' => 'string',
|
||||
'docker_registry_image_name' => 'string|nullable',
|
||||
@@ -101,12 +104,14 @@ function sharedDataApplications()
|
||||
'base_directory' => 'string|nullable',
|
||||
'publish_directory' => 'string|nullable',
|
||||
'health_check_enabled' => 'boolean',
|
||||
'health_check_path' => 'string',
|
||||
'health_check_port' => 'string|nullable',
|
||||
'health_check_host' => 'string',
|
||||
'health_check_method' => 'string',
|
||||
'health_check_type' => 'string|in:http,cmd',
|
||||
'health_check_command' => ['nullable', 'string', 'max:1000', 'regex:/^[a-zA-Z0-9 \-_.\/:=@,+]+$/'],
|
||||
'health_check_path' => ['string', 'regex:#^[a-zA-Z0-9/\-_.~%]+$#'],
|
||||
'health_check_port' => 'integer|nullable|min:1|max:65535',
|
||||
'health_check_host' => ['string', 'regex:/^[a-zA-Z0-9.\-_]+$/'],
|
||||
'health_check_method' => 'string|in:GET,HEAD,POST,OPTIONS',
|
||||
'health_check_return_code' => 'numeric',
|
||||
'health_check_scheme' => 'string',
|
||||
'health_check_scheme' => 'string|in:http,https',
|
||||
'health_check_response_text' => 'string|nullable',
|
||||
'health_check_interval' => 'numeric',
|
||||
'health_check_timeout' => 'numeric',
|
||||
@@ -129,12 +134,13 @@ function sharedDataApplications()
|
||||
'manual_webhook_secret_gitlab' => 'string|nullable',
|
||||
'manual_webhook_secret_bitbucket' => 'string|nullable',
|
||||
'manual_webhook_secret_gitea' => 'string|nullable',
|
||||
'docker_compose_location' => 'string',
|
||||
'dockerfile_location' => ['string', 'nullable', 'max:255', 'regex:/^\/[a-zA-Z0-9._\-\/]+$/'],
|
||||
'docker_compose_location' => ['string', 'nullable', 'max:255', 'regex:/^\/[a-zA-Z0-9._\-\/]+$/'],
|
||||
'docker_compose' => 'string|nullable',
|
||||
'docker_compose_raw' => 'string|nullable',
|
||||
'docker_compose_domains' => 'array|nullable',
|
||||
'docker_compose_custom_start_command' => 'string|nullable',
|
||||
'docker_compose_custom_build_command' => 'string|nullable',
|
||||
'is_container_label_escape_enabled' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -177,6 +183,12 @@ function removeUnnecessaryFieldsFromRequest(Request $request)
|
||||
$request->offsetUnset('private_key_uuid');
|
||||
$request->offsetUnset('use_build_server');
|
||||
$request->offsetUnset('is_static');
|
||||
$request->offsetUnset('is_spa');
|
||||
$request->offsetUnset('is_auto_deploy_enabled');
|
||||
$request->offsetUnset('is_force_https_enabled');
|
||||
$request->offsetUnset('connect_to_docker_network');
|
||||
$request->offsetUnset('force_domain_override');
|
||||
$request->offsetUnset('autogenerate_domain');
|
||||
$request->offsetUnset('is_container_label_escape_enabled');
|
||||
$request->offsetUnset('docker_compose_raw');
|
||||
}
|
||||
@@ -191,6 +191,10 @@ function clone_application(Application $source, $destination, array $overrides =
|
||||
$uuid = $overrides['uuid'] ?? (string) new Cuid2;
|
||||
$server = $destination->server;
|
||||
|
||||
if ($server->team_id !== currentTeam()->id) {
|
||||
throw new \RuntimeException('Destination does not belong to the current team.');
|
||||
}
|
||||
|
||||
// Prepare name and URL
|
||||
$name = $overrides['name'] ?? 'clone-of-'.str($source->name)->limit(20).'-'.$uuid;
|
||||
$applicationSettings = $source->settings;
|
||||
|
||||
@@ -72,7 +72,7 @@ const SUPPORTED_OS = [
|
||||
|
||||
const NEEDS_TO_CONNECT_TO_PREDEFINED_NETWORK = [
|
||||
'pgadmin',
|
||||
'postgresus',
|
||||
'databasus',
|
||||
'redis-insight',
|
||||
];
|
||||
const NEEDS_TO_DISABLE_GZIP = [
|
||||
|
||||
@@ -129,8 +129,8 @@ function format_docker_envs_to_json($rawOutput)
|
||||
}
|
||||
function checkMinimumDockerEngineVersion($dockerVersion)
|
||||
{
|
||||
$majorDockerVersion = str($dockerVersion)->before('.')->value();
|
||||
$requiredDockerVersion = str(config('constants.docker.minimum_required_version'))->before('.')->value();
|
||||
$majorDockerVersion = (int) str($dockerVersion)->before('.')->value();
|
||||
$requiredDockerVersion = (int) str(config('constants.docker.minimum_required_version'))->before('.')->value();
|
||||
if ($majorDockerVersion < $requiredDockerVersion) {
|
||||
$dockerVersion = null;
|
||||
}
|
||||
@@ -139,8 +139,9 @@ function checkMinimumDockerEngineVersion($dockerVersion)
|
||||
}
|
||||
function executeInDocker(string $containerId, string $command)
|
||||
{
|
||||
return "docker exec {$containerId} bash -c '{$command}'";
|
||||
// return "docker exec {$this->deployment_uuid} bash -c '{$command} |& tee -a /proc/1/fd/1; [ \$PIPESTATUS -eq 0 ] || exit \$PIPESTATUS'";
|
||||
$escapedCommand = str_replace("'", "'\\''", $command);
|
||||
|
||||
return "docker exec {$containerId} bash -c '{$escapedCommand}'";
|
||||
}
|
||||
|
||||
function getContainerStatus(Server $server, string $container_id, bool $all_data = false, bool $throwError = false)
|
||||
@@ -231,7 +232,7 @@ function defaultLabels($id, $name, string $projectName, string $resourceName, st
|
||||
$labels->push('coolify.version='.config('constants.coolify.version'));
|
||||
$labels->push('coolify.'.$type.'Id='.$id);
|
||||
$labels->push("coolify.type=$type");
|
||||
$labels->push('coolify.name='.$name);
|
||||
$labels->push('coolify.name='.Str::slug($name));
|
||||
$labels->push('coolify.resourceName='.Str::slug($resourceName));
|
||||
$labels->push('coolify.projectName='.Str::slug($projectName));
|
||||
$labels->push('coolify.serviceName='.Str::slug($subName ?? $resourceName));
|
||||
|
||||
@@ -158,8 +158,7 @@ function checkIfDomainIsAlreadyUsedViaAPI(Collection|array $domains, ?string $te
|
||||
return str($domain);
|
||||
});
|
||||
|
||||
// Check applications within the same team
|
||||
$applications = Application::ownedByCurrentTeamAPI($teamId)->get(['fqdn', 'uuid', 'name', 'id']);
|
||||
$applications = Application::ownedByCurrentTeamAPI($teamId)->get(['fqdn', 'uuid', 'name', 'id', 'docker_compose_domains', 'build_pack']);
|
||||
$serviceApplications = ServiceApplication::ownedByCurrentTeamAPI($teamId)->with('service:id,name')->get(['fqdn', 'uuid', 'id', 'service_id']);
|
||||
|
||||
if ($uuid) {
|
||||
@@ -168,23 +167,51 @@ function checkIfDomainIsAlreadyUsedViaAPI(Collection|array $domains, ?string $te
|
||||
}
|
||||
|
||||
foreach ($applications as $app) {
|
||||
if (is_null($app->fqdn)) {
|
||||
continue;
|
||||
}
|
||||
$list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== '');
|
||||
foreach ($list_of_domains as $domain) {
|
||||
if (str($domain)->endsWith('/')) {
|
||||
$domain = str($domain)->beforeLast('/');
|
||||
if (! is_null($app->fqdn)) {
|
||||
$list_of_domains = collect(explode(',', $app->fqdn))->filter(fn ($fqdn) => $fqdn !== '');
|
||||
foreach ($list_of_domains as $domain) {
|
||||
if (str($domain)->endsWith('/')) {
|
||||
$domain = str($domain)->beforeLast('/');
|
||||
}
|
||||
$naked_domain = str($domain)->value();
|
||||
if ($domains->contains($naked_domain)) {
|
||||
$conflicts[] = [
|
||||
'domain' => $naked_domain,
|
||||
'resource_name' => $app->name,
|
||||
'resource_uuid' => $app->uuid,
|
||||
'resource_type' => 'application',
|
||||
'message' => "Domain $naked_domain is already in use by application '{$app->name}'",
|
||||
];
|
||||
}
|
||||
}
|
||||
$naked_domain = str($domain)->value();
|
||||
if ($domains->contains($naked_domain)) {
|
||||
$conflicts[] = [
|
||||
'domain' => $naked_domain,
|
||||
'resource_name' => $app->name,
|
||||
'resource_uuid' => $app->uuid,
|
||||
'resource_type' => 'application',
|
||||
'message' => "Domain $naked_domain is already in use by application '{$app->name}'",
|
||||
];
|
||||
}
|
||||
|
||||
if ($app->build_pack === 'dockercompose' && ! empty($app->docker_compose_domains)) {
|
||||
$dockerComposeDomains = json_decode($app->docker_compose_domains, true);
|
||||
if (is_array($dockerComposeDomains)) {
|
||||
foreach ($dockerComposeDomains as $serviceName => $domainConfig) {
|
||||
$domainValue = data_get($domainConfig, 'domain');
|
||||
if (empty($domainValue)) {
|
||||
continue;
|
||||
}
|
||||
$list_of_domains = collect(explode(',', $domainValue))->filter(fn ($fqdn) => $fqdn !== '');
|
||||
foreach ($list_of_domains as $domain) {
|
||||
if (str($domain)->endsWith('/')) {
|
||||
$domain = str($domain)->beforeLast('/');
|
||||
}
|
||||
$naked_domain = str($domain)->value();
|
||||
if ($domains->contains($naked_domain)) {
|
||||
$conflicts[] = [
|
||||
'domain' => $naked_domain,
|
||||
'resource_name' => $app->name,
|
||||
'resource_uuid' => $app->uuid,
|
||||
'resource_type' => 'application',
|
||||
'service_name' => $serviceName,
|
||||
'message' => "Domain $naked_domain is already in use by application '{$app->name}' (service: {$serviceName})",
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,3 +162,54 @@ function loadRepositoryByPage(GithubApp $source, string $token, int $page)
|
||||
'repositories' => $json['repositories'],
|
||||
];
|
||||
}
|
||||
function getGithubCommitRangeFiles(?GithubApp $source, string $owner, string $repo, string $beforeSha, string $afterSha): array
|
||||
{
|
||||
try {
|
||||
if (! $source) {
|
||||
// Manual webhooks don't have GitHub App authentication
|
||||
// Return empty array so watch paths are ignored (current behavior)
|
||||
return [];
|
||||
}
|
||||
|
||||
$endpoint = "/repos/{$owner}/{$repo}/compare/{$beforeSha}...{$afterSha}";
|
||||
$response = githubApi($source, $endpoint, 'get', null, false);
|
||||
|
||||
if (! $response) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$files = collect(data_get($response, 'data.files', []));
|
||||
|
||||
return $files->pluck('filename')->filter()->values()->toArray();
|
||||
} catch (Exception $e) {
|
||||
ray('Error fetching GitHub commit range files: '.$e->getMessage());
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function getGithubPullRequestFiles(?GithubApp $source, string $owner, string $repo, int $pullRequestId): array
|
||||
{
|
||||
try {
|
||||
if (! $source) {
|
||||
// Manual webhooks don't have GitHub App authentication
|
||||
// Return empty array so watch paths are ignored (current behavior)
|
||||
return [];
|
||||
}
|
||||
|
||||
$endpoint = "/repos/{$owner}/{$repo}/pulls/{$pullRequestId}/files";
|
||||
$response = githubApi($source, $endpoint, 'get', null, false);
|
||||
|
||||
if (! $response) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$files = collect(data_get($response, 'data', []));
|
||||
|
||||
return $files->pluck('filename')->filter()->values()->toArray();
|
||||
} catch (Exception $e) {
|
||||
ray('Error fetching GitHub PR files: '.$e->getMessage());
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -1319,7 +1319,7 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
|
||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik(
|
||||
uuid: $uuid,
|
||||
domains: $fqdns,
|
||||
is_force_https_enabled: true,
|
||||
is_force_https_enabled: $originalResource->isForceHttpsEnabled(),
|
||||
serviceLabels: $serviceLabels,
|
||||
is_gzip_enabled: $originalResource->isGzipEnabled(),
|
||||
is_stripprefix_enabled: $originalResource->isStripprefixEnabled(),
|
||||
@@ -1332,7 +1332,7 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
|
||||
network: $network,
|
||||
uuid: $uuid,
|
||||
domains: $fqdns,
|
||||
is_force_https_enabled: true,
|
||||
is_force_https_enabled: $originalResource->isForceHttpsEnabled(),
|
||||
serviceLabels: $serviceLabels,
|
||||
is_gzip_enabled: $originalResource->isGzipEnabled(),
|
||||
is_stripprefix_enabled: $originalResource->isStripprefixEnabled(),
|
||||
@@ -1346,7 +1346,7 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
|
||||
$serviceLabels = $serviceLabels->merge(fqdnLabelsForTraefik(
|
||||
uuid: $uuid,
|
||||
domains: $fqdns,
|
||||
is_force_https_enabled: true,
|
||||
is_force_https_enabled: $originalResource->isForceHttpsEnabled(),
|
||||
serviceLabels: $serviceLabels,
|
||||
is_gzip_enabled: $originalResource->isGzipEnabled(),
|
||||
is_stripprefix_enabled: $originalResource->isStripprefixEnabled(),
|
||||
@@ -1357,7 +1357,7 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
|
||||
network: $network,
|
||||
uuid: $uuid,
|
||||
domains: $fqdns,
|
||||
is_force_https_enabled: true,
|
||||
is_force_https_enabled: $originalResource->isForceHttpsEnabled(),
|
||||
serviceLabels: $serviceLabels,
|
||||
is_gzip_enabled: $originalResource->isGzipEnabled(),
|
||||
is_stripprefix_enabled: $originalResource->isStripprefixEnabled(),
|
||||
|
||||
@@ -175,7 +175,7 @@ function excludeCertainErrors(string $errorOutput, ?int $exitCode = null)
|
||||
throw new \RuntimeException($errorMessage, $exitCode);
|
||||
}
|
||||
|
||||
function decode_remote_command_output(?ApplicationDeploymentQueue $application_deployment_queue = null): Collection
|
||||
function decode_remote_command_output(?ApplicationDeploymentQueue $application_deployment_queue = null, bool $includeAll = false): Collection
|
||||
{
|
||||
if (is_null($application_deployment_queue)) {
|
||||
return collect([]);
|
||||
@@ -216,7 +216,7 @@ function decode_remote_command_output(?ApplicationDeploymentQueue $application_d
|
||||
|
||||
$seenCommands = collect();
|
||||
$formatted = collect($decoded);
|
||||
if (! $is_debug_enabled) {
|
||||
if (! $is_debug_enabled && ! $includeAll) {
|
||||
$formatted = $formatted->filter(fn ($i) => $i['hidden'] === false ?? false);
|
||||
}
|
||||
|
||||
|
||||
@@ -167,6 +167,10 @@ function currentTeam()
|
||||
|
||||
function showBoarding(): bool
|
||||
{
|
||||
if (isDev()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Auth::user()?->isMember()) {
|
||||
return false;
|
||||
}
|
||||
@@ -182,8 +186,11 @@ function refreshSession(?Team $team = null): void
|
||||
$team = User::find(Auth::id())->teams->first();
|
||||
}
|
||||
}
|
||||
// Clear old cache key format for backwards compatibility
|
||||
Cache::forget('team:'.Auth::id());
|
||||
Cache::remember('team:'.Auth::id(), 3600, function () use ($team) {
|
||||
// Use new cache key format that includes team ID
|
||||
Cache::forget('user:'.Auth::id().':team:'.$team->id);
|
||||
Cache::remember('user:'.Auth::id().':team:'.$team->id, 3600, function () use ($team) {
|
||||
return $team;
|
||||
});
|
||||
session(['currentTeam' => $team]);
|
||||
@@ -384,7 +391,7 @@ function base_url(bool $withPort = true): string
|
||||
|
||||
function isSubscribed()
|
||||
{
|
||||
return isSubscriptionActive() || auth()->user()->isInstanceAdmin();
|
||||
return isSubscriptionActive();
|
||||
}
|
||||
|
||||
function isProduction(): bool
|
||||
@@ -815,7 +822,21 @@ function getResourceByUuid(string $uuid, ?int $teamId = null)
|
||||
return null;
|
||||
}
|
||||
$resource = queryResourcesByUuid($uuid);
|
||||
if (! is_null($resource) && $resource->environment->project->team_id === $teamId) {
|
||||
if (is_null($resource)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// ServiceDatabase has a different relationship path: service->environment->project->team_id
|
||||
if ($resource instanceof \App\Models\ServiceDatabase) {
|
||||
if ($resource->service?->environment?->project?->team_id === $teamId) {
|
||||
return $resource;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Standard resources: environment->project->team_id
|
||||
if ($resource->environment->project->team_id === $teamId) {
|
||||
return $resource;
|
||||
}
|
||||
|
||||
@@ -902,6 +923,12 @@ function queryResourcesByUuid(string $uuid)
|
||||
return $clickhouse;
|
||||
}
|
||||
|
||||
// Check for ServiceDatabase by its own UUID
|
||||
$serviceDatabase = ServiceDatabase::whereUuid($uuid)->first();
|
||||
if ($serviceDatabase) {
|
||||
return $serviceDatabase;
|
||||
}
|
||||
|
||||
return $resource;
|
||||
}
|
||||
function generateTagDeployWebhook($tag_name)
|
||||
@@ -1233,6 +1260,9 @@ function generateEnvValue(string $command, Service|Application|null $service = n
|
||||
case 'USER':
|
||||
$generatedValue = Str::random(16);
|
||||
break;
|
||||
case 'LOWERCASEUSER':
|
||||
$generatedValue = Str::lower(Str::random(16));
|
||||
break;
|
||||
case 'SUPABASEANON':
|
||||
$signingKey = $service->environment_variables()->where('key', 'SERVICE_PASSWORD_JWT')->first();
|
||||
if (is_null($signingKey)) {
|
||||
@@ -1431,7 +1461,7 @@ function get_public_ips()
|
||||
$ipv4 = $first->output();
|
||||
if ($ipv4) {
|
||||
$ipv4 = trim($ipv4);
|
||||
$validate_ipv4 = filter_var($ipv4, FILTER_VALIDATE_IP);
|
||||
$validate_ipv4 = filter_var($ipv4, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
|
||||
if ($validate_ipv4 == false) {
|
||||
echo "Invalid ipv4: $ipv4\n";
|
||||
|
||||
@@ -1446,7 +1476,7 @@ function get_public_ips()
|
||||
$ipv6 = $second->output();
|
||||
if ($ipv6) {
|
||||
$ipv6 = trim($ipv6);
|
||||
$validate_ipv6 = filter_var($ipv6, FILTER_VALIDATE_IP);
|
||||
$validate_ipv6 = filter_var($ipv6, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
|
||||
if ($validate_ipv6 == false) {
|
||||
echo "Invalid ipv6: $ipv6\n";
|
||||
|
||||
@@ -3744,3 +3774,81 @@ function extractHardcodedEnvironmentVariables(string $dockerComposeRaw): \Illumi
|
||||
|
||||
return $hardcodedVars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downsample metrics using the Largest-Triangle-Three-Buckets (LTTB) algorithm.
|
||||
* This preserves the visual shape of the data better than simple averaging.
|
||||
*
|
||||
* @param array $data Array of [timestamp, value] pairs
|
||||
* @param int $threshold Target number of points
|
||||
* @return array Downsampled data
|
||||
*/
|
||||
function downsampleLTTB(array $data, int $threshold): array
|
||||
{
|
||||
$dataLength = count($data);
|
||||
|
||||
// Return unchanged if threshold >= data length, or if threshold <= 2
|
||||
// (threshold <= 2 would cause division by zero in bucket calculation)
|
||||
if ($threshold >= $dataLength || $threshold <= 2) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$sampled = [];
|
||||
$sampled[] = $data[0]; // Always keep first point
|
||||
|
||||
$bucketSize = ($dataLength - 2) / ($threshold - 2);
|
||||
|
||||
$a = 0; // Index of previous selected point
|
||||
|
||||
for ($i = 0; $i < $threshold - 2; $i++) {
|
||||
// Calculate bucket range
|
||||
$bucketStart = (int) floor(($i + 1) * $bucketSize) + 1;
|
||||
$bucketEnd = (int) floor(($i + 2) * $bucketSize) + 1;
|
||||
$bucketEnd = min($bucketEnd, $dataLength - 1);
|
||||
|
||||
// Calculate average point for next bucket (used as reference)
|
||||
$nextBucketStart = (int) floor(($i + 2) * $bucketSize) + 1;
|
||||
$nextBucketEnd = (int) floor(($i + 3) * $bucketSize) + 1;
|
||||
$nextBucketEnd = min($nextBucketEnd, $dataLength - 1);
|
||||
|
||||
$avgX = 0;
|
||||
$avgY = 0;
|
||||
$nextBucketCount = $nextBucketEnd - $nextBucketStart + 1;
|
||||
|
||||
if ($nextBucketCount > 0) {
|
||||
for ($j = $nextBucketStart; $j <= $nextBucketEnd; $j++) {
|
||||
$avgX += $data[$j][0];
|
||||
$avgY += $data[$j][1];
|
||||
}
|
||||
$avgX /= $nextBucketCount;
|
||||
$avgY /= $nextBucketCount;
|
||||
}
|
||||
|
||||
// Find point in current bucket with largest triangle area
|
||||
$maxArea = -1;
|
||||
$maxAreaIndex = $bucketStart;
|
||||
|
||||
$pointAX = $data[$a][0];
|
||||
$pointAY = $data[$a][1];
|
||||
|
||||
for ($j = $bucketStart; $j <= $bucketEnd; $j++) {
|
||||
// Triangle area calculation
|
||||
$area = abs(
|
||||
($pointAX - $avgX) * ($data[$j][1] - $pointAY) -
|
||||
($pointAX - $data[$j][0]) * ($avgY - $pointAY)
|
||||
) * 0.5;
|
||||
|
||||
if ($area > $maxArea) {
|
||||
$maxArea = $area;
|
||||
$maxAreaIndex = $j;
|
||||
}
|
||||
}
|
||||
|
||||
$sampled[] = $data[$maxAreaIndex];
|
||||
$a = $maxAreaIndex;
|
||||
}
|
||||
|
||||
$sampled[] = $data[$dataLength - 1]; // Always keep last point
|
||||
|
||||
return $sampled;
|
||||
}
|
||||
@@ -13,6 +13,10 @@ function isSubscriptionActive()
|
||||
if (! $team) {
|
||||
return false;
|
||||
}
|
||||
// Root team (id=0) doesn't require subscription
|
||||
if ($team->id === 0) {
|
||||
return true;
|
||||
}
|
||||
$subscription = $team?->subscription;
|
||||
|
||||
if (is_null($subscription)) {
|
||||
@@ -73,6 +77,7 @@ function allowedPathsForUnsubscribedAccounts()
|
||||
'login',
|
||||
'logout',
|
||||
'force-password-reset',
|
||||
'two-factor-challenge',
|
||||
'livewire/update',
|
||||
'admin',
|
||||
];
|
||||
@@ -91,6 +96,7 @@ function allowedPathsForInvalidAccounts()
|
||||
'logout',
|
||||
'verify',
|
||||
'force-password-reset',
|
||||
'two-factor-challenge',
|
||||
'livewire/update',
|
||||
];
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user