authorizeWrite('settings'); $repositories = StorageRepository::orderBy('ordine')->get(); if ($request->ajax()) { return response()->json($repositories); } return view('storage-repositories.index', compact('repositories')); } public function store(Request $request): RedirectResponse { $this->authorizeWrite('settings'); $validated = $request->validate([ 'nome' => 'required|string|max:255', 'tipo' => 'required|string|in:webdav,google_drive', 'config' => 'required|array', 'is_active' => 'boolean', 'ordine' => 'nullable|integer|min:0', ]); $maxOrdine = StorageRepository::max('ordine') ?? 0; $validated['ordine'] ??= $maxOrdine + 1; $validated['config'] = $this->repoService->encryptSensitiveConfig($validated['config'], $validated['tipo']); StorageRepository::create($validated); return redirect('/impostazioni#repository')->with('success', 'Repository creato.'); } public function update(Request $request, StorageRepository $storageRepository): RedirectResponse { $this->authorizeWrite('settings'); $validated = $request->validate([ 'nome' => 'required|string|max:255', 'tipo' => 'required|string|in:webdav,google_drive', 'config' => 'required|array', 'is_active' => 'boolean', 'ordine' => 'nullable|integer|min:0', ]); $validated['config'] = $this->repoService->encryptSensitiveConfig( $validated['config'], $validated['tipo'], $storageRepository->config ); $storageRepository->update($validated); return redirect('/impostazioni#repository')->with('success', 'Repository aggiornato.'); } public function destroy(StorageRepository $storageRepository): JsonResponse|RedirectResponse { $this->authorizeDelete('settings'); $docCount = $storageRepository->documenti()->count(); if ($docCount > 0) { $message = "Impossibile eliminare: {$docCount} documenti associati a questo repository."; if (request()->expectsJson()) { return response()->json(['success' => false, 'message' => $message], 409); } return redirect('/impostazioni#repository')->with('error', $message); } $storageRepository->delete(); if (request()->expectsJson()) { return response()->json(['success' => true]); } return redirect('/impostazioni#repository')->with('success', 'Repository eliminato.'); } public function test(StorageRepository $storageRepository): JsonResponse { $this->authorizeWrite('settings'); $result = $this->repoService->testConnection($storageRepository); return response()->json($result); } public function browse(Request $request, StorageRepository $storageRepository): JsonResponse { $this->authorizeRead('documenti'); $path = $request->get('path', '/'); $contents = $this->repoService->listContents($storageRepository, $path); return response()->json([ 'success' => true, 'contents' => $contents, 'path' => $path, 'repository' => [ 'id' => $storageRepository->id, 'nome' => $storageRepository->nome, 'tipo' => $storageRepository->tipo, ], ]); } public function reorder(Request $request): JsonResponse { $this->authorizeWrite('settings'); $order = $request->input('order', []); foreach ($order as $index => $id) { StorageRepository::where('id', $id)->update(['ordine' => $index]); } return response()->json(['success' => true]); } public function downloadRemote(StorageRepository $storageRepository, Request $request): StreamedResponse { $this->authorizeRead('documenti'); $path = $request->get('path'); if (!$path) { abort(400, 'Path required'); } $filesystem = $this->repoService->buildFilesystem($storageRepository); if (!$filesystem || !$filesystem->fileExists($path)) { abort(404); } $stream = $filesystem->readStream($path); if (!$stream) { abort(404); } $mimeType = $filesystem->mimeType($path) ?? 'application/octet-stream'; $basename = basename($path); return response()->stream(function () use ($stream) { fpassthru($stream); fclose($stream); }, 200, [ 'Content-Type' => $mimeType, 'Content-Disposition' => 'attachment; filename="' . $basename . '"', ]); } public function previewRemote(StorageRepository $storageRepository, Request $request): StreamedResponse { $this->authorizeRead('documenti'); $path = $request->get('path'); if (!$path) { abort(400, 'Path required'); } $filesystem = $this->repoService->buildFilesystem($storageRepository); if (!$filesystem || !$filesystem->fileExists($path)) { abort(404); } $stream = $filesystem->readStream($path); if (!$stream) { abort(404); } $mimeType = $filesystem->mimeType($path) ?? 'application/octet-stream'; $basename = basename($path); return response()->stream(function () use ($stream) { fpassthru($stream); fclose($stream); }, 200, [ 'Content-Type' => $mimeType, 'Content-Disposition' => 'inline; filename="' . $basename . '"', ]); } public function oauthRedirect(Request $request): RedirectResponse { $this->authorizeWrite('settings'); $clientId = $request->get('client_id'); $clientSecret = $request->get('client_secret'); $repoId = (int) $request->get('repo_id', 0); if (empty($clientId) || empty($clientSecret)) { return redirect('/impostazioni#repository')->with('error', 'Inserisci Client ID e Client Secret prima di autorizzare.'); } session(['google_drive_oauth' => [ 'client_id' => $clientId, 'client_secret' => $clientSecret, 'repo_id' => $repoId, 'nome' => $request->get('nome', ''), 'is_active' => $request->get('is_active', '1'), 'root_folder_id' => $request->get('root_folder_id', 'root'), ]]); $client = new \Google_Client(); $client->setClientId($clientId); $client->setClientSecret($clientSecret); $client->setRedirectUri(url('/auth/google-drive/callback')); $client->addScope(\Google_Service_Drive::DRIVE); $client->setAccessType('offline'); $client->setPrompt('consent'); $authUrl = $client->createAuthUrl(); return redirect()->away($authUrl); } public function oauthCallback(Request $request): RedirectResponse { $this->authorizeWrite('settings'); if ($request->get('error')) { return redirect('/impostazioni#repository')->with('error', 'Autorizzazione Google Drive annullata o fallita.'); } $code = $request->get('code'); if (!$code) { return redirect('/impostazioni#repository')->with('error', 'Codice di autorizzazione mancante.'); } $oauthData = session('google_drive_oauth'); if (!$oauthData) { return redirect('/impostazioni#repository')->with('error', 'Sessione scaduta. Riprova l\'autorizzazione.'); } try { $client = new \Google_Client(); $client->setClientId($oauthData['client_id']); $client->setClientSecret($oauthData['client_secret']); $client->setRedirectUri(url('/auth/google-drive/callback')); $client->addScope(\Google_Service_Drive::DRIVE); $token = $client->fetchAccessTokenWithAuthCode($code); if (isset($token['error'])) { throw new \Exception($token['error_description'] ?? $token['error']); } $refreshToken = $client->getRefreshToken(); if (!$refreshToken) { throw new \Exception('Refresh token non ricevuto. Assicurati che l\'app OAuth sia configurata con access_type=offline e prompt=consent nella Google Cloud Console.'); } $repoId = $oauthData['repo_id']; session()->forget('google_drive_oauth'); if ($repoId > 0) { $repo = StorageRepository::find($repoId); if ($repo) { $config = $repo->config; $config['client_id'] = $oauthData['client_id']; $config['client_secret'] = $oauthData['client_secret']; $config['refresh_token'] = $refreshToken; $config = $this->repoService->encryptSensitiveConfig($config, 'google_drive'); $repo->update(['config' => $config]); return redirect('/impostazioni#repository')->with('success', 'Google Drive autorizzato con successo! Refresh token salvato.'); } return redirect('/impostazioni#repository')->with('error', 'Repository non trovato (ID: ' . $repoId . ').'); } session()->flash('google_drive_new_token', [ 'refresh_token' => $refreshToken, 'client_id' => $oauthData['client_id'], 'client_secret' => $oauthData['client_secret'], 'nome' => $oauthData['nome'] ?? '', 'is_active' => $oauthData['is_active'] ?? '1', 'root_folder_id' => $oauthData['root_folder_id'] ?? 'root', ]); return redirect('/impostazioni#repository')->with('success', 'Autorizzazione Google Drive completata! Ora completa la creazione del repository e salva.'); } catch (\Exception $e) { return redirect('/impostazioni#repository')->with('error', 'Errore autorizzazione Google Drive: ' . $e->getMessage()); } } }