Files
iplist/src/Domain/Entity/Site.php
2024-11-02 05:23:56 +03:00

214 lines
7.3 KiB
PHP

<?php
namespace OpenCCK\Domain\Entity;
use OpenCCK\Domain\Factory\SiteFactory;
use OpenCCK\Domain\Helper\DNSHelper;
use OpenCCK\Domain\Helper\IP4Helper;
use OpenCCK\Domain\Helper\IP6Helper;
use OpenCCK\Infrastructure\API\App;
use Revolt\EventLoop;
use stdClass;
use function Amp\async;
use function Amp\Future\await;
use function OpenCCK\getEnv;
final class Site {
private DNSHelper $dnsHelper;
private bool $isUseIpv6;
private bool $isUseIpv4;
/**
* @param string $name Name of portal
* @param string $group Group of portal
* @param array $domains List of portal domains
* @param array $dns List of DNS servers for updating IP addresses
* @param int $timeout Time interval between domain IP address updates (seconds)
* @param array $ip4 List of IPv4 addresses
* @param array $ip6 List of IPv6 addresses
* @param array $cidr4 List of CIDRv4 zones of IPv4 addresses
* @param array $cidr6 List of CIDRv6 zones of IPv6 addresses
* @param object $external Lists of URLs to retrieve data from external sources
*
*/
public function __construct(
public string $name,
public string $group,
public array $domains = [],
public array $dns = [],
public int $timeout = 1440 * 60,
public array $ip4 = [],
public array $ip6 = [],
public array $cidr4 = [],
public array $cidr6 = [],
public object $external = new stdClass()
) {
$this->dnsHelper = new DNSHelper($dns);
$this->isUseIpv4 = (getEnv('SYS_DNS_RESOLVE_IP4') ?? 'true') == 'true';
$this->isUseIpv6 = (getEnv('SYS_DNS_RESOLVE_IP6') ?? 'true') == 'true';
}
/**
* @return void
*/
public function preload(): void {
$startTime = time();
App::getLogger()->notice('Preloading for ' . $this->name, ['started']);
if ($this->timeout) {
if ($this->isUseIpv4) {
$this->cidr4 = SiteFactory::normalize(
IP4Helper::processCIDR($this->ip4, SiteFactory::normalize($this->cidr4)),
true
);
}
if ($this->isUseIpv6) {
$this->cidr6 = SiteFactory::normalize(
IP6Helper::processCIDR($this->ip6, SiteFactory::normalize($this->cidr6)),
true
);
}
}
App::getLogger()->notice('Preloaded for ' . $this->name, ['finished', time() - $startTime]);
}
/**
* @return void
*/
public function reload(): void {
$startTime = time();
App::getLogger()->notice('Reloading for ' . $this->name, ['started']);
$ip4 = [];
$ip6 = [];
foreach (array_chunk($this->domains, \OpenCCK\getEnv('SYS_DNS_RESOLVE_CHUNK_SIZE') ?? 10) as $chunk) {
$executions = array_map(fn(string $domain) => async(fn() => $this->dnsHelper->resolve($domain)), $chunk);
foreach (await($executions) as $result) {
$ip4 = array_merge($ip4, $result[0]);
$ip6 = array_merge($ip6, $result[1]);
}
}
if ($this->isUseIpv4) {
$newIp4 = SiteFactory::normalize(array_diff($ip4, $this->ip4), true);
$this->cidr4 = SiteFactory::normalize(IP4Helper::processCIDR($newIp4, $this->cidr4), true);
$this->ip4 = SiteFactory::normalize(array_merge($this->ip4, $ip4), true);
}
if ($this->isUseIpv6) {
$newIp6 = SiteFactory::normalize(array_diff($ip6, $this->ip6), true);
$this->cidr6 = SiteFactory::normalize(IP6Helper::processCIDR($newIp6, $this->cidr6), true);
$this->ip6 = SiteFactory::normalize(array_merge($this->ip6, $ip6), true);
}
$this->saveConfig();
App::getLogger()->notice('Reloaded for ' . $this->name, ['finished', time() - $startTime]);
EventLoop::delay($this->timeout, function () {
$this->reloadExternal();
$this->reload();
});
}
/**
* @return void
*/
private function reloadExternal(): void {
if (isset($this->external->domains)) {
foreach ($this->external->domains as $url) {
$this->domains = SiteFactory::normalize(
array_merge($this->domains, explode("\n", file_get_contents($url)))
);
}
}
if (isset($this->external->ip4) && $this->isUseIpv4) {
foreach ($this->external->ip4 as $url) {
$this->ip4 = SiteFactory::normalize(
array_merge($this->ip4, explode("\n", file_get_contents($url))),
true
);
}
}
if (isset($this->external->ip6) && $this->isUseIpv6) {
foreach ($this->external->ip6 as $url) {
$this->ip6 = SiteFactory::normalize(
array_merge($this->ip6, explode("\n", file_get_contents($url))),
true
);
}
}
if (isset($this->external->cidr4) && $this->isUseIpv4) {
foreach ($this->external->cidr4 as $url) {
$this->cidr4 = IP4Helper::minimizeSubnets(
SiteFactory::normalize(array_merge($this->cidr4, explode("\n", file_get_contents($url))), true)
);
}
}
if (isset($this->external->cidr6) && $this->isUseIpv6) {
foreach ($this->external->cidr6 as $url) {
// todo IP6Helper::minimizeSubnets
$this->cidr6 = SiteFactory::normalize(
array_merge($this->cidr6, explode("\n", file_get_contents($url))),
true
);
}
}
App::getLogger()->debug('External reloaded for ' . $this->name);
}
/**
* @return object
*/
public function getConfig(): object {
return (object) [
'domains' => SiteFactory::normalizeArray($this->domains),
'dns' => $this->dns,
'timeout' => $this->timeout,
'ip4' => SiteFactory::normalizeArray($this->ip4, true),
'ip6' => SiteFactory::normalizeArray($this->ip6, true),
'cidr4' => SiteFactory::normalizeArray($this->cidr4, true),
'cidr6' => SiteFactory::normalizeArray($this->cidr6, true),
'external' => $this->external,
];
}
/**
* @return void
*/
private function saveConfig(): void {
file_put_contents(
PATH_ROOT . '/config/' . $this->group . '/' . $this->name . '.json',
json_encode($this->getConfig(), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
);
}
/**
* @param bool $wildcard
* @return array
*/
public function getDomains(bool $wildcard = false): array {
if ($wildcard) {
$domains = [];
foreach ($this->domains as $domain) {
$parts = explode('.', $domain);
$wildcardDomain = array_slice($parts, -2);
if (in_array(implode('.', $wildcardDomain), SiteFactory::TWO_LEVEL_DOMAIN_ZONES)) {
$wildcardDomain = array_slice($parts, -3);
}
$domains[] = implode('.', $wildcardDomain);
}
return SiteFactory::normalizeArray($domains);
}
return $this->domains;
}
}