Вход Регистрация
Файл: app/Models/ModuleRegistry.php
Строк: 93
<?php

declare(strict_types=1);

namespace 
AppModels;

use 
CarbonCarbonImmutable;
use 
IlluminateDatabaseEloquentModel;
use 
IlluminateSupportFacadesHttp;

/**
 * @property int                  $id
 * @property string               $url
 * @property string               $name
 * @property bool                 $active
 * @property array                $cached_data
 * @property CarbonImmutable|null $cached_at
 * @property CarbonImmutable      $created_at
 * @property CarbonImmutable      $updated_at
 */
class ModuleRegistry extends Model
{
    protected 
$guarded = [];

    protected function 
casts(): array
    {
        return [
            
'active'      => 'bool',
            
'cached_data' => 'array',
            
'cached_at'   => 'datetime',
        ];
    }

    
/**
     * Fetches the data from the registry.
     */
    
public bool $fetchFailed false;

    public function 
fetch(bool $force false): array
    {
        
$ttl = (int) config('modules.registry_cache_ttl');

        
// Реестр уже опрашивался: отдаём кэш сразу (даже пустой у недоступного реестра),
        // протухший обновляем после отправки ответа. Синхронный путь — только для
        // нового реестра и force
        
if (! $force && $this->cached_at !== null) {
            if (! 
$this->cached_at->gt(now()->subSeconds($ttl))) {
                
dispatch(fn () => $this->fetch(true))->afterResponse();
            }

            return 
$this->cached_data ?? [];
        }

        try {
            
$response Http::timeout(10)->get($this->url);

            if (! 
$response->ok()) {
                return 
$this->markFailed();
            }

            
$data $response->json();

            if (! 
is_array($data)) {
                return 
$this->markFailed();
            }

            
$this->update([
                
'name'        => $data['name'] ?? $this->name,
                
'cached_data' => $data,
                
'cached_at'   => now(),
            ]);

            return 
$data;
        } catch (
Exception) {
            return 
$this->markFailed();
        }
    }

    
/**
     * Помечает реестр недоступным и продлевает кэш, чтобы не опрашивать его каждый запрос.
     */
    
private function markFailed(): array
    {
        
$this->fetchFailed true;
        
$this->update(['cached_at' => now()]);

        return 
$this->cached_data ?? [];
    }

    
/**
     * Returns an array of available modules.
     */
    
public static function getAvailableModules(bool $force false): array
    {
        
$registries self::query()->where('active'true)->get();
        
$modules = [];

        foreach (
$registries as $registry) {
            
$data $registry->fetch($force);
            
$registryLabel $registry->name ?: $registry->url;

            foreach (
$data['modules'] ?? [] as $module) {
                if (! isset(
$module['module'])) {
                    continue;
                }

                
$name $module['module'];

                if (isset(
$modules[$name])) {
                    
$modules[$name]['conflict'][] = $registryLabel;
                    continue;
                }

                
$versions $module['versions'] ?? [];
                
$best self::bestCompatibleVersion($versions);
                
$latest $versions[0] ?? [];
                
$version $best ?? $latest;

                
$extra = ['registry' => $registryLabel'conflict' => []];

                if (
$best && isset($latest['version']) && version_compare($latest['version'], $best['version'], '>')) {
                    
$extra['latest_version'] = $latest['version'];
                    
$extra['latest_requires'] = $latest['requires'] ?? null;
                }

                
$modules[$name] = array_merge(
                    
array_diff_key($module, ['versions' => true]),
                    
$version,
                    
$extra,
                );
            }
        }

        return 
$modules;
    }

    
/**
     * Returns the best compatible version of a module.
     */
    
private static function bestCompatibleVersion(array $versions): ?array
    {
        
$compatible array_filter(
            
$versions,
            static 
fn (array $v) => empty($v['requires']) || version_compare(ROTOR_VERSION$v['requires'], '>='),
        );

        if (empty(
$compatible)) {
            return 
null;
        }

        
usort($compatible, static fn ($a$b) => version_compare($b['version'], $a['version']));

        return 
$compatible[0];
    }
}
Онлайн: 1
Реклама