Files
services_core/app/Services/Nas/CustomerRouterOsProvisioningService.php
T
2026-08-07 09:16:13 +07:00

161 lines
6.0 KiB
PHP

<?php
namespace App\Services\Nas;
use App\Models\Customer;
use App\Models\CustomerNasAccount;
use App\Models\NasMikrotik;
use App\Models\NasPackageProfile;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
use Throwable;
use Illuminate\Support\Carbon;
class CustomerRouterOsProvisioningService
{
public function __construct(
protected RouterOsApiService $routerOs,
protected PackageProfileDeploymentService $deployments,
) {}
public function create(Customer $customer, array $data): CustomerNasAccount
{
[$mikrotik, $profile] = $this->resources($customer, $data);
if ($mikrotik->connection_type !== 'api') {
throw ValidationException::withMessages(['nas_mikrotik_id' => 'Tahap ini hanya mendukung Mikrotik dengan koneksi API.']);
}
$target = $this->deployments->assertSynced($profile, $mikrotik);
$username = trim((string) ($data['external_username'] ?? ''));
$password = (string) ($data['external_password'] ?? '');
if ($username === '' || $password === '') {
throw ValidationException::withMessages(['external_username' => 'Username dan password RouterOS wajib diisi.']);
}
$remote = $this->routerOs->createAccount($mikrotik, $profile->service_type, [
'username' => $username,
'password' => $password,
'profile' => $target->external_profile_name,
'comment' => $this->comment($customer),
'disabled' => 'no',
]);
try {
return DB::transaction(fn () => CustomerNasAccount::create([
'tenant_id' => $customer->tenant_id,
'customer_id' => $customer->id,
'nas_mikrotik_id' => $mikrotik->id,
'nas_package_profile_id' => $profile->id,
'nas_package_profile_target_id' => $target->id,
'service_type' => $profile->service_type,
'usage_mode' => $profile->usage_mode,
'username' => $username,
'password' => $password,
'mac_address' => $data['mac_address'] ?? null,
'serial_number' => $data['serial_number'] ?? null,
'assigned_ip' => $data['assigned_ip'] ?? null,
'remote_id' => $remote['.id'] ?? null,
'remote_profile' => $target->external_profile_name,
'status' => 'active',
'validity_started_at' => $profile->validity_start === 'activation' ? now() : null,
'expires_at' => $profile->validity_start === 'activation' ? $this->expiresAt($profile) : null,
'last_synced_at' => now(),
]));
} catch (Throwable $exception) {
try {
$this->routerOs->deleteAccount($mikrotik, $profile->service_type, $username);
} catch (Throwable) {
// Compensating delete is best effort; original database error remains authoritative.
}
throw $exception;
}
}
public function setEnabled(Customer $customer, bool $enabled): CustomerNasAccount
{
$account = $this->account($customer);
$remote = $this->routerOs->setEnabled(
$account->mikrotik,
$account->service_type,
$account->username,
$enabled,
);
$account->update([
'remote_id' => $remote['.id'] ?? $account->remote_id,
'status' => $enabled ? 'active' : 'disabled',
'last_synced_at' => now(),
'last_sync_error' => null,
]);
return $account->fresh();
}
public function update(Customer $customer, array $attributes): CustomerNasAccount
{
$account = $this->account($customer);
$remote = $this->routerOs->updateAccount(
$account->mikrotik,
$account->service_type,
$account->username,
$attributes,
);
$account->update([
'username' => $attributes['username'] ?? $account->username,
'password' => $attributes['password'] ?? $account->password,
'remote_profile' => $attributes['profile'] ?? $account->remote_profile,
'remote_id' => $remote['.id'] ?? $account->remote_id,
'last_synced_at' => now(),
'last_sync_error' => null,
]);
return $account->fresh();
}
public function delete(Customer $customer): void
{
$account = $customer->nasAccount()->with('mikrotik')->first();
if (! $account) {
return;
}
$this->routerOs->deleteAccount($account->mikrotik, $account->service_type, $account->username);
$account->delete();
}
private function resources(Customer $customer, array $data): array
{
$mikrotik = NasMikrotik::whereKey($data['nas_mikrotik_id'])
->where('tenant_id', $customer->tenant_id)->firstOrFail();
$profile = NasPackageProfile::whereKey($data['package_profile_id'])
->where('tenant_id', $customer->tenant_id)->firstOrFail();
return [$mikrotik, $profile];
}
private function account(Customer $customer): CustomerNasAccount
{
$account = $customer->nasAccount()->with('mikrotik')->first();
if (! $account) {
throw ValidationException::withMessages(['nas_account' => 'Akun RouterOS customer belum tercatat.']);
}
return $account;
}
private function comment(Customer $customer): string
{
return "ManjaPro | {$customer->customer_code} | {$customer->name}";
}
private function expiresAt(NasPackageProfile $profile): ?Carbon
{
if (! $profile->validity_value || ! $profile->validity_unit) return null;
return match ($profile->validity_unit) {
'minutes' => now()->addMinutes($profile->validity_value),
'hours' => now()->addHours($profile->validity_value),
'days' => now()->addDays($profile->validity_value),
'months' => now()->addMonths($profile->validity_value),
default => null,
};
}
}