diff --git a/app/Filament/Admin/Resources/DnsSettingResource.php b/app/Filament/Admin/Resources/DnsSettingResource.php index 99fe7540..5d007a65 100644 --- a/app/Filament/Admin/Resources/DnsSettingResource.php +++ b/app/Filament/Admin/Resources/DnsSettingResource.php @@ -12,6 +12,8 @@ use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; +use Illuminate\Support\Facades\Storage; +use Symfony\Component\Process\Process; class DnsSettingResource extends Resource { @@ -26,12 +28,9 @@ public static function form(Form $form): Form Forms\Components\TextInput::make('domain_id') ->required() ->numeric(), - Forms\Components\Select::make('record_type') - ->options([ - 'A' => 'A', - 'MX' => 'MX', - ]) - ->required(), + Forms\Components\TextInput::make('record_type') + ->required() + ->maxLength(255), Forms\Components\TextInput::make('name') ->required() ->maxLength(255), @@ -41,6 +40,10 @@ public static function form(Form $form): Form Forms\Components\TextInput::make('ttl') ->required() ->numeric(), + Forms\Components\TextInput::make('priority') + ->numeric() + ->visibleIf('record_type', 'MX') + ->requiredIf('record_type', 'MX'), ]); } @@ -57,6 +60,9 @@ public static function table(Table $table): Table ->searchable(), Tables\Columns\TextColumn::make('value') ->searchable(), + Tables\Columns\TextColumn::make('priority') + ->numeric() + ->visibleIf('record_type', 'MX'), Tables\Columns\TextColumn::make('ttl') ->numeric() ->sortable(), @@ -97,4 +103,41 @@ public static function getPages(): array 'edit' => Pages\EditDnsSetting::route('/{record}/edit'), ]; } + + protected function updateBindDnsRecord(DnsSetting $dnsSetting): void + { + switch ($dnsSetting->record_type) { + case 'A': + $this->generateARecordEntry($dnsSetting); + break; + case 'MX': + $this->generateMxRecordEntry($dnsSetting); + break; + } + + $this->restartBindContainer(); + } + + protected function generateARecordEntry(DnsSetting $dnsSetting): void + { + $entry = "{$dnsSetting->name} IN A {$dnsSetting->value}"; + $zonePath = "/etc/bind/records/{$dnsSetting->domain->name}.db"; + + Storage::disk('bind')->append($zonePath, $entry); + } + + protected function generateMxRecordEntry(DnsSetting $dnsSetting): void + { + $entry = "{$dnsSetting->name} IN MX {$dnsSetting->priority} {$dnsSetting->value}"; + $zonePath = "/etc/bind/records/{$dnsSetting->domain->name}.db"; + + Storage::disk('bind')->append($zonePath, $entry); + } + + protected function restartBindContainer(): void + { + $process = new Process(['docker-compose', 'restart', 'bind9']); + $process->setWorkingDirectory(base_path()); + $process->run(); + } } diff --git a/app/Filament/Admin/Resources/DomainResource/Pages/CreateDomain.php b/app/Filament/Admin/Resources/DomainResource/Pages/CreateDomain.php index 77343cea..fa057fad 100644 --- a/app/Filament/Admin/Resources/DomainResource/Pages/CreateDomain.php +++ b/app/Filament/Admin/Resources/DomainResource/Pages/CreateDomain.php @@ -15,7 +15,15 @@ class CreateDomain extends CreateRecord protected function handleRecordCreation(array $data): Domain { - $domain = static::getModel()::create($data); + $user = auth()->user(); + if ($user->hasReachedDockerComposeLimit()) { + throw new \Exception('You have reached the limit of Docker Compose instances for your hosting plan.'); + } + + $domain = static::getModel()::create([ + ...$data, + 'hosting_plan_id' => $user->currentHostingPlan()->id, + ]); $composeContent = $this->generateDockerComposeContent($data); Storage::disk('local')->put('docker-compose-'.$data['domain_name'].'.yml', $composeContent); diff --git a/app/Filament/Admin/Resources/DomainResource/Pages/EditDomain.php b/app/Filament/Admin/Resources/DomainResource/Pages/EditDomain.php index 8cadb5d0..0d74234c 100644 --- a/app/Filament/Admin/Resources/DomainResource/Pages/EditDomain.php +++ b/app/Filament/Admin/Resources/DomainResource/Pages/EditDomain.php @@ -22,6 +22,11 @@ protected function getHeaderActions(): array protected function handleRecordUpdate(Domain $record, array $data): Domain { + $user = auth()->user(); + if ($user->hasReachedDockerComposeLimit()) { + throw new \Exception('You have reached the limit of Docker Compose instances for your hosting plan.'); + } + $record->update($data); $composeContent = $this->generateDockerComposeContent($data); diff --git a/app/Listeners/DnsSettingSavedListener.php b/app/Listeners/DnsSettingSavedListener.php new file mode 100644 index 00000000..f7993a2c --- /dev/null +++ b/app/Listeners/DnsSettingSavedListener.php @@ -0,0 +1,27 @@ +dnsSettingResource->updateBindDnsRecord($dnsSetting); + } +} \ No newline at end of file diff --git a/app/Models/DnsSetting.php b/app/Models/DnsSetting.php index 43298d9b..92dee598 100644 --- a/app/Models/DnsSetting.php +++ b/app/Models/DnsSetting.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Events\DnsSettingSaved; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -10,18 +9,6 @@ class DnsSetting extends Model { use HasFactory; - /** - * The "booted" method of the model. - * - * @return void - */ - protected static function booted() - { - static::saved(function ($dnsSetting) { - event(new DnsSettingSaved($dnsSetting)); - }); - } - /** * The table associated with the model. * @@ -40,15 +27,7 @@ protected static function booted() 'name', 'value', 'ttl', - ]; - - /** - * The validation rules for the model attributes. - * - * @var array - */ - public $rules = [ - 'record_type' => 'required|in:A,MX', + 'priority', ]; /** diff --git a/app/Models/Domain.php b/app/Models/Domain.php index 98d93b75..e2e6df72 100644 --- a/app/Models/Domain.php +++ b/app/Models/Domain.php @@ -26,6 +26,7 @@ class Domain extends Model 'domain_name', 'registration_date', 'expiration_date', + 'hosting_plan_id', ]; /** @@ -46,6 +47,14 @@ public function user() return $this->belongsTo(User::class); } + /** + * Get the hosting plan associated with the domain. + */ + public function hostingPlan() + { + return $this->belongsTo(UserHostingPlan::class); + } + /** * Get the email accounts for the domain. */ diff --git a/app/Models/User.php b/app/Models/User.php index 8e5b55ea..55cc08d8 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -98,6 +98,30 @@ public function resourceUsages() return $this->hasMany(ResourceUsage::class); } +public function hasReachedDockerComposeLimit(): bool +{ + $currentPlan = $this->currentHostingPlan(); + + if (!$currentPlan) { + return true; // Default to true if no hosting plan is found + } + + if ($currentPlan->name === 'free') { + return $this->domains()->count() >= 1; + } + + if ($currentPlan->name === 'premium') { + return false; // Unlimited instances for premium plan + } + + return true; // Default to true for other plans +} + +public function currentHostingPlan() +{ + return $this->userHostingPlans()->latest()->first(); +} + } diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 29ed54c5..4ec430ee 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -9,9 +9,6 @@ class EventServiceProvider extends ServiceProvider { - use App\Events\DnsSettingSaved; - use App\Listeners\UpdateBindDnsRecords; - /** * The event to listener mappings for the application. * @@ -21,8 +18,8 @@ class EventServiceProvider extends ServiceProvider Registered::class => [ SendEmailVerificationNotification::class, ], - DnsSettingSaved::class => [ - UpdateBindDnsRecords::class, + DnsSetting::class => [ + DnsSettingSavedListener::class, ], ]; diff --git a/database/migrations/2023_06_10_000000_add_hosting_plan_id_to_domains_table.php b/database/migrations/2023_06_10_000000_add_hosting_plan_id_to_domains_table.php new file mode 100644 index 00000000..09d66e9c --- /dev/null +++ b/database/migrations/2023_06_10_000000_add_hosting_plan_id_to_domains_table.php @@ -0,0 +1,30 @@ +id(); + $table->string('domain_name'); + $table->foreignId('user_id')->constrained()->onDelete('cascade'); + $table->foreignId('hosting_plan_id')->constrained('user_hosting_plans')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('domains'); + } +}; \ No newline at end of file diff --git a/database/migrations/2024_06_15_125614_add_priority_to_dns_settings_table.php b/database/migrations/2024_06_15_125614_add_priority_to_dns_settings_table.php new file mode 100644 index 00000000..84fd0733 --- /dev/null +++ b/database/migrations/2024_06_15_125614_add_priority_to_dns_settings_table.php @@ -0,0 +1,32 @@ +integer('priority')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('dns_settings', function (Blueprint $table) { + $table->dropColumn('priority'); + }); + } +} \ No newline at end of file diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index ae436a07..179848a6 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -5,6 +5,7 @@ use App\Models\User; // use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; +use Database\Seeders\HostingPlanSeeder; class DatabaseSeeder extends Seeder { @@ -19,5 +20,7 @@ public function run(): void 'name' => 'Test User', 'email' => 'test@example.com', ]); + + $this->call(HostingPlanSeeder::class); } } diff --git a/database/seeders/HostingPlanSeeder.php b/database/seeders/HostingPlanSeeder.php new file mode 100644 index 00000000..a78f6f13 --- /dev/null +++ b/database/seeders/HostingPlanSeeder.php @@ -0,0 +1,33 @@ + 'Free', + 'description' => 'Free hosting plan with limited resources', + 'disk_space' => 1000, // 1 GB + 'bandwidth' => 10000, // 10 GB + 'price' => 0.00, + ]); + + HostingPlan::create([ + 'name' => 'Premium', + 'description' => 'Premium hosting plan with enhanced resources', + 'disk_space' => 10000, // 10 GB + 'bandwidth' => 100000, // 100 GB + 'price' => 9.99, + ]); + } +} \ No newline at end of file