diff --git a/src/app/Console/Command.php b/src/app/Console/Command.php new file mode 100644 --- /dev/null +++ b/src/app/Console/Command.php @@ -0,0 +1,104 @@ +getObject(\App\Domain::class, $domain, 'namespace'); + } + + /** + * Find an object. + * + * @param string $objectClass The name of the class + * @param string $objectIdOrTitle The name of a database field to match. + * @param string|null $objectTitle An additional database field to match. + * + * @return mixed + */ + public function getObject($objectClass, $objectIdOrTitle, $objectTitle) + { + $object = $objectClass::find($objectIdOrTitle); + + if (!$object && !empty($objectTitle)) { + $object = $objectClass::where($objectTitle, $objectIdOrTitle)->first(); + } + + return $object; + } + + /** + * Find the user. + * + * @param string $user User ID or email + * + * @return \App\User|null + */ + public function getUser($user) + { + return $this->getObject(\App\User::class, $user, 'email'); + } + + /** + * Find the wallet. + * + * @param string $wallet Wallet ID + * + * @return \App\Wallet|null + */ + public function getWallet($wallet) + { + return $this->getObject(\App\Wallet::class, $wallet, null); + } + + /** + * Return a string for output, with any additional attributes specified as well. + * + * @param mixed $entry An object + * + * @return string + */ + protected function toString($entry) + { + /** + * Haven't figured out yet, how to test if this command implements an option for additional + * attributes. + if (!in_array('attr', $this->options())) { + return $entry->{$entry->getKeyName()}; + } + */ + + $str = [ + $entry->{$entry->getKeyName()} + ]; + + foreach ($this->option('attr') as $attr) { + if ($attr == $entry->getKeyName()) { + $this->warn("Specifying {$attr} is not useful."); + continue; + } + + if (!array_key_exists($attr, $entry->toArray())) { + $this->error("Attribute {$attr} isn't available"); + continue; + } + + if (is_numeric($entry->{$attr})) { + $str[] = $entry->{$attr}; + } else { + $str[] = !empty($entry->{$attr}) ? $entry->{$attr} : "null"; + } + } + + return implode(" ", $str); + } +} diff --git a/src/app/Console/Commands/DataCountries.php b/src/app/Console/Commands/Data/Import/CountriesCommand.php rename from src/app/Console/Commands/DataCountries.php rename to src/app/Console/Commands/Data/Import/CountriesCommand.php --- a/src/app/Console/Commands/DataCountries.php +++ b/src/app/Console/Commands/Data/Import/CountriesCommand.php @@ -1,10 +1,11 @@ currency @@ -16,14 +17,14 @@ * * @var string */ - protected $signature = 'data:countries'; + protected $signature = 'data:import:countries'; /** * The console command description. * * @var string */ - protected $description = 'Fetches countries map from wikipedia'; + protected $description = 'Fetches countries map from country.io'; /** * Execute the console command. @@ -32,50 +33,65 @@ */ public function handle() { + $today = Carbon::now()->toDateString(); + $countries = []; $currencies = []; - $currencies_url = 'http://country.io/currency.json'; - $countries_url = 'http://country.io/names.json'; - - $this->info("Fetching currencies from $currencies_url..."); + $currencySource = 'http://country.io/currency.json'; + $countrySource = 'http://country.io/names.json'; - // fetch currency table and create an index by country page url - $currencies_json = file_get_contents($currencies_url); - - if (!$currencies_json) { - $this->error("Failed to fetch currencies"); - return; - } + // + // countries + // + $file = storage_path("countries-{$today}.json"); - $this->info("Fetching countries from $countries_url..."); + \App\Utils::downloadFile($countrySource, $file); - $countries_json = file_get_contents($countries_url); + $countryJson = file_get_contents($file); - if (!$countries_json) { + if (!$countryJson) { $this->error("Failed to fetch countries"); - return; + return 1; } - $currencies = json_decode($currencies_json, true); - $countries = json_decode($countries_json, true); + $countries = json_decode($countryJson, true); if (!is_array($countries) || empty($countries)) { $this->error("Invalid countries data"); + return 1; + } + + // + // currencies + // + $file = storage_path("currencies-{$today}.json"); + + \App\Utils::downloadFile($currencySource, $file); + + // fetch currency table and create an index by country page url + $currencyJson = file_get_contents($file); + + if (!$currencyJson) { + $this->error("Failed to fetch currencies"); return; } + $currencies = json_decode($currencyJson, true); + if (!is_array($currencies) || empty($currencies)) { $this->error("Invalid currencies data"); - return; + return 1; } + // + // export + // $file = resource_path('countries.php'); - $this->info("Generating resource file $file..."); - asort($countries); $out = " $name) { $currency = $currencies[$code] ?? null; @@ -84,12 +100,13 @@ } if (!$currency) { - $this->error("Unknown currency for {$name} ({$code}). Skipped."); + $this->warn("Unknown currency for {$name} ({$code}). Skipped."); continue; } $out .= sprintf(" '%s' => ['%s','%s'],\n", $code, $currency, addslashes($name)); } + $out .= "];\n"; file_put_contents($file, $out); diff --git a/src/app/Console/Commands/Data/Import/IP4NetsCommand.php b/src/app/Console/Commands/Data/Import/IP4NetsCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/Commands/Data/Import/IP4NetsCommand.php @@ -0,0 +1,225 @@ + 'http://ftp.afrinic.net/stats/afrinic/delegated-afrinic-latest', + 'apnic' => 'http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest', + 'arin' => 'http://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest', + 'lacnic' => 'http://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-latest', + 'ripencc' => 'https://ftp.ripe.net/ripe/stats/delegated-ripencc-latest' + ]; + + $today = Carbon::now()->toDateString(); + + foreach ($rirs as $rir => $url) { + $file = storage_path("{$rir}-{$today}"); + + \App\Utils::downloadFile($url, $file); + + $serial = $this->serialFromStatsFile($file); + + if (!$serial) { + \Log::error("Can not derive serial from {$file}"); + continue; + } + + $numLines = $this->countLines($file); + + if (!$numLines) { + \Log::error("No relevant lines could be found in {$file}"); + continue; + } + + $bar = \App\Utils::createProgressBar( + $this->output, + $numLines, + "Importing IPv4 Networks from {$file}" + ); + + $fp = fopen($file, 'r'); + + $nets = []; + + while (!feof($fp)) { + $line = trim(fgets($fp)); + + if ($line == "") { + continue; + } + + if ((int)$line) { + continue; + } + + if ($line[0] == "#") { + continue; + } + + $items = explode('|', $line); + + if (sizeof($items) < 7) { + continue; + } + + if ($items[1] == "*") { + continue; + } + + if ($items[2] != "ipv4") { + continue; + } + + if ($items[5] == "00000000") { + $items[5] = "19700102"; + } + + if ($items[1] == "" || $items[1] == "ZZ") { + continue; + } + + $bar->advance(); + + $mask = 32 - log($items[4], 2); + + $net = \App\IP4Net::where( + [ + 'net_number' => $items[3], + 'net_mask' => $mask, + 'net_broadcast' => long2ip((ip2long($items[3]) + 2 ** (32 - $mask)) - 1) + ] + )->first(); + + if ($net) { + if ($net->updated_at > Carbon::now()->subDays(1)) { + continue; + } + + // don't use ->update() method because it doesn't update updated_at which we need for expiry + $net->rir_name = $rir; + $net->country = $items[1]; + $net->serial = $serial; + $net->updated_at = Carbon::now(); + + $net->save(); + + continue; + } + + $nets[] = [ + 'rir_name' => $rir, + 'net_number' => $items[3], + 'net_mask' => $mask, + 'net_broadcast' => long2ip((ip2long($items[3]) + 2 ** (32 - $mask)) - 1), + 'country' => $items[1], + 'serial' => $serial, + 'created_at' => Carbon::parse($items[5], 'UTC'), + 'updated_at' => Carbon::now() + ]; + + if (sizeof($nets) >= 100) { + \App\IP4Net::insert($nets); + $nets = []; + } + } + + if (sizeof($nets) > 0) { + \App\IP4Net::insert($nets); + $nets = []; + } + + $bar->finish(); + + $this->info("DONE"); + } + + return 0; + } + + private function countLines($file) + { + $numLines = 0; + + $fh = fopen($file, 'r'); + + while (!feof($fh)) { + $line = trim(fgets($fh)); + + $items = explode('|', $line); + + if (sizeof($items) < 3) { + continue; + } + + if ($items[2] == "ipv4") { + $numLines++; + } + } + + fclose($fh); + + return $numLines; + } + + private function serialFromStatsFile($file) + { + $serial = null; + + $fh = fopen($file, 'r'); + + while (!feof($fh)) { + $line = trim(fgets($fh)); + + $items = explode('|', $line); + + if (sizeof($items) < 2) { + continue; + } + + if ((int)$items[2]) { + $serial = (int)$items[2]; + break; + } + } + + fclose($fh); + + return $serial; + } +} diff --git a/src/app/Console/Commands/Data/Import/IP6NetsCommand.php b/src/app/Console/Commands/Data/Import/IP6NetsCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/Commands/Data/Import/IP6NetsCommand.php @@ -0,0 +1,223 @@ + 'http://ftp.afrinic.net/stats/afrinic/delegated-afrinic-latest', + 'apnic' => 'http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest', + 'arin' => 'http://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest', + 'lacnic' => 'http://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-latest', + 'ripencc' => 'https://ftp.ripe.net/ripe/stats/delegated-ripencc-latest' + ]; + + $today = Carbon::now()->toDateString(); + + foreach ($rirs as $rir => $url) { + $file = storage_path("{$rir}-{$today}"); + + \App\Utils::downloadFile($url, $file); + + $serial = $this->serialFromStatsFile($file); + + if (!$serial) { + \Log::error("Can not derive serial from {$file}"); + continue; + } + + $numLines = $this->countLines($file); + + if (!$numLines) { + \Log::error("No relevant lines could be found in {$file}"); + continue; + } + + $bar = \App\Utils::createProgressBar( + $this->output, + $numLines, + "Importing IPv6 Networks from {$file}" + ); + + $fp = fopen($file, 'r'); + + $nets = []; + + while (!feof($fp)) { + $line = trim(fgets($fp)); + + if ($line == "") { + continue; + } + + if ((int)$line) { + continue; + } + + if ($line[0] == "#") { + continue; + } + + $items = explode('|', $line); + + if (sizeof($items) < 7) { + continue; + } + + if ($items[1] == "*") { + continue; + } + + if ($items[2] != "ipv6") { + continue; + } + + if ($items[5] == "00000000") { + $items[5] = "19700102"; + } + + if ($items[1] == "" || $items[1] == "ZZ") { + continue; + } + + $bar->advance(); + + $broadcast = \App\Utils::ip6Broadcast($items[3], (int)$items[4]); + + $net = \App\IP6Net::where( + [ + 'net_number' => $items[3], + 'net_mask' => (int)$items[4], + 'net_broadcast' => $broadcast + ] + )->first(); + + if ($net) { + if ($net->updated_at > Carbon::now()->subDays(1)) { + continue; + } + + // don't use ->update() method because it doesn't update updated_at which we need for expiry + $net->rir_name = $rir; + $net->country = $items[1]; + $net->serial = $serial; + $net->updated_at = Carbon::now(); + + $net->save(); + + continue; + } + + $nets[] = [ + 'rir_name' => $rir, + 'net_number' => $items[3], + 'net_mask' => (int)$items[4], + 'net_broadcast' => $broadcast, + 'country' => $items[1], + 'serial' => $serial, + 'created_at' => Carbon::parse($items[5], 'UTC'), + 'updated_at' => Carbon::now() + ]; + + if (sizeof($nets) >= 100) { + \App\IP6Net::insert($nets); + $nets = []; + } + } + + if (sizeof($nets) > 0) { + \App\IP6Net::insert($nets); + $nets = []; + } + + $bar->finish(); + + $this->info("DONE"); + } + } + + private function countLines($file) + { + $numLines = 0; + + $fh = fopen($file, 'r'); + + while (!feof($fh)) { + $line = trim(fgets($fh)); + + $items = explode('|', $line); + + if (sizeof($items) < 3) { + continue; + } + + if ($items[2] == "ipv6") { + $numLines++; + } + } + + fclose($fh); + + return $numLines; + } + + private function serialFromStatsFile($file) + { + $serial = null; + + $fh = fopen($file, 'r'); + + while (!feof($fh)) { + $line = trim(fgets($fh)); + + $items = explode('|', $line); + + if (sizeof($items) < 2) { + continue; + } + + if ((int)$items[2]) { + $serial = (int)$items[2]; + break; + } + } + + fclose($fh); + + return $serial; + } +} diff --git a/src/app/Console/Commands/Data/ImportCommand.php b/src/app/Console/Commands/Data/ImportCommand.php new file mode 100644 --- /dev/null +++ b/src/app/Console/Commands/Data/ImportCommand.php @@ -0,0 +1,54 @@ +output = $this->output; + $execution->handle(); + } + + return 0; + } +} diff --git a/src/app/Console/Kernel.php b/src/app/Console/Kernel.php --- a/src/app/Console/Kernel.php +++ b/src/app/Console/Kernel.php @@ -25,8 +25,18 @@ */ protected function schedule(Schedule $schedule) { - // $schedule->command('inspire') - // ->hourly(); + // This command imports countries and the current set of IPv4 and IPv6 networks allocated to countries. + $schedule->command('data:import')->dailyAt('05:00'); + + $schedule->command('wallet:charge')->dailyAt('00:00'); + $schedule->command('wallet:charge')->dailyAt('04:00'); + $schedule->command('wallet:charge')->dailyAt('08:00'); + $schedule->command('wallet:charge')->dailyAt('12:00'); + $schedule->command('wallet:charge')->dailyAt('16:00'); + $schedule->command('wallet:charge')->dailyAt('20:00'); + + // this is a laravel 8-ism + //$schedule->command('wallet:charge')->everyFourHours(); } /** diff --git a/src/app/IP4Net.php b/src/app/IP4Net.php new file mode 100644 --- /dev/null +++ b/src/app/IP4Net.php @@ -0,0 +1,19 @@ += INET6_ATON(?) + ORDER BY INET6_ATON(net_number), net_mask DESC LIMIT 1 + "; + + $results = DB::select($query, [$ip, $ip]); + + if (sizeof($results) == 0) { + return null; + } + + return \App\IP6Net::find($results[0]->id); + } +} diff --git a/src/app/Observers/UserObserver.php b/src/app/Observers/UserObserver.php --- a/src/app/Observers/UserObserver.php +++ b/src/app/Observers/UserObserver.php @@ -54,7 +54,7 @@ public function created(User $user) { $settings = [ - 'country' => 'CH', + 'country' => \App\Utils::countryForRequest(), 'currency' => 'CHF', /* 'first_name' => '', diff --git a/src/app/Utils.php b/src/app/Utils.php --- a/src/app/Utils.php +++ b/src/app/Utils.php @@ -11,6 +11,98 @@ */ class Utils { + /** + * Count the number of lines in a file. + * + * Useful for progress bars. + * + * @param string $file The filepath to count the lines of. + * + * @return int + */ + public static function countLines($file) + { + $fh = fopen($file, 'rb'); + $numLines = 0; + + while (!feof($fh)) { + $numLines += substr_count(fread($fh, 8192), "\n"); + } + + fclose($fh); + + return $numLines; + } + + /** + * Return the country ISO code for an IP address. + * + * @return string + */ + public static function countryForIP($ip) + { + if (strpos(':', $ip) === false) { + $query = " + SELECT country FROM ip4nets + WHERE INET_ATON(net_number) <= INET_ATON(?) + AND INET_ATON(net_broadcast) >= INET_ATON(?) + ORDER BY INET_ATON(net_number), net_mask DESC LIMIT 1 + "; + } else { + $query = " + SELECT id FROM ip6nets + WHERE INET6_ATON(net_number) <= INET6_ATON(?) + AND INET6_ATON(net_broadcast) >= INET6_ATON(?) + ORDER BY INET6_ATON(net_number), net_mask DESC LIMIT 1 + "; + } + + $nets = \Illuminate\Support\Facades\DB::select($query, [$ip, $ip]); + + if (sizeof($nets) > 0) { + return $nets[0]->country; + } + + return 'CH'; + } + + /** + * Return the country ISO code for the current request. + */ + public static function countryForRequest() + { + $request = \request(); + $ip = $request->ip(); + + return self::countryForIP($ip); + } + + /** + * Shortcut to creating a progress bar of a particular format with a particular message. + * + * @param \Illuminate\Console\OutputStyle $output Console output object + * @param int $count Number of progress steps + * @param string $message The description + * + * @return \Symfony\Component\Console\Helper\ProgressBar + */ + public static function createProgressBar($output, $count, $message = null) + { + $bar = $output->createProgressBar($count); + + $bar->setFormat( + '%current:7s%/%max:7s% [%bar%] %percent:3s%% %elapsed:7s%/%estimated:-7s% %message% ' + ); + + if ($message) { + $bar->setMessage($message . " ..."); + } + + $bar->start(); + + return $bar; + } + /** * Return the number of days in the month prior to this one. * @@ -24,6 +116,100 @@ return $start->diffInDays($end) + 1; } + /** + * Download a file from the interwebz and store it locally. + * + * @param string $source The source location + * @param string $target The target location + * @param bool $force Force the download (and overwrite target) + * + * @return void + */ + public static function downloadFile($source, $target, $force = false) + { + if (is_file($target) && !$force) { + return; + } + + \Log::info("Retrieving {$source}"); + + $fp = fopen($target, 'w'); + + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $source); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curl, CURLOPT_FILE, $fp); + curl_exec($curl); + + if (curl_errno($curl)) { + \Log::error("Request error on {$source}: " . curl_error($curl)); + + curl_close($curl); + fclose($fp); + + unlink($target); + return; + } + + curl_close($curl); + fclose($fp); + } + + /** + * Calculate the broadcast address provided a net number and a prefix. + * + * @param string $net A valid IPv6 network number. + * @param int $prefix The network prefix. + * + * @return string + */ + public static function ip6Broadcast($net, $prefix) + { + $netHex = bin2hex(inet_pton($net)); + + // Overwriting first address string to make sure notation is optimal + $net = inet_ntop(hex2bin($netHex)); + + // Calculate the number of 'flexible' bits + $flexbits = 128 - $prefix; + + // Build the hexadecimal string of the last address + $lastAddrHex = $netHex; + + // We start at the end of the string (which is always 32 characters long) + $pos = 31; + while ($flexbits > 0) { + // Get the character at this position + $orig = substr($lastAddrHex, $pos, 1); + + // Convert it to an integer + $origval = hexdec($orig); + + // OR it with (2^flexbits)-1, with flexbits limited to 4 at a time + $newval = $origval | (pow(2, min(4, $flexbits)) - 1); + + // Convert it back to a hexadecimal character + $new = dechex($newval); + + // And put that character back in the string + $lastAddrHex = substr_replace($lastAddrHex, $new, $pos, 1); + + // We processed one nibble, move to previous position + $flexbits -= 4; + $pos -= 1; + } + + // Convert the hexadecimal string to a binary string + # Using pack() here + # Newer PHP version can use hex2bin() + $lastaddrbin = pack('H*', $lastAddrHex); + + // And create an IPv6 address from the binary string + $lastaddrstr = inet_ntop($lastaddrbin); + + return $lastaddrstr; + } + /** * Provide all unique combinations of elements in $input, with order and duplicates irrelevant. * diff --git a/src/composer.json b/src/composer.json --- a/src/composer.json +++ b/src/composer.json @@ -16,22 +16,18 @@ "require": { "php": "^7.1.3", "barryvdh/laravel-dompdf": "^0.8.6", - "doctrine/dbal": "^2.9", + "dyrynda/laravel-nullable-fields": "*", "fideloper/proxy": "^4.0", - "geoip2/geoip2": "^2.9", - "iatstuti/laravel-nullable-fields": "*", "kolab/net_ldap3": "dev-master", "laravel/framework": "6.*", "laravel/tinker": "^2.4", "mollie/laravel-mollie": "^2.9", "morrislaptop/laravel-queue-clear": "^1.2", - "silviolleite/laravelpwa": "^1.0", + "silviolleite/laravelpwa": "^2.0", "spatie/laravel-translatable": "^4.2", "spomky-labs/otphp": "~4.0.0", "stripe/stripe-php": "^7.29", "swooletw/laravel-swoole": "^2.6", - "torann/currency": "^1.0", - "torann/geoip": "^1.0", "tymon/jwt-auth": "^1.0" }, "require-dev": { diff --git a/src/config/geoip.php b/src/config/geoip.php deleted file mode 100644 --- a/src/config/geoip.php +++ /dev/null @@ -1,165 +0,0 @@ - true, - - /* - |-------------------------------------------------------------------------- - | Include Currency in Results - |-------------------------------------------------------------------------- - | - | When enabled the system will do it's best in deciding the user's currency - | by matching their ISO code to a preset list of currencies. - | - */ - - 'include_currency' => true, - - /* - |-------------------------------------------------------------------------- - | Default Service - |-------------------------------------------------------------------------- - | - | Here you may specify the default storage driver that should be used - | by the framework. - | - | Supported: "maxmind_database", "maxmind_api", "ipapi" - | - */ - - 'service' => 'maxmind_database', - - /* - |-------------------------------------------------------------------------- - | Storage Specific Configuration - |-------------------------------------------------------------------------- - | - | Here you may configure as many storage drivers as you wish. - | - */ - - 'services' => [ - - 'maxmind_database' => [ - 'class' => \Torann\GeoIP\Services\MaxMindDatabase::class, - 'database_path' => storage_path('app/geoip.mmdb'), - 'update_url' => 'https://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz', - 'locales' => ['en'], - ], - - 'maxmind_api' => [ - 'class' => \Torann\GeoIP\Services\MaxMindWebService::class, - 'user_id' => env('MAXMIND_USER_ID'), - 'license_key' => env('MAXMIND_LICENSE_KEY'), - 'locales' => ['en'], - ], - - 'ipapi' => [ - 'class' => \Torann\GeoIP\Services\IPApi::class, - 'secure' => true, - 'key' => env('IPAPI_KEY'), - 'continent_path' => storage_path('app/continents.json'), - 'lang' => 'en', - ], - - 'ipgeolocation' => [ - 'class' => \Torann\GeoIP\Services\IPGeoLocation::class, - 'secure' => true, - 'key' => env('IPGEOLOCATION_KEY'), - 'continent_path' => storage_path('app/continents.json'), - 'lang' => 'en', - ], - - 'ipdata' => [ - 'class' => \Torann\GeoIP\Services\IPData::class, - 'key' => env('IPDATA_API_KEY'), - 'secure' => true, - ], - - 'ipfinder' => [ - 'class' => \Torann\GeoIP\Services\IPFinder::class, - 'key' => env('IPFINDER_API_KEY'), - 'secure' => true, - 'locales' => ['en'], - ], - - ], - - /* - |-------------------------------------------------------------------------- - | Default Cache Driver - |-------------------------------------------------------------------------- - | - | Here you may specify the type of caching that should be used - | by the package. - | - | Options: - | - | all - All location are cached - | some - Cache only the requesting user - | none - Disable cached - | - */ - - 'cache' => 'all', - - /* - |-------------------------------------------------------------------------- - | Cache Tags - |-------------------------------------------------------------------------- - | - | Cache tags are not supported when using the file or database cache - | drivers in Laravel. This is done so that only locations can be cleared. - | - */ - - 'cache_tags' => false, - - /* - |-------------------------------------------------------------------------- - | Cache Expiration - |-------------------------------------------------------------------------- - | - | Define how long cached location are valid. - | - */ - - 'cache_expires' => 30, - - /* - |-------------------------------------------------------------------------- - | Default Location - |-------------------------------------------------------------------------- - | - | Return when a location is not found. - | - */ - - 'default_location' => [ - 'ip' => '127.0.0.0', - 'iso_code' => 'CH', - 'country' => 'Switzerland', - 'city' => 'Zurich', - 'state' => 'ZH', - 'state_name' => 'Zurich', - 'postal_code' => '8703', - 'lat' => 47.30, - 'lon' => 8.59, - 'timezone' => 'Europe/Zurich', - 'continent' => 'EU', - 'default' => true, - 'currency' => 'CHF', - ], - -]; diff --git a/src/database/migrations/2020_06_04_140800_create_ip4nets_table.php b/src/database/migrations/2020_06_04_140800_create_ip4nets_table.php new file mode 100644 --- /dev/null +++ b/src/database/migrations/2020_06_04_140800_create_ip4nets_table.php @@ -0,0 +1,43 @@ +bigIncrements('id'); + $table->string('rir_name', 8); + $table->string('net_number', 15)->index(); + $table->tinyInteger('net_mask')->unsigned(); + $table->string('net_broadcast', 15)->index(); + $table->string('country', 2)->nullable(); + $table->bigInteger('serial')->unsigned(); + $table->timestamps(); + + $table->index(['net_number', 'net_mask', 'net_broadcast']); + } + ); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('ip4nets'); + } +} diff --git a/src/database/migrations/2020_06_04_140800_create_ip6nets_table.php b/src/database/migrations/2020_06_04_140800_create_ip6nets_table.php new file mode 100644 --- /dev/null +++ b/src/database/migrations/2020_06_04_140800_create_ip6nets_table.php @@ -0,0 +1,43 @@ +bigIncrements('id'); + $table->string('rir_name', 8); + $table->string('net_number', 39)->index(); + $table->tinyInteger('net_mask')->unsigned(); + $table->string('net_broadcast', 39)->index(); + $table->string('country', 2)->nullable(); + $table->bigInteger('serial')->unsigned(); + $table->timestamps(); + + $table->index(['net_number', 'net_mask', 'net_broadcast']); + } + ); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('ip6nets'); + } +}