path($publicDiskPath); if (! is_file($source)) { Log::warning("Site asset sync skipped (source missing): {$publicDiskPath}"); return null; } $prefix = config('services.nextjs.public_upload_prefix', 'uploads'); $prefix = trim((string) $prefix, '/'); if ($prefix === '') { $prefix = 'uploads'; } $relativeDest = $prefix.'/'.$publicDiskPath; $dest = rtrim($nextPublicRoot, '/').'/'.$relativeDest; $destDir = dirname($dest); if (! is_dir($destDir) && ! @mkdir($destDir, 0755, true) && ! is_dir($destDir)) { Log::warning("Site asset sync failed (mkdir): {$destDir}"); return null; } if (! @copy($source, $dest)) { Log::warning("Site asset sync failed (copy): {$publicDiskPath} -> {$dest}"); return null; } // Optionally commit+push the updated assets into the usher-site repo. NextjsRepoSyncService::scheduleAssetsPush(); return $relativeDest; } /** * Delete a previously synced Next.js public asset (best-effort). */ public static function deleteNextPublicFile(?string $nextPublicRelativePath): void { if (! $nextPublicRelativePath) { return; } $nextPublicRoot = static::resolveNextPublicRoot(); if (! $nextPublicRoot) { return; } $rel = ltrim($nextPublicRelativePath, '/'); $path = rtrim($nextPublicRoot, '/').'/'.$rel; if (is_file($path)) { @unlink($path); } // Stage deletion via scheduled push (best-effort). NextjsRepoSyncService::scheduleAssetsPush(); } private static function resolveNextPublicRoot(): ?string { $configured = config('services.nextjs.public_path'); if (is_string($configured) && $configured !== '' && is_dir($configured)) { return $configured; } // Local-dev default: sibling repo next to this Laravel repo. $guess = base_path('../usher-site/public'); if (is_dir($guess)) { return $guess; } return null; } }