Switch to "provide license, download automatically" for GeoLite DB.

This commit is contained in:
Buster "Silver Eagle" Neece 2020-01-08 14:37:56 -06:00
parent ebb6349ae1
commit a208c172df
No known key found for this signature in database
GPG Key ID: 6D9E12FF03411F4E
9 changed files with 197 additions and 93 deletions

View File

@ -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

View File

@ -1,8 +1,6 @@
<?php
return [
'method' => 'post',
'enctype' => 'multipart/form-data',
'groups' => [
[
'use_grid' => true,
@ -16,14 +14,11 @@ return [
'<ul>' .
'<li>' . __('Create an account on <a href="%s" target="_blank">the MaxMind developer site</a>.',
'https://www.maxmind.com/en/geolite2/signup') . '</li>' .
'<li>' . __('Visit the <a href="%s" target="_blank">direct downloads page</a>.',
'https://www.maxmind.com/en/download_files?direct=1') . '</li>' .
'<li>' . __('Download the <code>%s</code> file (in GZIP) format.',
'GeoLite2-City') . '</li>' .
'<li>' . __('Select the downloaded file below to upload it.') . '</li>'
. '</ul>' .
'<p>' . __('You can repeat this process any time you need to update the GeoLite database.') . '</p>',
'form_group_class' => 'col-sm-12',
'<li>' . __('Visit the "My License Key" page under the "Services" section.') . '</li>' .
'<li>' . __('Click "Generate new license key".') . '</li>' .
'<li>' . __('Paste the generated license key into the field on this page.') . '</li>'
. '</ul>',
'form_group_class' => 'col-md-6',
],
],
@ -32,20 +27,16 @@ return [
[
'label' => __('Current Installed Version'),
'markup' => '<p class="text-danger">' . __('GeoLite is not currently installed on this installation.') . '</p>',
'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',
],
],
],

View File

@ -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

View File

@ -1,75 +1,22 @@
<?php
namespace App\Controller\Admin;
use App\Form\Form;
use App\Form\GeoLiteSettingsForm;
use App\Http\Response;
use App\Http\ServerRequest;
use App\Service\GeoLite;
use Azura\Config;
use Exception;
use Azura\Session\Flash;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\UploadedFileInterface;
use Symfony\Component\Process\Process;
use const UPLOAD_ERR_OK;
class InstallGeoLiteController
{
protected array $form_config;
protected GeoLite $geoLite;
public function __construct(Config $config, GeoLite $geoLite)
{
$this->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'] = '<p class="text-success">' . __('GeoLite version "%s" is currently installed.',
$version) . '</p>';
}
$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', [

View File

@ -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);

View File

@ -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

View File

@ -0,0 +1,59 @@
<?php
namespace App\Form;
use App\Entity;
use App\Http\ServerRequest;
use App\Service\GeoLite;
use App\Settings;
use App\Sync\Task\UpdateGeoLiteDatabase;
use Azura\Config;
use AzuraForms\Field\Markup;
use Doctrine\ORM\EntityManager;
class GeoLiteSettingsForm extends AbstractSettingsForm
{
protected GeoLite $geoLite;
protected UpdateGeoLiteDatabase $syncTask;
public function __construct(
EntityManager $em,
Entity\Repository\SettingsRepository $settingsRepo,
Settings $settings,
Config $config,
GeoLite $geoLite,
UpdateGeoLiteDatabase $syncTask
) {
$formConfig = $config->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',
'<p class="text-success">' . __('GeoLite version "%s" is currently installed.', $version) . '</p>'
);
}
$processed = parent::process($request);
if ($processed) {
$this->syncTask->run(true);
}
return $processed;
}
}

View File

@ -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',

View File

@ -0,0 +1,93 @@
<?php
namespace App\Sync\Task;
use App\Entity;
use App\Service\GeoLite;
use Azura\Logger;
use Doctrine\ORM\EntityManager;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use Symfony\Component\Process\Process;
class UpdateGeoLiteDatabase extends AbstractTask
{
protected const UPDATE_THRESHOLD = 86000;
protected Client $httpClient;
protected GeoLite $geoLite;
public function __construct(
EntityManager $em,
Entity\Repository\SettingsRepository $settingsRepo,
Client $httpClient,
GeoLite $geoLite
) {
parent::__construct($em, $settingsRepo);
$this->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());
}
}