Skip to content
This repository was archived by the owner on Dec 14, 2023. It is now read-only.

Commit 3fe12e5

Browse files
authored
Merge pull request #131 from pacoorozco/issue-122
Fix import zone tool
2 parents 8b1ce4c + 47531e7 commit 3fe12e5

7 files changed

Lines changed: 150 additions & 162 deletions

File tree

app/Console/Commands/ProBINDImportZone.php

Lines changed: 29 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -9,60 +9,36 @@
99

1010
class ProBINDImportZone extends Command
1111
{
12-
/**
13-
* The name and signature of the console command.
14-
*
15-
* @var string
16-
*/
12+
const SUCCESS_CODE = 0;
13+
const ERROR_PARSING_FILE_CODE = 1;
14+
const ERROR_EXISTING_ZONE_CODE = 2;
15+
1716
protected $signature = 'probind:import
1817
{--domain= : The zone domain name to create}
19-
{--file= : The file name to import}
20-
{--force : Delete existing zone before import}';
21-
22-
/**
23-
* The console command description.
24-
*
25-
* @var string
26-
*/
27-
protected $description = 'Import a BIND zone file to ProBIND';
18+
{--file= : The file name to import}';
2819

29-
/**
30-
* Create a new command instance.
31-
*/
32-
public function __construct()
33-
{
34-
parent::__construct();
35-
}
20+
protected $description = 'Imports a BIND zone file to ProBIND';
3621

37-
/**
38-
* Execute the console command.
39-
*/
40-
public function handle(): void
22+
public function handle(): int
4123
{
42-
// Cast supplied arguments and options.
43-
$domain = (string) $this->option('domain');
44-
$filename = (string) $this->option('file');
24+
$domain = $this->ensureFQDN($this->option('domain'));
4525

46-
// Adds the ending '.' (dot) to the zone name.
47-
$domain = (substr($domain, -1) != '.') ? $domain . '.' : $domain;
26+
if (Zone::where('domain', $domain)->first()) {
27+
$this->error('Zone can not be imported. A zone for the provided domain already exists.');
4828

49-
if (! $this->option('force')) {
50-
// Check if Zone exists on database.
51-
$existingZone = Zone::where('domain', $domain)->first();
29+
return self::ERROR_EXISTING_ZONE_CODE;
30+
}
5231

53-
if ($existingZone) {
54-
$this->error('Zone \'' . $existingZone->domain . '\' exists on ProBIND. Use \'--force\' option if you want to import this zone.');
32+
try {
33+
$zoneData = $this->parseFile($domain, $this->option('file'));
34+
} catch (\Throwable $exception) {
35+
$this->error('The provided file could not be parsed.');
5536

56-
return;
57-
}
37+
return self::ERROR_PARSING_FILE_CODE;
5838
}
5939

60-
// Delete zone, if exists on database.
61-
$this->deleteZoneIfExists($domain);
62-
63-
$zoneData = $this->parseFile($domain, $filename);
40+
/** @var Zone $zone */
6441
$zone = Zone::create([
65-
'custom_settings' => true,
6642
'domain' => $domain,
6743
'reverse_zone' => Zone::isReverseZoneName($domain),
6844
]);
@@ -79,6 +55,7 @@ public function handle(): void
7955
]);
8056
continue;
8157
}
58+
8259
$zone->records()->create([
8360
'name' => $record->getName(),
8461
'ttl' => $record->getTtl(),
@@ -88,43 +65,28 @@ public function handle(): void
8865
$createdRecordsCount++;
8966
}
9067

91-
$this->info('Import zone \'' . $domain . '\' has created with ' . $createdRecordsCount . ' imported records.');
92-
activity()->log('Import zone <strong>' . $zone->domain . '</strong> has created <strong>' . $createdRecordsCount . '</strong> records.');
68+
$this->info('A zone for ' . $domain . ' domain has been created. ' . $createdRecordsCount . ' records has been imported.');
69+
activity()->log('Created zone <strong>' . $zone->domain . '</strong> by importing <strong>' . $createdRecordsCount . '</strong> records.');
70+
71+
return self::SUCCESS_CODE;
9372
}
9473

95-
/**
96-
* Delete the specified zone by domain search if exists.
97-
*
98-
* @param string $domain
99-
*/
100-
private function deleteZoneIfExists(string $domain): void
74+
private function ensureFQDN(string $domain): string
10175
{
102-
// Check if Zone exists on database, including trashed zones.
103-
$existingZone = Zone::withTrashed()
104-
->where('domain', $domain)->first();
105-
106-
if ($existingZone) {
107-
$existingZone->forceDelete();
108-
}
76+
return (substr($domain, -1) != '.') ? $domain . '.' : $domain;
10977
}
11078

11179
/**
11280
* Parses a DNS zone file and returns its content.
11381
*
114-
* @param string $domain
115-
* @param string $filename
82+
* @param string $domain
83+
* @param string $filename
11684
*
11785
* @return \Badcow\DNS\Zone
118-
* @throws \ErrorException
86+
* @throws \Badcow\DNS\Parser\ParseException
11987
*/
12088
private function parseFile(string $domain, string $filename): \Badcow\DNS\Zone
12189
{
122-
try {
123-
$file = file_get_contents($filename);
124-
125-
return Parser\Parser::parse($domain, $file);
126-
} catch (\Exception $exception) {
127-
throw new \ErrorException();
128-
}
90+
return Parser\Parser::parse($domain, file_get_contents($filename));
12991
}
13092
}

app/Http/Controllers/ToolsController.php

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
use App\Server;
2222
use App\Zone;
2323
use Illuminate\Support\Facades\Artisan;
24+
use Illuminate\Support\Facades\Storage;
25+
use Illuminate\View\View;
2426

2527
class ToolsController extends Controller
2628
{
@@ -103,36 +105,18 @@ public function importZone()
103105
return view('tools.import_zone');
104106
}
105107

106-
/**
107-
* Call Artisan 'probind:import' command with supplied data.
108-
*
109-
* @param ImportZoneRequest $request
110-
*
111-
* @return \Illuminate\Http\RedirectResponse
112-
*
113-
* FIXME
114-
* Use queues for doing long tasks on background. This will require some notification system.
115-
* Errors processing import Zone.
116-
*/
117-
public function importZonePost(ImportZoneRequest $request)
108+
public function importZoneFromFile(ImportZoneRequest $request): View
118109
{
119-
// Move uploaded file to local storage.
120-
$zonefile = $request->file('zonefile')->store('temp');
121-
if (false === $zonefile) {
122-
// Handle error
123-
redirect()->route('home')
124-
->with('error',
125-
__('tools/messages.import_zone_error', ['zone' => $request->input('domain')]));
126-
}
110+
$filename = $request->zoneFile()->store('temp');
127111

128112
Artisan::call('probind:import', [
129-
'zone' => $request->input('domain'),
130-
'zonefile' => storage_path('app/' . $zonefile),
131-
'--force' => $request->has('overwrite'),
113+
'--domain' => $request->domain(),
114+
'--file' => Storage::path($filename),
132115
]);
133116

134-
return redirect()->route('home')
135-
->with('success',
136-
__('tools/messages.import_zone_success', ['zone' => $request->input('domain')]));
117+
Storage::delete($filename);
118+
119+
return view('tools.import_zone_result')
120+
->with('output', Artisan::output());
137121
}
138122
}

app/Http/Requests/ImportZoneRequest.php

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,39 @@
33
namespace App\Http\Requests;
44

55
use Illuminate\Foundation\Http\FormRequest;
6+
use Illuminate\Http\UploadedFile;
7+
use Illuminate\Validation\Rule;
68

79
class ImportZoneRequest extends FormRequest
810
{
9-
/**
10-
* Determine if the user is authorized to make this request.
11-
*
12-
* @return bool
13-
*/
14-
public function authorize()
11+
public function authorize(): bool
1512
{
1613
return true;
1714
}
1815

19-
/**
20-
* Get the validation rules that apply to the request.
21-
*
22-
* @return array
23-
*/
24-
public function rules()
16+
public function rules(): array
2517
{
2618
return [
27-
'domain' => 'required|string',
28-
'zonefile' => 'required|file|max:2048',
29-
'overwrite' => 'sometimes|boolean',
19+
'domain' => [
20+
'required',
21+
'string',
22+
Rule::unique('zones'),
23+
],
24+
'zonefile' => [
25+
'required',
26+
'mimetypes:text/plain',
27+
'max:2048',
28+
],
3029
];
3130
}
31+
32+
public function domain(): string
33+
{
34+
return $this->input('domain');
35+
}
36+
37+
public function zoneFile(): ?UploadedFile
38+
{
39+
return $this->file('zonefile');
40+
}
3241
}

resources/views/tools/import_zone.blade.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
<span class="help-block">{{ $errors->first('domain', ':message') }}</span>
5858
</div>
5959
</div>
60+
6061
</div>
6162

6263
<div class="box-footer">
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
@extends('layouts.admin')
2+
3+
{{-- Web site Title --}}
4+
@section('title', __('tools/title.push_updates'))
5+
6+
{{-- Content Header --}}
7+
@section('header')
8+
{{ __('tools/title.import_zone') }}
9+
<small>{{ __('tools/title.import_zone_subtitle') }}</small>
10+
@endsection
11+
12+
{{-- Breadcrumbs --}}
13+
@section('breadcrumbs')
14+
<li>
15+
<a href="{{ route('home') }}">
16+
<i class="fa fa-dashboard"></i> {{ __('site.dashboard') }}
17+
</a>
18+
</li>
19+
<li>
20+
<a href="{{-- route('tools.index') --}}">
21+
{{ __('site.tools') }}
22+
</a>
23+
</li>
24+
<li class="active">
25+
{{ __('tools/title.import_zone') }}
26+
</li>
27+
@endsection
28+
29+
{{-- Content --}}
30+
@section('content')
31+
<div class="box">
32+
<div class="box-header with-border">
33+
<h3 class="box-title">{{ __('tools/title.import_zone') }}</h3>
34+
</div>
35+
36+
<div class="box-body">
37+
<div class="form-group">
38+
<textarea class="form-control" rows="15" id="output" disabled>{{ $output }}</textarea>
39+
</div>
40+
</div>
41+
42+
<div class="box-footer">
43+
<a href="{{ route('home') }}" class="btn btn-primary pull-right" role="button">
44+
{{ __('general.done') }} <i class="fa fa-arrow-right"></i>
45+
</a>
46+
</div>
47+
48+
</div>
49+
@endsection
50+
51+
{{-- Scripts --}}
52+
@push('scripts')
53+
<script>
54+
$(function () {
55+
var $textarea = $('#output');
56+
$textarea.scrollTop($textarea[0].scrollHeight);
57+
});
58+
</script>
59+
@endpush
60+

routes/web.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@
105105
Route::get('tools/import',
106106
['as' => 'tools.import_zone', 'uses' => 'ToolsController@importZone']);
107107
Route::post('tools/import',
108-
['as' => 'tools.import_zone_post', 'uses' => 'ToolsController@importZonePost']);
108+
['as' => 'tools.import_zone_post', 'uses' => 'ToolsController@importZoneFromFile']);
109109
/* ------------------------------------------
110110
* Settings
111111
* ------------------------------------------

0 commit comments

Comments
 (0)