dd/app/Services/Freenode/Pipeline.php

49 lines
2.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Services\Freenode;
use App\Models\CrawlFreenode;
use App\Models\SiteMap;
use RuntimeException;
final class Pipeline
{
public static function enabled(): bool { return config('freenode.engine', 'legacy') === 'v2'; }
public static function store(): ReleaseStore
{
return new ReleaseStore(storage_path('app/freenode-v2'), public_path('freenode/merge'));
}
public static function prepare(): array
{
$sources = [];
$cycle = strtotime(date('Y-m-d').' '.sprintf('%02d', intdiv((int) date('H'), 6) * 6).':00:00');
foreach (CrawlFreenode::where('status', 1)->get() as $source) {
if (!preg_match('/^[a-zA-Z0-9_-]+$/D', $source->name)) throw new RuntimeException('invalid_source_name');
$row = ['name' => $source->name];
foreach (json_decode($source->file_real ?? '[]', true, 512, JSON_THROW_ON_ERROR) as $format => $file) {
if (!in_array($format, ['v2ray', 'clash'], true)) continue;
if (!preg_match('/^'.date('Ymd').'-(v2ray\.txt|clash\.yaml)$/D', $file)) throw new RuntimeException('stale_source_date:'.$source->name);
$path = public_path('freenode/from/'.$source->name.'/'.$file);
if (!is_file($path) || filemtime($path) < $cycle) throw new RuntimeException('stale_source_cycle:'.$source->name);
$row[$format] = file_get_contents($path);
}
if (!isset($row['v2ray'], $row['clash'])) throw new RuntimeException('incomplete_source:'.$source->name);
$sources[] = $row;
}
$sites = [];
foreach (SiteMap::whereIn('group_code', ['fn_a', 'fn_b'])->where('status', 1)->get() as $site) {
$config = json_decode($site->config ?? '[]', true, 512, JSON_THROW_ON_ERROR);
$sites[] = ['code' => $site->code, 'clients' => $config['freenode_client'] ?? []];
}
if (!$sources || !$sites) throw new RuntimeException('no_enabled_sources_or_sites');
$sites[] = ['code' => '_admin', 'clients' => ['v2ray', 'clash', 'singbox']];
$clashTemplate = \Symfony\Component\Yaml\Yaml::parseFile(public_path('freenode/template/clash.txt'));
$validator = new NativeValidator(config('freenode.singbox_binary'), config('freenode.mihomo_binary'), $clashTemplate);
$result = (new Builder())->build($sources, $sites, $validator);
return self::store()->stage($result, date('Y-m-d'), (float) config('freenode.minimum_ratio', 0.5));
}
}