161 lines
6.0 KiB
PHP
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,
|
|
};
|
|
}
|
|
}
|