From a208c172dfa886bccf39c137f959392cd4b98699 Mon Sep 17 00:00:00 2001 From: "Buster \"Silver Eagle\" Neece" Date: Wed, 8 Jan 2020 14:37:56 -0600 Subject: [PATCH] Switch to "provide license, download automatically" for GeoLite DB. --- azuracast.dev.env | 1 + config/forms/install_geolite.php | 35 +++---- config/services.php | 2 + .../Admin/InstallGeoLiteController.php | 73 ++------------- src/Entity/Fixture/Settings.php | 3 +- src/Entity/Settings.php | 3 + src/Form/GeoLiteSettingsForm.php | 59 ++++++++++++ src/Service/GeoLite.php | 21 +++-- src/Sync/Task/UpdateGeoLiteDatabase.php | 93 +++++++++++++++++++ 9 files changed, 197 insertions(+), 93 deletions(-) create mode 100644 src/Form/GeoLiteSettingsForm.php create mode 100644 src/Sync/Task/UpdateGeoLiteDatabase.php diff --git a/azuracast.dev.env b/azuracast.dev.env index cee3db54a..786da127f 100644 --- a/azuracast.dev.env +++ b/azuracast.dev.env @@ -15,6 +15,7 @@ INIT_ADMIN_EMAIL= INIT_ADMIN_PASSWORD= INIT_ADMIN_API_KEY= INIT_MUSIC_PATH=/var/azuracast/www/util/fixtures/init_music +INIT_GEOLITE_LICENSE_KEY= # # Database Configuration diff --git a/config/forms/install_geolite.php b/config/forms/install_geolite.php index 855cd3a28..b17a8beef 100644 --- a/config/forms/install_geolite.php +++ b/config/forms/install_geolite.php @@ -1,8 +1,6 @@ 'post', - 'enctype' => 'multipart/form-data', - 'groups' => [ [ 'use_grid' => true, @@ -16,14 +14,11 @@ return [ '' . - '

' . __('You can repeat this process any time you need to update the GeoLite database.') . '

', - 'form_group_class' => 'col-sm-12', + '
  • ' . __('Visit the "My License Key" page under the "Services" section.') . '
  • ' . + '
  • ' . __('Click "Generate new license key".') . '
  • ' . + '
  • ' . __('Paste the generated license key into the field on this page.') . '
  • ' + . '', + 'form_group_class' => 'col-md-6', ], ], @@ -32,20 +27,16 @@ return [ [ 'label' => __('Current Installed Version'), 'markup' => '

    ' . __('GeoLite is not currently installed on this installation.') . '

    ', - 'form_group_class' => 'col-sm-12', + 'form_group_class' => 'col-md-6', ], ], - 'binary' => [ - 'file', + \App\Entity\Settings::GEOLITE_LICENSE_KEY => [ + 'text', [ - 'label' => __('Select GeoLite2-City .tar.gz File'), - 'required' => true, - 'type' => 'archive', - 'max_size' => 50 * 1024 * 1024, + 'label' => __('MaxMind License Key'), + 'default' => '', 'form_group_class' => 'col-md-6', - 'button_text' => __('Select File'), - 'button_icon' => 'cloud_upload', ], ], @@ -53,9 +44,9 @@ return [ 'submit', [ 'type' => 'submit', - 'label' => __('Upload'), - 'class' => 'ui-button btn-lg btn-primary', - 'form_group_class' => 'col-sm-12', + 'label' => __('Save Changes'), + 'class' => 'btn btn-lg btn-primary', + 'form_group_class' => 'col-md-12', ], ], ], diff --git a/config/services.php b/config/services.php index be72fbb47..5372aa090 100644 --- a/config/services.php +++ b/config/services.php @@ -276,6 +276,7 @@ return [ $di->get(App\Sync\Task\RadioAutomation::class), $di->get(App\Sync\Task\HistoryCleanup::class), $di->get(App\Sync\Task\RotateLogs::class), + $di->get(App\Sync\Task\UpdateGeoLiteDatabase::class), ] ); }, @@ -291,6 +292,7 @@ return [ App\Sync\Task\RadioRequests::class => DI\autowire(), App\Sync\Task\RelayCleanup::class => DI\autowire(), App\Sync\Task\RotateLogs::class => DI\autowire(), + App\Sync\Task\UpdateGeoLiteDatabase::class => DI\autowire(), /** * Web Hooks diff --git a/src/Controller/Admin/InstallGeoLiteController.php b/src/Controller/Admin/InstallGeoLiteController.php index f51e3d9d2..0fd225dd9 100644 --- a/src/Controller/Admin/InstallGeoLiteController.php +++ b/src/Controller/Admin/InstallGeoLiteController.php @@ -1,75 +1,22 @@ form_config = $config->get('forms/install_geolite'); - $this->geoLite = $geoLite; - } - - public function __invoke(ServerRequest $request, Response $response): ResponseInterface - { - $form_config = $this->form_config; - - $version = $this->geoLite->getVersion(); - if (null !== $version) { - $form_config['groups'][0]['elements']['current_version'][1]['markup'] = '

    ' . __('GeoLite version "%s" is currently installed.', - $version) . '

    '; - } - - $form = new Form($form_config, []); - - if ($request->isPost() && $form->isValid($request->getParsedBody())) { - try { - $baseDir = dirname($this->geoLite->getDatabasePath()); - - $files = $request->getUploadedFiles(); - /** @var UploadedFileInterface $import_file */ - $import_file = $files['binary']; - - if (UPLOAD_ERR_OK === $import_file->getError()) { - $tgzPath = $baseDir . '/maxmind.tar.gz'; - if (file_exists($tgzPath)) { - unlink($tgzPath); - } - - $import_file->moveTo($tgzPath); - - $process = new Process([ - 'tar', - 'xvzf', - $tgzPath, - '--strip-components=1', - ], $baseDir); - - $process->mustRun(); - - unlink($tgzPath); - } - - return $response->withRedirect($request->getUri()->getPath()); - } catch (Exception $e) { - $form - ->getField('binary') - ->addError(get_class($e) . ': ' . $e->getMessage()); - } + public function __invoke( + ServerRequest $request, + Response $response, + GeoLiteSettingsForm $form + ): ResponseInterface { + if (false !== $form->process($request)) { + $request->getFlash()->addMessage(__('Changes saved.'), Flash::SUCCESS); + return $response->withRedirect($request->getUri()->getPath()); } return $request->getView()->renderToResponse($response, 'system/form_page', [ diff --git a/src/Entity/Fixture/Settings.php b/src/Entity/Fixture/Settings.php index 1331cfeb2..a599d3e5a 100644 --- a/src/Entity/Fixture/Settings.php +++ b/src/Entity/Fixture/Settings.php @@ -12,6 +12,7 @@ class Settings extends AbstractFixture $settings = [ Entity\Settings::BASE_URL => getenv('INIT_BASE_URL') ?? 'docker.local', Entity\Settings::INSTANCE_NAME => getenv('INIT_INSTANCE_NAME') ?? 'local test', + Entity\Settings::GEOLITE_LICENSE_KEY => getenv('INIT_GEOLITE_LICENSE_KEY') ?? '', Entity\Settings::PREFER_BROWSER_URL => 1, Entity\Settings::SETUP_COMPLETE => time(), Entity\Settings::USE_RADIO_PROXY => 1, @@ -19,7 +20,7 @@ class Settings extends AbstractFixture Entity\Settings::CENTRAL_UPDATES => Entity\Settings::UPDATES_NONE, Entity\Settings::EXTERNAL_IP => '127.0.0.1', ]; - + foreach ($settings as $setting_key => $setting_value) { $record = new Entity\Settings($setting_key); $record->setSettingValue($setting_value); diff --git a/src/Entity/Settings.php b/src/Entity/Settings.php index 30aafdcc8..f188fe1f8 100644 --- a/src/Entity/Settings.php +++ b/src/Entity/Settings.php @@ -66,6 +66,9 @@ class Settings public const BACKUP_LAST_RESULT = 'backup_last_result'; public const BACKUP_LAST_OUTPUT = 'backup_last_output'; + public const GEOLITE_LICENSE_KEY = 'geolite_license_key'; + public const GEOLITE_LAST_RUN = 'geolite_last_run'; + /** * @ORM\Column(name="setting_key", type="string", length=64) * @ORM\Id diff --git a/src/Form/GeoLiteSettingsForm.php b/src/Form/GeoLiteSettingsForm.php new file mode 100644 index 000000000..e9d0981af --- /dev/null +++ b/src/Form/GeoLiteSettingsForm.php @@ -0,0 +1,59 @@ +get('forms/install_geolite'); + + parent::__construct( + $em, + $settingsRepo, + $settings, + $formConfig + ); + + $this->geoLite = $geoLite; + $this->syncTask = $syncTask; + } + + public function process(ServerRequest $request): bool + { + $version = $this->geoLite->getVersion(); + if (null !== $version) { + /** @var Markup $currentVersionField */ + $currentVersionField = $this->getField('current_version'); + $currentVersionField->setAttribute( + 'markup', + '

    ' . __('GeoLite version "%s" is currently installed.', $version) . '

    ' + ); + } + + $processed = parent::process($request); + if ($processed) { + $this->syncTask->run(true); + } + + return $processed; + } +} diff --git a/src/Service/GeoLite.php b/src/Service/GeoLite.php index a59927e46..eb2a76697 100644 --- a/src/Service/GeoLite.php +++ b/src/Service/GeoLite.php @@ -14,11 +14,18 @@ class GeoLite public function __construct(Settings $settings) { $this->settings = $settings; + } - $mmdbPath = $this->getDatabasePath(); - if (file_exists($mmdbPath)) { - $this->reader = new Reader($mmdbPath); + protected function getReader(): ?Reader + { + if (null === $this->reader) { + $mmdbPath = $this->getDatabasePath(); + if (file_exists($mmdbPath)) { + $this->reader = new Reader($mmdbPath); + } } + + return $this->reader; } public function getDatabasePath(): string @@ -28,11 +35,11 @@ class GeoLite public function getVersion(): ?string { - if (null === $this->reader) { + if (null === ($reader = $this->getReader())) { return null; } - $metadata = $this->reader->metadata(); + $metadata = $reader->metadata(); $buildDate = Chronos::createFromTimestampUTC($metadata->buildEpoch); return $metadata->databaseType . ' (' . $buildDate->format('Y-m-d') . ')'; @@ -40,7 +47,7 @@ class GeoLite public function getLocationInfo(string $ip, string $locale): array { - if (null === $this->reader) { + if (null === ($reader = $this->getReader())) { return [ 'status' => 'error', 'message' => 'GeoLite database not configured for this installation. See System Administration for instructions.', @@ -48,7 +55,7 @@ class GeoLite } try { - $ipInfo = $this->reader->get($ip); + $ipInfo = $reader->get($ip); } catch (\Exception $e) { return [ 'status' => 'error', diff --git a/src/Sync/Task/UpdateGeoLiteDatabase.php b/src/Sync/Task/UpdateGeoLiteDatabase.php new file mode 100644 index 000000000..6b6b9f881 --- /dev/null +++ b/src/Sync/Task/UpdateGeoLiteDatabase.php @@ -0,0 +1,93 @@ +httpClient = $httpClient; + $this->geoLite = $geoLite; + } + + public function run($force = false): void + { + $logger = Logger::getInstance(); + + if (!$force) { + $lastRun = (int)$this->settingsRepo->getSetting(Entity\Settings::GEOLITE_LAST_RUN, 0); + + if ($lastRun > (time() - self::UPDATE_THRESHOLD)) { + $logger->debug('Not checking for updates; checked too recently.'); + return; + } + } + + $licenseKey = trim($this->settingsRepo->getSetting(Entity\Settings::GEOLITE_LICENSE_KEY)); + + if (!empty($licenseKey)) { + $baseDir = dirname($this->geoLite->getDatabasePath()); + $downloadPath = $baseDir . '/geolite.tar.gz'; + + try { + set_time_limit(900); + + $this->httpClient->get('https://download.maxmind.com/app/geoip_download', [ + 'query' => [ + 'license_key' => $licenseKey, + 'edition_id' => 'GeoLite2-City', + 'suffix' => 'tar.gz', + ], + 'decode_content' => false, + 'sink' => $downloadPath, + 'timeout' => 600, + ]); + } catch (ClientException $e) { + $logger->error('Error downloading GeoLite database: ' . $e->getMessage()); + } + + if (file_exists($downloadPath)) { + $process = new Process([ + 'tar', + 'xvzf', + $downloadPath, + '--strip-components=1', + ], $baseDir); + + $process->mustRun(); + + unlink($downloadPath); + + $newVersion = $this->geoLite->getVersion(); + $logger->info('GeoLite DB updated. New version: ' . $newVersion); + } else { + $logger->error('Could not download updated GeoLite database.'); + } + } else { + $logger->info('Not checking for GeoLite updates; no license key provided.'); + } + + $this->settingsRepo->setSetting(Entity\Settings::GEOLITE_LAST_RUN, time()); + } +} + +