big update all menus

This commit is contained in:
Wian Drs
2026-08-07 09:16:13 +07:00
parent 75342dc7b7
commit e7575ba1c0
101 changed files with 3451 additions and 89 deletions
@@ -0,0 +1,93 @@
<?php
namespace App\Services\Nas;
use App\Models\NasMikrotik;
use App\Models\NasPackageProfile;
use App\Models\NasPackageProfileTarget;
use Illuminate\Validation\ValidationException;
use Throwable;
class PackageProfileDeploymentService
{
public function __construct(protected RouterOsApiService $routerOs) {}
public function targets(NasPackageProfile $profile)
{
return $profile->targets()->with('mikrotik:id,tenant_id,name,connection_type,host,connection_status')->orderBy('id')->get();
}
public function saveTarget(NasPackageProfile $profile, array $data): NasPackageProfileTarget
{
$nas = NasMikrotik::with('tenant.nasSetting')->whereKey($data['nas_mikrotik_id'])->where('tenant_id', $profile->tenant_id)->firstOrFail();
$backendMode = $nas->effectiveBackendMode();
return NasPackageProfileTarget::updateOrCreate(
['nas_package_profile_id' => $profile->id, 'nas_mikrotik_id' => $nas->id],
['tenant_id' => $profile->tenant_id, 'backend_type' => in_array($backendMode, ['platform_radius','external_radius','hybrid'], true) ? 'radius' : 'routeros_api',
'external_profile_name' => $data['external_profile_name'], 'sync_status' => 'not_deployed']
);
}
public function sync(NasPackageProfileTarget $target, string $mode): NasPackageProfileTarget
{
$target->load(['packageProfile.scripts.versions', 'mikrotik']);
$desired = $this->desiredSnapshot($target->packageProfile);
try {
$remote = $target->backend_type === 'routeros_api'
? (in_array($target->packageProfile->service_type, ['static_ip', 'dynamic_ip'], true)
? ['managed_per_customer' => true, 'network' => $desired['network']]
: $this->routerOs->syncProfile($target->mikrotik, $target->packageProfile->service_type, $target->external_profile_name, $desired['routeros'], $mode))
: ['radius_attributes' => $desired['radius']];
$checksum = hash('sha256', json_encode($desired));
$target->update(['sync_status' => 'synced', 'desired_snapshot' => $desired, 'remote_snapshot' => $remote,
'remote_id' => $remote['.id'] ?? null, 'deployed_checksum' => $checksum, 'last_checked_at' => now(), 'last_synced_at' => now(), 'last_error' => null]);
} catch (Throwable $e) {
$target->update(['sync_status' => 'error', 'desired_snapshot' => $desired, 'last_checked_at' => now(), 'last_error' => $e instanceof ValidationException ? collect($e->errors())->flatten()->first() : $e->getMessage()]);
throw $e;
}
return $target->fresh('mikrotik');
}
public function assertSynced(NasPackageProfile $profile, NasMikrotik $nas): NasPackageProfileTarget
{
$target = $profile->targets()->where('nas_mikrotik_id', $nas->id)->where('sync_status', 'synced')->first();
if (! $target) throw ValidationException::withMessages(['package_profile_id' => 'Profile paket belum disinkronkan ke NAS yang dipilih.']);
return $target;
}
private function desiredSnapshot(NasPackageProfile $profile): array
{
$scripts = $profile->scripts->keyBy(fn ($policy) => $policy->pivot->event);
$script = fn (string $event) => $this->scriptTemplate($scripts->get($event), $profile);
$rate = ($profile->upload_kbps ? ($profile->upload_kbps.'k') : '0').'/'.($profile->download_kbps ? ($profile->download_kbps.'k') : '0');
$common = ['rate-limit' => $rate, 'session-timeout' => $profile->session_timeout ? $profile->session_timeout.'s' : null,
'idle-timeout' => $profile->idle_timeout ? $profile->idle_timeout.'s' : null];
$router = $profile->service_type === 'hotspot'
? [...$common, 'shared-users' => (string) $profile->shared_users, 'on-login' => $script('on_login'), 'on-logout' => $script('on_logout')]
: ($profile->service_type === 'ppp'
? [...$common, 'local-address' => $profile->local_address, 'remote-address' => $profile->remote_address_pool, 'on-up' => $script('on_up'), 'on-down' => $script('on_down')]
: []);
return ['routeros' => $router, 'radius' => ['Mikrotik-Rate-Limit' => $rate, 'Session-Timeout' => $profile->session_timeout, 'Idle-Timeout' => $profile->idle_timeout, 'Simultaneous-Use' => $profile->shared_users],
'network' => $profile->only(['mac_lock_required','ip_pool','subnet_cidr','gateway','dns_servers','vlan_id']),
'profile' => $profile->only(['profile_code','service_type','usage_mode','validity_start','validity_value','validity_unit','uptime_limit_seconds','data_limit_bytes'])];
}
private function scriptTemplate($policy, NasPackageProfile $profile): ?string
{
if (! $policy || ! $policy->pivot->enabled) return null;
$versionId = $policy->pivot->nas_script_policy_version_id;
$version = $versionId
? $policy->versions->firstWhere('id', $versionId)
: $policy->versions->sortByDesc('version')->first();
if (! $version) return null;
return strtr($version->script_template, [
'{{ username }}' => '$user',
'{{username}}' => '$user',
'{{ package_code }}' => (string) $profile->profile_code,
'{{package_code}}' => (string) $profile->profile_code,
'{{ tenant_code }}' => (string) optional($profile->tenant)->tenant_code,
'{{tenant_code}}' => (string) optional($profile->tenant)->tenant_code,
]);
}
}