From 23c413cd6b1ce56cabaedc9c8d4880d47bd399f7 Mon Sep 17 00:00:00 2001 From: ghost Date: Sun, 6 Aug 2023 20:55:49 +0300 Subject: [PATCH] initial commit --- .gitignore | 5 ++ config/app.php.example | 54 ++++++++++++++ crontab/crawler.php | 156 +++++++++++++++++++++++++++++++++++++++ database/yggstate.mwb | Bin 0 -> 7610 bytes library/mysql.php | 164 +++++++++++++++++++++++++++++++++++++++++ library/url.php | 82 +++++++++++++++++++++ library/yggdrasil.php | 55 ++++++++++++++ media/db-prototype.png | Bin 0 -> 39556 bytes public/index.html | 0 9 files changed, 516 insertions(+) create mode 100644 .gitignore create mode 100644 config/app.php.example create mode 100644 crontab/crawler.php create mode 100644 database/yggstate.mwb create mode 100644 library/mysql.php create mode 100644 library/url.php create mode 100644 library/yggdrasil.php create mode 100644 media/db-prototype.png create mode 100644 public/index.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6e3ac62 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.vscode +.ftpignore + +/config/app.php +/database/yggstate.mwb.bak \ No newline at end of file diff --git a/config/app.php.example b/config/app.php.example new file mode 100644 index 0000000..8eca5da --- /dev/null +++ b/config/app.php.example @@ -0,0 +1,54 @@ + disk_free_space('/') / 1000000) { + + exit (PHP_EOL . 'Disk quota reached.' . PHP_EOL); +} + +// Init Debug +$debug = [ + 'time' => [ + 'ISO8601' => date('c'), + 'total' => microtime(true), + ], + 'yggdrasil' => [ + 'peer' => [ + 'total' => [ + 'online' => 0, + 'insert' => 0, + ], + 'remote' => [ + 'total' => [ + 'insert' => 0, + 'update' => 0, + ] + ] + ] + ] +]; + +// Connect database +try { + + $db = new MySQL(DB_HOST, DB_PORT, DB_NAME, DB_USERNAME, DB_PASSWORD); + +} catch(Exception $e) { + + var_dump($e); + + exit; +} + +// Collect connected peers +if ($connectedPeers = Yggdrasil::getPeers()) { + + foreach ($connectedPeers as $connectedPeerAddress => $connectedPeerInfo) { + + try { + + $db->beginTransaction(); + + if ($dbPeer = $db->findPeer($connectedPeerAddress)) { + + $dbPeerId = $dbPeer->peerId; + + } else { + + $dbPeerId = $db->addPeer($connectedPeerAddress, $connectedPeerInfo->key, time()); + + if ($connectedPeerRemoteUrl = URL::parse($connectedPeerInfo->remote)) { + + if ($dbPeerRemote = $db->findPeerRemote($dbPeerId, + $connectedPeerRemoteUrl->host->scheme, + $connectedPeerRemoteUrl->host->name, + $connectedPeerRemoteUrl->host->port)) { + + // Update connection stats + if ($dbPeerRemote->received < $connectedPeerInfo->bytes_recvd) { + + $debug['yggdrasil']['peer']['remote']['total']['update'] += + $db->updatePeerRemoteReceived($dbPeerRemote->dbPeerRemoteId, $connectedPeerInfo->bytes_recvd, time()); + + } + + if ($dbPeerRemote->sent < $connectedPeerInfo->bytes_sent) { + + $debug['yggdrasil']['peer']['remote']['total']['update'] += + $db->updatePeerRemoteSent($dbPeerRemote->dbPeerRemoteId, $connectedPeerInfo->bytes_sent, time()); + } + + if ($dbPeerRemote->uptime < $connectedPeerInfo->uptime) { + + $debug['yggdrasil']['peer']['remote']['total']['update'] += + $db->updatePeerRemoteUptime($dbPeerRemote->dbPeerRemoteId, $connectedPeerInfo->uptime, time()); + } + + } else { + + $peerRemoteId = $db->addPeerRemote($dbPeerId, + $connectedPeerRemoteUrl->host->scheme, + $connectedPeerRemoteUrl->host->name, + $connectedPeerRemoteUrl->host->port, + $connectedPeerInfo->bytes_recvd, + $connectedPeerInfo->bytes_sent, + $connectedPeerInfo->uptime, + time()); + + $debug['yggdrasil']['peer']['remote']['total']['insert']++; + } + } + + $debug['yggdrasil']['peer']['total']['insert']++; + } + + $debug['yggdrasil']['peer']['total']['online']++; + + $db->commit(); + + } catch(Exception $e) { + + $db->rollBack(); + + var_dump($e); + + break; + } + } +} + +// Debug output +$debug['time']['total'] = microtime(true) - $debug['time']['total']; + +print_r( + array_merge($debug, [ + 'db' => [ + 'total' => [ + 'select' => $db->getDebug()->query->select->total, + 'insert' => $db->getDebug()->query->insert->total, + 'update' => $db->getDebug()->query->update->total, + 'delete' => $db->getDebug()->query->delete->total, + ] + ] + ]) +); \ No newline at end of file diff --git a/database/yggstate.mwb b/database/yggstate.mwb new file mode 100644 index 0000000000000000000000000000000000000000..a8e2d67f3d1ec5994cc317f33dd42359fa6d3a67 GIT binary patch literal 7610 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 n3lA%M2Ok?CA2;tSmw!E6o>ulYE*75RKz}_db = new PDO('mysql:dbname=' . $database . ';host=' . $host . ';port=' . $port . ';charset=utf8', $username, $password, [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8']); + $this->_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $this->_db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ); + $this->_db->setAttribute(PDO::ATTR_TIMEOUT, 600); + + $this->_debug = (object) + [ + 'query' => (object) + [ + 'select' => (object) + [ + 'total' => 0 + ], + 'insert' => (object) + [ + 'total' => 0 + ], + 'update' => (object) + [ + 'total' => 0 + ], + 'delete' => (object) + [ + 'total' => 0 + ], + ] + ]; + } + + // Tools + public function beginTransaction() { + + $this->_db->beginTransaction(); + } + + public function commit() { + + $this->_db->commit(); + } + + public function rollBack() { + + $this->_db->rollBack(); + } + + public function getDebug() { + + return $this->_debug; + } + + // Peer + public function addPeer(string $address, string $key, int $timeAdded) { + + $this->_debug->query->insert->total++; + + $query = $this->_db->prepare('INSERT INTO `peer` SET `address` = ?, `key` = ?, `timeAdded` = ?'); + + $query->execute([$address, $key, $timeAdded]); + + return $this->_db->lastInsertId(); + } + + public function findPeer(string $address) { + + $this->_debug->query->select->total++; + + $query = $this->_db->prepare('SELECT * FROM `peer` WHERE `address` = ? LIMIT 1'); + + $query->execute([$address]); + + return $query->fetch(); + } + + public function getPeers() { + + $this->_debug->query->select->total++; + + $query = $this->_db->prepare('SELECT * FROM `peer`'); + + $query->execute(); + + return $query->fetchAll(); + } + + // Peer remote + public function addPeerRemote(int $peerId, string $scheme, string $host, int $port, int $received, int $sent, float $uptime, int $timeAdded) { + + $this->_debug->query->insert->total++; + + $query = $this->_db->prepare('INSERT INTO `peerRemote` SET `peerId` = ?, + `scheme` = ?, + `host` = ?, + `port` = ?, + `received` = ?, + `sent` = ?, + `uptime` = ?, + `timeAdded` = ?'); + + $query->execute([$peerId, $scheme, $host, $port, $received, $sent, $uptime, $timeAdded]); + + return $this->_db->lastInsertId(); + } + + public function updatePeerRemoteReceived(int $peerRemoteId, int $received, int $timeUpdated) { + + $this->_debug->query->update->total++; + + $query = $this->_db->prepare('UPDATE `peerRemote` SET `received` = ?, `timeUpdated` = ? WHERE `peerRemoteId` = ? LIMIT 1'); + + $query->execute([$received, $timeUpdated, $peerRemoteId]); + + return $query->rowCount(); + } + + public function updatePeerRemoteSent(int $peerRemoteId, int $sent, int $timeUpdated) { + + $this->_debug->query->update->total++; + + $query = $this->_db->prepare('UPDATE `peerRemote` SET `sent` = ?, `timeUpdated` = ? WHERE `peerRemoteId` = ? LIMIT 1'); + + $query->execute([$sent, $timeUpdated, $peerRemoteId]); + + return $query->rowCount(); + } + + public function updatePeerRemoteUptime(int $peerRemoteId, float $uptime, int $timeUpdated) { + + $this->_debug->query->update->total++; + + $query = $this->_db->prepare('UPDATE `peerRemote` SET `uptime` = ?, `timeUpdated` = ? WHERE `peerRemoteId` = ? LIMIT 1'); + + $query->execute([$uptime, $timeUpdated, $peerRemoteId]); + + return $query->rowCount(); + } + + public function findPeerRemote(int $peerId, string $scheme, string $host, int $port) { + + $this->_debug->query->select->total++; + + $query = $this->_db->prepare('SELECT * FROM `peerRemote` WHERE `peerId` = ? AND `scheme` = ? AND `host` = ? AND `port` = ? LIMIT 1'); + + $query->execute([$peerId, $scheme, $host, $port]); + + return $query->fetch(); + } + + // Other + public function optimize() { + + $this->_db->query('OPTIMIZE TABLE `peer`'); + $this->_db->query('OPTIMIZE TABLE `peerRemote`'); + } +} diff --git a/library/url.php b/library/url.php new file mode 100644 index 0000000..bada461 --- /dev/null +++ b/library/url.php @@ -0,0 +1,82 @@ + (object) + [ + 'url' => null, + 'scheme' => null, + 'name' => null, + 'port' => null, + ], + 'page' => (object) + [ + 'url' => null, + 'uri' => null, + 'path' => null, + 'query' => null, + ] + ]; + + // Validate URL + if (!self::is($url)) { + + return false; + } + + // Parse host + if ($scheme = parse_url($url, PHP_URL_SCHEME)) { + + $result->host->url = $scheme . '://'; + $result->host->scheme = $scheme; + + } else { + + return false; + } + + if ($host = parse_url($url, PHP_URL_HOST)) { + + $result->host->url .= $host; + $result->host->name = $host; + + } else { + + return false; + } + + if ($port = parse_url($url, PHP_URL_PORT)) { + + $result->host->url .= ':' . $port; + $result->host->port = $port; + + // port is optional + } + + // Parse page + if ($path = parse_url($url, PHP_URL_PATH)) { + + $result->page->uri = $path; + $result->page->path = $path; + } + + if ($query = parse_url($url, PHP_URL_QUERY)) { + + $result->page->uri .= '?' . $query; + $result->page->query = '?' . $query; + } + + $result->page->url = $result->host->url . $result->page->uri; + + return $result; + } +} \ No newline at end of file diff --git a/library/yggdrasil.php b/library/yggdrasil.php new file mode 100644 index 0000000..dd90f92 --- /dev/null +++ b/library/yggdrasil.php @@ -0,0 +1,55 @@ +peers)) { + + return false; + } + + foreach ((object) $result->peers as $peer) { + + switch (false) { + + case isset($peer->bytes_recvd): + case isset($peer->bytes_sent): + case isset($peer->remote): + case isset($peer->port): + case isset($peer->key): + case isset($peer->uptime): + case !empty($peer->coords): + + return false; + } + } + + return $result->peers; + } +} \ No newline at end of file diff --git a/media/db-prototype.png b/media/db-prototype.png new file mode 100644 index 0000000000000000000000000000000000000000..0fce0766aff3a0f7e796027d8d9686000aba3822 GIT binary patch literal 39556 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 z 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