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

151 lines
6.9 KiB
PHP

<?php
namespace App\Services\Nas;
use App\Models\NasMikrotik;
use App\Models\NasOlt;
use App\Models\NasPackageProfile;
use App\Models\NasWebfigDevice;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Validation\ValidationException;
use App\Models\NasScriptPolicy;
class NasResourceService
{
private const MODELS = [
'mikrotik' => NasMikrotik::class,
'package-profile' => NasPackageProfile::class,
'olt' => NasOlt::class,
'webfig' => NasWebfigDevice::class,
];
private const SECRET_FIELDS = [
'mikrotik' => ['api_password', 'radius_secret'],
'olt' => ['community', 'auth_password', 'privacy_password'],
'webfig' => ['password'],
'package-profile' => [],
];
public function list(string $resource, array $filters, User $actor, ?int $tenantId)
{
$model = $this->model($resource);
return $model::query()
->with(['tenant:id,tenant_code,tenant_name', ...($resource === 'mikrotik' ? ['tenant.nasSetting'] : []), ...($resource === 'package-profile' ? ['scripts.latestVersion'] : [])])
->when(! $actor->isMasterAdmin(), fn ($q) => $q->where('tenant_id', $tenantId))
->when($actor->isMasterAdmin() && $tenantId, fn ($q) => $q->where('tenant_id', $tenantId))
->when(! empty($filters['status']), fn ($q) => $q->where('status', $filters['status']))
->when(! empty($filters['connection_type']) && $resource === 'mikrotik', fn ($q) => $q->where('connection_type', $filters['connection_type']))
->when(! empty($filters['service_type']) && $resource === 'package-profile', fn ($q) => $q->where('service_type', $filters['service_type']))
->when(! empty($filters['search']), function ($q) use ($filters, $resource) {
$columns = match ($resource) {
'mikrotik' => ['name', 'host', 'api_username'],
'package-profile' => ['name', 'profile_code'],
'olt' => ['name', 'host', 'vendor', 'model'],
'webfig' => ['name', 'url', 'device_type'],
};
$q->where(function ($search) use ($columns, $filters) {
foreach ($columns as $index => $column) {
$method = $index === 0 ? 'where' : 'orWhere';
$search->{$method}($column, 'ILIKE', "%{$filters['search']}%");
}
});
})
->orderBy($filters['sort_by'] ?? 'id', $filters['sort_direction'] ?? 'desc')
->paginate($filters['per_page'] ?? 10);
}
public function find(string $resource, int $id): Model
{
$model = $this->model($resource);
return $model::with(['tenant:id,tenant_code,tenant_name', ...($resource === 'mikrotik' ? ['tenant.nasSetting'] : []), ...($resource === 'package-profile' ? ['scripts.latestVersion'] : [])])->findOrFail($id);
}
public function create(string $resource, array $data, User $actor, ?int $tenantId): Model
{
if ($resource === 'mikrotik') $data['connection_type'] = 'api';
if ($resource === 'package-profile') $data = $this->normalizePackageProfile($data);
$scripts = $resource === 'package-profile' ? ($data['scripts'] ?? []) : [];
unset($data['scripts']);
$data['tenant_id'] = $actor->isMasterAdmin() ? ($data['tenant_id'] ?? $tenantId) : $tenantId;
if (! $data['tenant_id']) {
throw ValidationException::withMessages(['tenant_id' => 'Tenant wajib dipilih.']);
}
$data['status'] ??= 'active';
$model = $this->model($resource);
$created = $model::create($data);
if ($resource === 'package-profile') $this->syncProfileScripts($created, $scripts);
return $created->load('tenant:id,tenant_code,tenant_name');
}
public function update(string $resource, Model $model, array $data, User $actor): Model
{
if ($resource === 'mikrotik') $data['connection_type'] = 'api';
if ($resource === 'package-profile') $data = $this->normalizePackageProfile($data, $model);
$hasScripts = $resource === 'package-profile' && array_key_exists('scripts', $data);
$scripts = $data['scripts'] ?? [];
unset($data['scripts']);
if (! $actor->isMasterAdmin()) {
unset($data['tenant_id']);
}
foreach (self::SECRET_FIELDS[$resource] as $field) {
if (array_key_exists($field, $data) && blank($data[$field])) {
unset($data[$field]);
}
}
$model->update($data);
if ($hasScripts) $this->syncProfileScripts($model, $scripts);
return $model->fresh()->load('tenant:id,tenant_code,tenant_name');
}
private function syncProfileScripts(Model $profile, array $scripts): void
{
$sync=[];
foreach($scripts as $row) {
$policy = NasScriptPolicy::with('versions:id,nas_script_policy_id')->findOrFail($row['policy_id']);
if ($policy->service_type !== $profile->service_type || $policy->event !== $row['event']) {
throw ValidationException::withMessages(['scripts' => "Script {$policy->name} tidak sesuai tipe layanan atau event profile."]);
}
if (($row['version_id'] ?? null) && ! $policy->versions->contains('id', $row['version_id'])) {
throw ValidationException::withMessages(['scripts' => "Versi Script {$policy->name} tidak valid."]);
}
$sync[$row['policy_id']]=['event'=>$row['event'],'nas_script_policy_version_id'=>$row['version_id'] ?? null,'enabled'=>true];
}
$profile->scripts()->sync($sync);
}
private function normalizePackageProfile(array $data, ?Model $profile = null): array
{
$type = $data['service_type'] ?? $profile?->service_type;
if ($type !== 'hotspot') {
foreach (['usage_mode','validity_start','validity_value','validity_unit','uptime_limit_seconds','data_limit_bytes','grace_period_minutes'] as $field) $data[$field] = null;
}
if ($type !== 'ppp') foreach (['local_address', 'remote_address_pool'] as $field) $data[$field] = null;
if ($type !== 'hotspot') foreach (['session_timeout', 'idle_timeout', 'shared_users'] as $field) $data[$field] = null;
if (! in_array($type, ['static_ip', 'dynamic_ip'], true)) {
foreach (['ip_pool','subnet_cidr','gateway','dns_servers','vlan_id'] as $field) $data[$field] = null;
$data['mac_lock_required'] = false;
} else {
$data['mac_lock_required'] = true;
if ($type === 'static_ip') $data['ip_pool'] = null;
}
return $data;
}
public function delete(Model $model): bool
{
return $model->delete();
}
private function model(string $resource): string
{
abort_unless(isset(self::MODELS[$resource]), 404);
return self::MODELS[$resource];
}
}