validate(['version' => 'required|integer|min:1', 'available' => 'required|boolean', 'evidence' => 'required|string|min:10|max:3000', 'contact' => 'nullable|string|max:255']); DB::transaction(function () use ($request, $id, $data) { $link = DB::table('desk_institutional_links')->where('id', $id)->lockForUpdate()->first(); abort_unless($link !== null, 404); abort_unless((int) $link->version === (int) $data['version'], 409); abort_unless(filter_var($link->url, FILTER_VALIDATE_URL) && parse_url($link->url, PHP_URL_SCHEME) === 'https', 422); DB::table('desk_institutional_links')->where('id', $id)->update(['status' => $data['available'] ? 'active' : 'unavailable', 'contact' => $data['contact'] ?? $link->contact, 'verified_at' => now(), 'verified_by' => $request->user()->id, 'version' => $link->version + 1, 'updated_at' => now()]); DB::table('audit_events')->insert(['source_system' => 'virtual-desk', 'entity_type' => 'desk_institutional_links', 'entity_id' => $id, 'actor_user_id' => $request->user()->id, 'action' => 'institutional_link_checked', 'after_data' => json_encode(['url' => $link->url, 'prior_version' => $link->version, ...$data, 'scope' => 'availability_only_not_legal_approval'])]); }); return $request->expectsJson() ? response()->json(['checked' => true]) : back()->with('success', 'Conferência de disponibilidade registada por sete dias.'); } public function sources(Request $request): JsonResponse|Response { $read = function (string $name): array { $file = storage_path('app/private/virtual-desk/'.$name.'.json'); if (! is_file($file)) { return []; } $bytes = file_get_contents($file); abort_unless(is_string($bytes), 503); return json_decode($bytes, true, 512, JSON_THROW_ON_ERROR); }; $inventory = $read('source-inventory'); $forms = $read('form-register'); $data = ['total' => count($inventory), 'attachmentCount' => array_sum(array_map(fn ($item) => count($item['attachments'] ?? []), $inventory)), 'forms' => array_map(fn ($item) => ['identifier' => $item['identifier'], 'sha256' => $item['sha256'], 'size' => $item['size'], 'edition' => $item['edition'], 'provenance' => str_contains($item['path'], DIRECTORY_SEPARATOR.'Scan'.DIRECTORY_SEPARATOR) ? 'Acervo disponibilizado — digitalização' : 'Acervo disponibilizado — fonte de desenho ou template', 'consultation' => $item['consultation'], 'legal_documentary_status' => $item['legal_documentary_status'], 'rule_use' => $item['rule_use'], 'official_confirmation' => $item['official_confirmation'], 'fields_status' => $item['fields_status']], $forms), 'source' => config('virtual-desk.source')]; return $request->expectsJson() ? response()->json($data) : Inertia::render('desk/sources', $data); } public function preview(Request $request, VirtualDeskInstallation $installation): JsonResponse|Response { $data = $installation->preview(); return $request->expectsJson() ? response()->json($data) : Inertia::render('desk/install', $data); } public function apply(Request $request, VirtualDeskInstallation $installation): JsonResponse|RedirectResponse { $data = $request->validate(['hash' => 'required|string|size:64', 'confirm' => 'accepted']); $installation->apply($data['hash'], $request->user()); return $request->expectsJson() ? response()->json(['installed' => true]) : back()->with('success', 'Instalação concluída; serviços ainda exigem publicação explícita.'); } public function publishService(Request $request): JsonResponse|RedirectResponse { $data = $request->validate(['code' => ['required', Rule::in(array_keys(config('virtual-desk.services')))], 'confirm' => 'accepted']); $row = DB::transaction(function () use ($request, $data) { DB::select('select pg_advisory_xact_lock(261008, 2)'); $definition = config('virtual-desk.services.'.$data['code']); $existing = DB::table('desk_service_versions')->where('code', $data['code'])->orderByDesc('version')->first(); if ($existing && $existing->status === 'published' && json_decode($existing->definition, true) === $definition) { return $existing; } $id = (string) Str::uuid(); DB::table('desk_service_versions')->insert(['id' => $id, 'code' => $data['code'], 'version' => ($existing->version ?? 0) + 1, 'status' => 'published', 'definition' => json_encode($definition), 'source' => json_encode(config('virtual-desk.source')), 'author_id' => $request->user()->id, 'published_by' => $request->user()->id, 'published_at' => now(), 'created_at' => now(), 'updated_at' => now()]); DB::table('audit_events')->insert(['source_system' => 'virtual-desk', 'entity_type' => 'desk_service_versions', 'entity_id' => $id, 'actor_user_id' => $request->user()->id, 'action' => 'internal_service_published', 'after_data' => json_encode(['code' => $data['code'], 'nature' => 'digital_preparation_not_official_form'])]); return DB::table('desk_service_versions')->where('id', $id)->first(); }); return $request->expectsJson() ? response()->json(['id' => $row->id], 201) : back()->with('success', 'Serviço de preparação interna publicado.'); } }