From adad8cea817ba671152f8dd6ec7377d1698a3add Mon Sep 17 00:00:00 2001 From: Aldarien Date: Fri, 16 Jun 2023 21:44:35 -0400 Subject: [PATCH] v0.5.0 --- Dockerfile | 6 +++ app/bin/console | 3 ++ app/common/Command/UpdateIp.php | 36 ++++++++++++++++++ app/common/Command/Watch.php | 40 ++++++++++++++++++++ app/common/Service/Ipify.php | 27 ++++++++++++++ app/common/Service/Remote.php | 13 +++++++ app/common/Service/Repository.php | 41 ++++++++++++++++++++ app/common/Wrapper/App.php | 19 ++++++++++ app/composer.json | 22 +++++++++++ app/crontab | 1 + app/public/index.php | 7 ++++ app/setup/app.php | 40 ++++++++++++++++++++ app/setup/composer.php | 6 +++ app/setup/middlewares/commands.php | 3 ++ app/setup/settings/01_env.php | 19 ++++++++++ app/setup/setups/commands.php | 8 ++++ app/setup/setups/services.php | 60 ++++++++++++++++++++++++++++++ docker-compose.yml | 9 +++++ 18 files changed, 360 insertions(+) create mode 100644 Dockerfile create mode 100755 app/bin/console create mode 100644 app/common/Command/UpdateIp.php create mode 100644 app/common/Command/Watch.php create mode 100644 app/common/Service/Ipify.php create mode 100644 app/common/Service/Remote.php create mode 100644 app/common/Service/Repository.php create mode 100644 app/common/Wrapper/App.php create mode 100644 app/composer.json create mode 100644 app/crontab create mode 100644 app/public/index.php create mode 100644 app/setup/app.php create mode 100644 app/setup/composer.php create mode 100644 app/setup/middlewares/commands.php create mode 100644 app/setup/settings/01_env.php create mode 100644 app/setup/setups/commands.php create mode 100644 app/setup/setups/services.php create mode 100644 docker-compose.yml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..861c6c2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM php:cli + +RUN apt-get update && apt-get install -yq --no-install-recommends cron && rm -r /var/lib/apt/lists/* \ + && docker-php-ext-install pdo_mysql + +CMD [ "cron", "-f", "-L", "15" ] diff --git a/app/bin/console b/app/bin/console new file mode 100755 index 0000000..b7e44fd --- /dev/null +++ b/app/bin/console @@ -0,0 +1,3 @@ +#!/bin/bash + +php /app/public/index.php "$@" diff --git a/app/common/Command/UpdateIp.php b/app/common/Command/UpdateIp.php new file mode 100644 index 0000000..4a98f8c --- /dev/null +++ b/app/common/Command/UpdateIp.php @@ -0,0 +1,36 @@ +service->update(); + return Command::SUCCESS; + } catch (PDOException $e) { + $this->logger->warning($e); + return Command::FAILURE; + } catch (Exception $e) { + $this->logger->error($e); + return Command::FAILURE; + } + } +} diff --git a/app/common/Command/Watch.php b/app/common/Command/Watch.php new file mode 100644 index 0000000..28f7c1f --- /dev/null +++ b/app/common/Command/Watch.php @@ -0,0 +1,40 @@ +period); + $current = new DateTimeImmutable(); + while(true) { + $now = new DateTimeImmutable(); + if ($now->diff($current) === $period) { + $this->runUpdate(); + $current = $now; + } + } + return Command::SUCCESS; + } + protected function runUpdate(): void + { + $command = '/app/bin/console update'; + shell_exec($command); + } +} diff --git a/app/common/Service/Ipify.php b/app/common/Service/Ipify.php new file mode 100644 index 0000000..fc415a6 --- /dev/null +++ b/app/common/Service/Ipify.php @@ -0,0 +1,27 @@ +logger->debug('Getting IP'); + $response = $this->client->get('?format=json'); + if (round($response->getCode() / 100, 0) !== 2) { + throw new Exception("Could not connect to '{$this->client->base_uri}'"); + } + $body = $response->getBody(); + $json = json_decode($body->getContents()); + if (!isset($json->ip)) { + throw new Exception('Missing `ip` in JSON response'); + } + return $json->ip; + } +} diff --git a/app/common/Service/Remote.php b/app/common/Service/Remote.php new file mode 100644 index 0000000..5a1131f --- /dev/null +++ b/app/common/Service/Remote.php @@ -0,0 +1,13 @@ +ipService->get(); + $this->dbService->update($ip); + } +} diff --git a/app/common/Service/Repository.php b/app/common/Service/Repository.php new file mode 100644 index 0000000..f358748 --- /dev/null +++ b/app/common/Service/Repository.php @@ -0,0 +1,41 @@ +logger->debug('Updating Database'); + + $old_ip = $this->getOld(); + $this->logger->debug($old_ip); + + if ($old_ip === $ip) { + $this->logger->debug('No change in IP'); + return; + } + + $this->doUpdate(); + $this->logger->debug('Updated IP'); + } + + protected function getOld(): string + { + $query = "SELECT `ip` FROM `{$this->table}` WHERE `host` = ?"; + $statement = $this->connection->prepare($query); + $statement->execute(['vialdelamaza']); + + return $statement->fetch()['ip']; + } + protected function doUpdate(string $ip): void + { + $query = "UPDATE `remote_ip` SET `ip` = ?, `updated` = CURRENT_TIMESTAMP() WHERE `host` = ?"; + $statement = $this->connection->prepare($query); + $statement->execute([$ip, 'vialdelamaza']); + } +} diff --git a/app/common/Wrapper/App.php b/app/common/Wrapper/App.php new file mode 100644 index 0000000..3c5107a --- /dev/null +++ b/app/common/Wrapper/App.php @@ -0,0 +1,19 @@ +container; + } + public function setContainer(ContainerInterface $container): App + { + $this->container = $container; + return $this; + } +} diff --git a/app/composer.json b/app/composer.json new file mode 100644 index 0000000..78aa08d --- /dev/null +++ b/app/composer.json @@ -0,0 +1,22 @@ +{ + "name": "provm/remote_ip", + "type": "project", + "require": { + "guzzlehttp/guzzle": "^7.7", + "monolog/monolog": "^3.3", + "php-di/php-di": "^7.0", + "symfony/console": "^6.3", + "thecodingmachine/safe": "^2.5" + }, + "require-dev": { + "phpunit/phpunit": "^10.2" + }, + "autoload": { + "psr-4": { + "ProVM\\": "common/" + } + }, + "config": { + "sort-packages": true + } +} diff --git a/app/crontab b/app/crontab new file mode 100644 index 0000000..388f26f --- /dev/null +++ b/app/crontab @@ -0,0 +1 @@ +*/20 * * * 1-5 /app/bin/console diff --git a/app/public/index.php b/app/public/index.php new file mode 100644 index 0000000..0606ece --- /dev/null +++ b/app/public/index.php @@ -0,0 +1,7 @@ +run(); diff --git a/app/setup/app.php b/app/setup/app.php new file mode 100644 index 0000000..b5df709 --- /dev/null +++ b/app/setup/app.php @@ -0,0 +1,40 @@ +isDir()) { + continue; + } + $builder->addDefinitions($file->getRealPath()); + } + } + $app = new App(); + $app->setContainer($builder->build()); + $folder = implode(DIRECTORY_SEPARATOR, [__DIR__, 'middlewares']); + if (file_exists($folder)) { + $files = new FilesystemIterator($folder); + foreach($files as $file) { + if ($file->isDir()) { + continue; + } + require_once $file->getRealPath(); + } + } + return $app; +} +return buildApp(); diff --git a/app/setup/composer.php b/app/setup/composer.php new file mode 100644 index 0000000..b451f96 --- /dev/null +++ b/app/setup/composer.php @@ -0,0 +1,6 @@ +add($app->getContainer()->get(ProVM\Command\Watch::class)); +$app->add($app->getContainer()->get(ProVM\Command\UpdateIp::class)); diff --git a/app/setup/settings/01_env.php b/app/setup/settings/01_env.php new file mode 100644 index 0000000..f991737 --- /dev/null +++ b/app/setup/settings/01_env.php @@ -0,0 +1,19 @@ + function() { + return new DI\Container([ + 'host' => $_ENV['MYSQL_HOST'], + 'name' => $_ENV['MYSQL_DATABASE'], + 'user' => function() { + return new DI\Container([ + 'name' => $_ENV['MYSQL_USER'], + 'password' => $_ENV['MYSQL_PASSWORD'] + ]); + }, + 'table' => 'remote_ip', + ]); + }, + 'uri' => 'https://api64.ipify.org', + 'period' => 'PT20M', + 'retries' => 5 +]; diff --git a/app/setup/setups/commands.php b/app/setup/setups/commands.php new file mode 100644 index 0000000..d4ee224 --- /dev/null +++ b/app/setup/setups/commands.php @@ -0,0 +1,8 @@ + function(ContainerInterface $container) { + return new ProVM\Command\Watch($container->get('period')); + } +]; diff --git a/app/setup/setups/services.php b/app/setup/setups/services.php new file mode 100644 index 0000000..9161c39 --- /dev/null +++ b/app/setup/setups/services.php @@ -0,0 +1,60 @@ + function(ContainerInterface $container) { + return new Monolog\Logger('file', [ + new Monolog\Handler\FilterHandler( + new Monolog\Handler\RotatingFileHandler('/var/log/remote.debug.log'), + Monolog\Level::Debug, + Monolog\Level::Warning + ), + new Monolog\Handler\FilterHandler( + new Monolog\Handler\RotatingFileHandler('/var/log/remote.error.log'), + Monolog\Level::Error + ) + ], [ + new Monolog\Processor\PsrLogMessageProcessor(), + new Monolog\Processor\IntrospectionProcessor(), + new Monolog\Processor\MemoryUsageProcessor(), + new Monolog\Processor\MemoryPeakUsageProcessor(), + ]); + }, + Psr\Http\Client\ClientInterface::class => function(ContainerInterface $container) { + return new GuzzleHttp\Client([ + 'base_uri' => $container->get('uri') + ]); + }, + ProVM\Service\Ipify::class => function(ContainerInterface $container) { + return new ProVM\Service\Ipify( + $container->get(Psr\Http\Client\ClientInterface::class), + $container->get(Psr\Log\LoggerInterface::class) + ); + }, + PDO::class => function(ContainerInterface $container) { + $database = $container->get('database'); + $retries = $container->get('retries'); + $r = 0; + $exception = null; + while($r < $retries) { + try { + $dsn = "mysql:host={$database->get('host')};dbname={$database->get('name')}"; + return new PDO($dsn, $database->get('user')->get('name'), $database->get('user')->get('password')); + } catch (PDOException $e) { + if ($exception !== null) { + $e = new PDOException($e->getMessage(), $e->getCode(), $exception); + } + $exception = $e; + $container->get(Psr\Log\LoggerInterface::class)->debug('Retrying Connection'); + } + } + throw $exception; + }, + ProVM\Service\Repository::class => function(ContainerInterface $container) { + return new ProVM\Service\Repository( + $container->get(PDO::class), + $container->get('database')->get('table'), + $container->get(Psr\Log\LoggerInterface::class) + ); + } +]; diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e43fdf3 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,9 @@ +services: + remote_ip: + container_name: remote_ip + build: . + restart: unless-stopped + env_file: .env + volumes: + - ./app:/app + - ./app/crontab:/var/spool/cron/crontabs/root