ensureTenantAccount(); abort_unless($request->user()->can('device.view'), 403); $devices = Device::query()->with(['vendor:id,name', 'type:id,name', 'model:id,name', 'creator:id,name'])->withCount('userAssignments') ->when($request->string('search')->isNotEmpty(), fn ($query) => $query->where(fn ($nested) => $nested ->where('name', 'ilike', '%'.$request->string('search').'%') ->orWhere('management_address', 'ilike', '%'.$request->string('search').'%') ->orWhere('location', 'ilike', '%'.$request->string('search').'%'))) ->when($request->integer('vendor'), fn ($query, $vendor) => $query->where('device_vendor_id', $vendor)) ->when($request->string('status')->isNotEmpty(), fn ($query) => $query->where('status', $request->string('status'))) ->latest()->paginate(15)->withQueryString(); return Inertia::render('devices/index', ['devices' => $devices, 'vendors' => DeviceVendor::where('is_active', true)->get(['id', 'name']), 'filters' => $request->only('search', 'vendor', 'status'), 'policy' => $this->policy(), 'isTenantAdmin' => $request->user()->hasRole(SystemRole::TenantAdmin->value)]); } /** * Show the form for creating a new resource. */ public function create(): Response { $this->authorizeCreate(); return Inertia::render('devices/form', $this->catalog() + ['device' => null]); } /** * Store a newly created resource in storage. */ public function store(StoreDeviceRequest $request): RedirectResponse { $this->authorizeCreate(); app(LicenseEntitlementService::class)->assertCanAddDevice($request->user()->tenant_id); $this->validateModelCombination($request); $data = $request->validated(); $username = $data['initial_username']; $password = $data['initial_password'] ?? ''; $enablePassword = $data['initial_enable_password'] ?? null; unset($data['initial_username'], $data['initial_password'], $data['initial_enable_password']); DB::transaction(function () use ($data, $username, $password, $enablePassword, $request): void { $device = Device::create($data + ['tenant_id' => $request->user()->tenant_id, 'created_by' => $request->user()->id, 'updated_by' => $request->user()->id]); DeviceCredential::create([ 'tenant_id' => $request->user()->tenant_id, 'device_id' => $device->id, 'name' => 'Login Awal', 'username' => $username, 'password' => $password, 'enable_password' => $enablePassword, 'connection_type' => $device->connection_type, 'privilege_type' => 'master', 'is_master' => true, 'is_active' => true, 'created_by' => $request->user()->id, 'updated_by' => $request->user()->id, ]); }); return to_route('devices.index')->with('success', 'Perangkat berhasil ditambahkan.'); } /** * Display the specified resource. */ public function show(Device $device): Response { $this->ensureOwnedDevice($device); abort_unless(request()->user()->can('device.view'), 403); return Inertia::render('devices/show', [ 'device' => $device->load(['vendor:id,name,slug', 'type:id,name', 'model:id,name', 'creator:id,name']), 'credentials' => request()->user()->can('device.credential.view') ? $device->credentials()->get(['id', 'name', 'username', 'connection_type', 'privilege_type', 'is_master', 'is_active', 'last_verified_at']) : [], ]); } /** * Show the form for editing the specified resource. */ public function edit(Device $device): Response { $this->ensureOwnedDevice($device); $this->authorizeUpdate($device); return Inertia::render('devices/form', $this->catalog() + ['device' => $device]); } /** * Update the specified resource in storage. */ public function update(UpdateDeviceRequest $request, Device $device): RedirectResponse { $this->ensureOwnedDevice($device); $this->authorizeUpdate($device); $this->validateModelCombination($request); $device->update($request->validated() + ['updated_by' => $request->user()->id]); return to_route('devices.show', $device)->with('success', 'Perangkat berhasil diperbarui.'); } /** * Remove the specified resource from storage. */ public function destroy(Device $device): RedirectResponse { $this->ensureOwnedDevice($device); abort_unless(request()->user()->can('device.delete'), 403); abort_if(request()->user()->hasRole(SystemRole::TenantUser->value) && (! $this->policy()->tenant_user_can_delete_own || $device->created_by !== request()->user()->id), 403); $device->delete(); return to_route('devices.index')->with('success', 'Perangkat berhasil dihapus.'); } private function catalog(): array { return ['vendors' => DeviceVendor::where('is_active', true)->get(['id', 'name', 'slug']), 'types' => DeviceType::where('is_active', true)->get(['id', 'name']), 'models' => DeviceModel::where('is_active', true)->get(['id', 'name', 'device_vendor_id', 'device_type_id'])]; } private function policy(): TenantDevicePolicy { return TenantDevicePolicy::firstOrCreate(['tenant_id' => request()->user()->tenant_id]); } private function ensureTenantAccount(): void { abort_if(request()->user()->is_platform_admin || ! request()->user()->tenant_id, 403); } private function ensureOwnedDevice(Device $device): void { $this->ensureTenantAccount(); abort_unless($device->tenant_id === request()->user()->tenant_id, 404); } private function authorizeCreate(): void { $this->ensureTenantAccount(); abort_unless(request()->user()->can('device.create'), 403); abort_if(request()->user()->hasRole(SystemRole::TenantUser->value) && ! $this->policy()->tenant_user_can_create, 403); } private function authorizeUpdate(Device $device): void { abort_unless(request()->user()->can('device.update'), 403); abort_if(request()->user()->hasRole(SystemRole::TenantUser->value) && (! $this->policy()->tenant_user_can_update_own || $device->created_by !== request()->user()->id), 403); } private function validateModelCombination(Request $request): void { if ($request->filled('device_model_id')) { abort_unless(DeviceModel::whereKey($request->integer('device_model_id'))->where('device_vendor_id', $request->integer('device_vendor_id'))->where('device_type_id', $request->integer('device_type_id'))->exists(), 422, 'Model tidak sesuai dengan vendor dan tipe perangkat.'); } } }