Add MaxMind geoipupdate and upload fallback
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
|
||||
A modern web hosting control panel for WordPress and general PHP hosting. Built with Laravel 12, Filament v5, Livewire 4, and Tailwind CSS v4.
|
||||
|
||||
Version: 0.9-rc20 (release candidate)
|
||||
Version: 0.9-rc21 (release candidate)
|
||||
|
||||
This is a release candidate. Expect rapid iteration and breaking changes until 1.0.
|
||||
|
||||
|
||||
@@ -10,9 +10,11 @@ use App\Services\Agent\AgentClient;
|
||||
use Exception;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Forms\Components\FileUpload;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class ListGeoBlockRules extends ListRecords
|
||||
{
|
||||
@@ -75,6 +77,64 @@ class ListGeoBlockRules extends ListRecords
|
||||
->send();
|
||||
}
|
||||
}),
|
||||
Action::make('uploadGeoIpDatabase')
|
||||
->label(__('Upload GeoIP Database'))
|
||||
->icon('heroicon-o-arrow-up-tray')
|
||||
->form([
|
||||
FileUpload::make('mmdb_file')
|
||||
->label(__('GeoIP .mmdb File'))
|
||||
->required()
|
||||
->acceptedFileTypes(['application/octet-stream', 'application/x-maxmind-db'])
|
||||
->helperText(__('Upload a MaxMind GeoIP .mmdb file (GeoLite2 or GeoIP2).')),
|
||||
TextInput::make('edition')
|
||||
->label(__('Edition ID'))
|
||||
->helperText(__('Example: GeoLite2-Country'))
|
||||
->default(fn (): string => (string) (DnsSetting::get('geoip_edition_ids') ?? 'GeoLite2-Country')),
|
||||
])
|
||||
->action(function (array $data): void {
|
||||
if (empty($data['mmdb_file'])) {
|
||||
Notification::make()
|
||||
->title(__('No file uploaded'))
|
||||
->danger()
|
||||
->send();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$edition = trim((string) ($data['edition'] ?? 'GeoLite2-Country'));
|
||||
if ($edition === '') {
|
||||
$edition = 'GeoLite2-Country';
|
||||
}
|
||||
|
||||
$filePath = Storage::disk('local')->path($data['mmdb_file']);
|
||||
if (! file_exists($filePath)) {
|
||||
Notification::make()
|
||||
->title(__('Uploaded file not found'))
|
||||
->danger()
|
||||
->send();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$content = base64_encode((string) file_get_contents($filePath));
|
||||
|
||||
try {
|
||||
$agent = new AgentClient;
|
||||
$result = $agent->geoUploadDatabase($edition, $content);
|
||||
|
||||
Notification::make()
|
||||
->title(__('GeoIP database uploaded'))
|
||||
->body($result['path'] ?? null)
|
||||
->success()
|
||||
->send();
|
||||
} catch (Exception $e) {
|
||||
Notification::make()
|
||||
->title(__('GeoIP upload failed'))
|
||||
->body($e->getMessage())
|
||||
->danger()
|
||||
->send();
|
||||
}
|
||||
}),
|
||||
CreateAction::make(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1359,6 +1359,14 @@ class AgentClient
|
||||
]);
|
||||
}
|
||||
|
||||
public function geoUploadDatabase(string $edition, string $content): array
|
||||
{
|
||||
return $this->send('geo.upload_database', [
|
||||
'edition' => $edition,
|
||||
'content' => $content,
|
||||
]);
|
||||
}
|
||||
|
||||
public function databasePersistTuning(string $name, string $value): array
|
||||
{
|
||||
return $this->send('database.persist_tuning', [
|
||||
|
||||
148
bin/jabali-agent
148
bin/jabali-agent
@@ -548,6 +548,7 @@ function handleAction(array $request): array
|
||||
'waf.apply' => wafApplySettings($params),
|
||||
'geo.apply_rules' => geoApplyRules($params),
|
||||
'geo.update_database' => geoUpdateDatabase($params),
|
||||
'geo.upload_database' => geoUploadDatabase($params),
|
||||
'database.persist_tuning' => databasePersistTuning($params),
|
||||
'database.get_variables' => databaseGetVariables($params),
|
||||
'database.set_global' => databaseSetGlobal($params),
|
||||
@@ -2926,8 +2927,9 @@ function geoUpdateDatabase(array $params): array
|
||||
$editionIdsRaw = $params['edition_ids'] ?? 'GeoLite2-Country';
|
||||
$useExisting = !empty($params['use_existing']);
|
||||
|
||||
if (!toolExists('geoipupdate')) {
|
||||
return ['success' => false, 'error' => 'geoipupdate is not installed'];
|
||||
$toolError = ensureGeoIpUpdateTool();
|
||||
if ($toolError !== null) {
|
||||
return ['success' => false, 'error' => $toolError];
|
||||
}
|
||||
|
||||
if (!$useExisting && ($accountId === '' || $licenseKey === '')) {
|
||||
@@ -3002,6 +3004,148 @@ function geoUpdateDatabase(array $params): array
|
||||
return ['success' => false, 'error' => 'GeoIP database not found after update'];
|
||||
}
|
||||
|
||||
function geoUploadDatabase(array $params): array
|
||||
{
|
||||
$edition = trim((string) ($params['edition'] ?? 'GeoLite2-Country'));
|
||||
$content = (string) ($params['content'] ?? '');
|
||||
|
||||
if ($content === '') {
|
||||
return ['success' => false, 'error' => 'No database content provided'];
|
||||
}
|
||||
|
||||
if (!preg_match('/^[A-Za-z0-9._-]+$/', $edition)) {
|
||||
return ['success' => false, 'error' => 'Invalid edition name'];
|
||||
}
|
||||
|
||||
$decoded = base64_decode($content, true);
|
||||
if ($decoded === false) {
|
||||
return ['success' => false, 'error' => 'Invalid database content'];
|
||||
}
|
||||
|
||||
$targetDir = '/usr/share/GeoIP';
|
||||
if (!is_dir($targetDir)) {
|
||||
@mkdir($targetDir, 0755, true);
|
||||
}
|
||||
|
||||
$target = $targetDir . '/' . $edition . '.mmdb';
|
||||
if (file_put_contents($target, $decoded) === false) {
|
||||
return ['success' => false, 'error' => 'Failed to write GeoIP database'];
|
||||
}
|
||||
|
||||
@chmod($target, 0644);
|
||||
|
||||
return ['success' => true, 'path' => $target];
|
||||
}
|
||||
|
||||
function ensureGeoIpUpdateTool(): ?string
|
||||
{
|
||||
if (toolExists('geoipupdate')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$error = installGeoIpUpdateBinary();
|
||||
if ($error !== null) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
if (!toolExists('geoipupdate')) {
|
||||
return 'geoipupdate is not installed';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function installGeoIpUpdateBinary(): ?string
|
||||
{
|
||||
$arch = php_uname('m');
|
||||
$archMap = [
|
||||
'x86_64' => 'amd64',
|
||||
'amd64' => 'amd64',
|
||||
'aarch64' => 'arm64',
|
||||
'arm64' => 'arm64',
|
||||
];
|
||||
$archToken = $archMap[$arch] ?? $arch;
|
||||
|
||||
$apiUrl = 'https://api.github.com/repos/maxmind/geoipupdate/releases/latest';
|
||||
$metadata = @shell_exec('curl -fsSL ' . escapeshellarg($apiUrl) . ' 2>/dev/null');
|
||||
if (!$metadata) {
|
||||
$metadata = @shell_exec('wget -qO- ' . escapeshellarg($apiUrl) . ' 2>/dev/null');
|
||||
}
|
||||
|
||||
if (!$metadata) {
|
||||
return 'Failed to download geoipupdate release metadata';
|
||||
}
|
||||
|
||||
$data = json_decode($metadata, true);
|
||||
if (!is_array($data)) {
|
||||
return 'Invalid geoipupdate release metadata';
|
||||
}
|
||||
|
||||
$downloadUrl = null;
|
||||
foreach (($data['assets'] ?? []) as $asset) {
|
||||
$name = strtolower((string) ($asset['name'] ?? ''));
|
||||
$url = (string) ($asset['browser_download_url'] ?? '');
|
||||
if ($name === '' || $url === '') {
|
||||
continue;
|
||||
}
|
||||
if (strpos($name, 'linux') === false) {
|
||||
continue;
|
||||
}
|
||||
if (strpos($name, $archToken) === false) {
|
||||
if (!($archToken === 'amd64' && strpos($name, 'x86_64') !== false)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!str_ends_with($name, '.tar.gz') && !str_ends_with($name, '.tgz')) {
|
||||
continue;
|
||||
}
|
||||
$downloadUrl = $url;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$downloadUrl) {
|
||||
return 'No suitable geoipupdate binary found for ' . $arch;
|
||||
}
|
||||
|
||||
$tmpDir = sys_get_temp_dir() . '/jabali-geoipupdate-' . bin2hex(random_bytes(4));
|
||||
@mkdir($tmpDir, 0755, true);
|
||||
$archive = $tmpDir . '/geoipupdate.tgz';
|
||||
|
||||
$downloadCmd = toolExists('curl')
|
||||
? 'curl -fsSL ' . escapeshellarg($downloadUrl) . ' -o ' . escapeshellarg($archive)
|
||||
: 'wget -qO ' . escapeshellarg($archive) . ' ' . escapeshellarg($downloadUrl);
|
||||
|
||||
exec($downloadCmd . ' 2>&1', $output, $code);
|
||||
if ($code !== 0) {
|
||||
return 'Failed to download geoipupdate binary';
|
||||
}
|
||||
|
||||
exec('tar -xzf ' . escapeshellarg($archive) . ' -C ' . escapeshellarg($tmpDir) . ' 2>&1', $output, $code);
|
||||
if ($code !== 0) {
|
||||
return 'Failed to extract geoipupdate archive';
|
||||
}
|
||||
|
||||
$binary = null;
|
||||
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($tmpDir, FilesystemIterator::SKIP_DOTS));
|
||||
foreach ($iterator as $file) {
|
||||
if ($file->isFile() && $file->getFilename() === 'geoipupdate') {
|
||||
$binary = $file->getPathname();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$binary) {
|
||||
return 'geoipupdate binary not found in archive';
|
||||
}
|
||||
|
||||
exec('install -m 0755 ' . escapeshellarg($binary) . ' /usr/local/bin/geoipupdate 2>&1', $output, $code);
|
||||
if ($code !== 0) {
|
||||
return 'Failed to install geoipupdate';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function ensureGeoIpModuleEnabled(): ?string
|
||||
{
|
||||
$modulePaths = [
|
||||
|
||||
71
install.sh
71
install.sh
@@ -12,7 +12,7 @@
|
||||
set -e
|
||||
|
||||
# Version - will be read from VERSION file after clone, this is fallback
|
||||
JABALI_VERSION="0.9-rc20"
|
||||
JABALI_VERSION="0.9-rc21"
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
@@ -720,6 +720,74 @@ install_packages() {
|
||||
log "System packages installed"
|
||||
}
|
||||
|
||||
install_geoipupdate_binary() {
|
||||
if command -v geoipupdate &>/dev/null; then
|
||||
return
|
||||
fi
|
||||
|
||||
info "geoipupdate not found, installing from MaxMind releases..."
|
||||
|
||||
local arch
|
||||
arch="$(uname -m)"
|
||||
local arch_token="$arch"
|
||||
if [[ "$arch" == "x86_64" ]]; then
|
||||
arch_token="amd64"
|
||||
elif [[ "$arch" == "aarch64" || "$arch" == "arm64" ]]; then
|
||||
arch_token="arm64"
|
||||
fi
|
||||
|
||||
local api_url="https://api.github.com/repos/maxmind/geoipupdate/releases/latest"
|
||||
local metadata
|
||||
metadata=$(curl -fsSL "$api_url" 2>/dev/null || true)
|
||||
if [[ -z "$metadata" ]]; then
|
||||
metadata=$(wget -qO- "$api_url" 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
if [[ -z "$metadata" ]]; then
|
||||
warn "Failed to download geoipupdate release metadata"
|
||||
return
|
||||
fi
|
||||
|
||||
local download_url
|
||||
download_url=$(echo "$metadata" | grep -Eo "https://[^\"]+${arch_token}[^\"]+\\.tar\\.gz" | head -n1)
|
||||
if [[ -z "$download_url" && "$arch_token" == "amd64" ]]; then
|
||||
download_url=$(echo "$metadata" | grep -Eo "https://[^\"]+x86_64[^\"]+\\.tar\\.gz" | head -n1)
|
||||
fi
|
||||
|
||||
if [[ -z "$download_url" ]]; then
|
||||
warn "No suitable geoipupdate binary found for ${arch}"
|
||||
return
|
||||
fi
|
||||
|
||||
local tmp_dir
|
||||
tmp_dir=$(mktemp -d)
|
||||
local archive="${tmp_dir}/geoipupdate.tgz"
|
||||
|
||||
if command -v curl &>/dev/null; then
|
||||
curl -fsSL "$download_url" -o "$archive" 2>/dev/null || true
|
||||
else
|
||||
wget -qO "$archive" "$download_url" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
if [[ ! -s "$archive" ]]; then
|
||||
warn "Failed to download geoipupdate binary"
|
||||
rm -rf "$tmp_dir"
|
||||
return
|
||||
fi
|
||||
|
||||
tar -xzf "$archive" -C "$tmp_dir" 2>/dev/null || true
|
||||
local binary
|
||||
binary=$(find "$tmp_dir" -type f -name geoipupdate | head -n1)
|
||||
if [[ -z "$binary" ]]; then
|
||||
warn "geoipupdate binary not found in archive"
|
||||
rm -rf "$tmp_dir"
|
||||
return
|
||||
fi
|
||||
|
||||
install -m 0755 "$binary" /usr/local/bin/geoipupdate 2>/dev/null || true
|
||||
rm -rf "$tmp_dir"
|
||||
}
|
||||
|
||||
# Install Composer
|
||||
install_composer() {
|
||||
header "Installing Composer"
|
||||
@@ -3248,6 +3316,7 @@ main() {
|
||||
|
||||
add_repositories
|
||||
install_packages
|
||||
install_geoipupdate_binary
|
||||
install_composer
|
||||
clone_jabali
|
||||
configure_php
|
||||
|
||||
Reference in New Issue
Block a user