svc = $svc; $this->jobs = $jobs; $this->points = $points; } public function register(): void { $this->route('/exports', 'GET', fn () => $this->exports()); $this->route('/exports/(?P[0-9a-f-]{36})', 'DELETE', function (\WP_REST_Request $r) { $this->svc->delete_export($r['id']); return ['deleted' => true]; }); $this->route('/exports/(?P[0-9a-f-]{36})/download', 'GET', function (\WP_REST_Request $r) { $info = $this->download_info($r['id']); Stream::send($info['path'], $info['filename'], (string) $r->get_header('range')); return null; }); $this->route('/restore-points', 'GET', fn () => $this->points->list()); $this->route('/restore-points/(?P[0-9a-f-]{36})', 'DELETE', function (\WP_REST_Request $r) { $this->points->delete($this->svc->find($r['id'])); return ['deleted' => true]; }); } /** @return array{path: string, filename: string} */ public function download_info(string $id): array { $job = $this->svc->find($id); if ($job->type !== 'export' || empty($job->stats['file']['ready']) || !is_file(ExportStartStep::path($job))) { throw new RelayError('export.not_ready', esc_html__('This export is not available for download.', 'siterelay')); } $host = (string) wp_parse_url(home_url(), PHP_URL_HOST); $stamp = gmdate('Ymd-His', (int) strtotime($job->created_at . ' UTC')); $ext = !empty($job->stats['file']['gzip']) ? '.sql.gz' : '.sql'; return ['path' => ExportStartStep::path($job), 'filename' => Stream::file_name($host . '-' . $stamp) . $ext]; } private function exports(): array { $out = []; foreach ($this->jobs->list(['type' => 'export', 'status' => Job::COMPLETED], 1, 100)['items'] as $job) { if (!empty($job->stats['file']['ready'])) { $out[] = [ 'job_id' => $job->id, 'created_at' => $job->created_at, 'size' => (int) ($job->stats['file']['size'] ?? 0), 'gzip' => !empty($job->stats['file']['gzip']), 'tables' => count($job->settings['tables'] ?? []), ]; } } return $out; } }