| @@ -10,12 +10,13 @@ | ||
| 10 | 10 | */ |
| 11 | 11 | declare (strict_types=1); |
| 12 | 12 | namespace WindPress\WindPress\Api\Admin; |
| 13 | 13 | |
| 14 | -use WindPressDeps\Symfony\Component\Finder\Finder; | |
| 15 | -use WindPressDeps\Symfony\Component\Finder\Glob; | |
| 16 | 14 | use WindPress\WindPress\Api\AbstractApi; |
| 17 | 15 | use WindPress\WindPress\Api\ApiInterface; |
| 16 | +use WindPress\WindPress\Core\Scanner\FileScanner; | |
| 17 | +use WindPress\WindPress\Core\Scanner\SourceIndex; | |
| 18 | +use WindPress\WindPress\Core\Scanner\SourceRevision; | |
| 18 | 19 | use WP_REST_Request; |
| 19 | 20 | use WP_REST_Response; |
| 20 | 21 | use WP_REST_Server; |
| 21 | 22 | class LocalFileProvider extends AbstractApi implements ApiInterface |
| @@ -33,17 +34,46 @@ | ||
| 33 | 34 | } |
| 34 | 35 | public function scan(WP_REST_Request $wprestRequest): WP_REST_Response |
| 35 | 36 | { |
| 36 | 37 | $payload = $wprestRequest->get_json_params(); |
| 37 | - $path = $payload['path']; | |
| 38 | - $contents = []; | |
| 39 | - $finder = new Finder(); | |
| 40 | - $finder->ignoreUnreadableDirs()->in(\WP_CONTENT_DIR)->files()->followLinks()->path(Glob::toRegex($path)); | |
| 41 | - foreach ($finder as $file) { | |
| 42 | - if (!is_readable($file->getPathname())) { | |
| 43 | - continue; | |
| 38 | + try { | |
| 39 | + if (!is_array($payload)) { | |
| 40 | + throw new \InvalidArgumentException(__('A file scan requires an object payload.', 'windpress')); | |
| 44 | 41 | } |
| 45 | - $contents[] = ['name' => $file->getFilename(), 'relative_path' => $file->getRelativePathname(), 'content' => $file->getContents()]; | |
| 42 | + $patterns = $payload['patterns'] ?? (isset($payload['path']) ? [$payload['path']] : []); | |
| 43 | + $exclude_patterns = $payload['exclude_patterns'] ?? []; | |
| 44 | + if (!is_array($patterns) || $patterns === [] || count($patterns) > 100 || !is_array($exclude_patterns) || count($exclude_patterns) > 100) { | |
| 45 | + throw new \InvalidArgumentException(__('Provide between one and 100 source patterns and at most 100 exclusions.', 'windpress')); | |
| 46 | + } | |
| 47 | + $batched = $payload['batch'] ?? \false; | |
| 48 | + if (!is_bool($batched)) { | |
| 49 | + throw new \InvalidArgumentException(__('The file scan batch option must be a boolean.', 'windpress')); | |
| 50 | + } | |
| 51 | + if (array_key_exists('source_index', $payload)) { | |
| 52 | + if (!$batched) { | |
| 53 | + throw new \InvalidArgumentException(__('Indexed file scans require batching.', 'windpress')); | |
| 54 | + } | |
| 55 | + $source_revision = array_key_exists('source_revision', $payload) ? $payload['source_revision'] : SourceRevision::get(); | |
| 56 | + if (!is_string($source_revision) || $source_revision === '') { | |
| 57 | + throw new \InvalidArgumentException(__('The source revision must be a nonempty string.', 'windpress')); | |
| 58 | + } | |
| 59 | + if (!SourceRevision::matches($source_revision)) { | |
| 60 | + throw new \RuntimeException(__('Source data changed. Restart the scan.', 'windpress'), 409); | |
| 61 | + } | |
| 62 | + $metadata = ['next_batch' => $payload['cursor'] ?? \false, 'source_index' => $payload['source_index'], 'source_revision' => $source_revision, 'kind' => $payload['kind'] ?? 'full']; | |
| 63 | + $result = SourceIndex::scan('local:' . FileScanner::local_scope(\WP_CONTENT_DIR, $patterns, $exclude_patterns), $metadata, static function (array $metadata) use ($patterns, $exclude_patterns): array { | |
| 64 | + return FileScanner::scan_local(\WP_CONTENT_DIR, $patterns, $exclude_patterns, $metadata['next_batch'] ?? \false); | |
| 65 | + }); | |
| 66 | + if (!SourceRevision::matches($source_revision)) { | |
| 67 | + throw new \RuntimeException(__('Source data changed. Restart the scan.', 'windpress'), 409); | |
| 68 | + } | |
| 69 | + $result['metadata']['source_revision'] = $source_revision; | |
| 70 | + return new WP_REST_Response($result); | |
| 71 | + } | |
| 72 | + return new WP_REST_Response(FileScanner::scan_local(\WP_CONTENT_DIR, $patterns, $exclude_patterns, $payload['cursor'] ?? \false, $batched)); | |
| 73 | + } catch (\InvalidArgumentException $throwable) { | |
| 74 | + return new WP_REST_Response(['message' => $throwable->getMessage()], 400); | |
| 75 | + } catch (\Throwable $throwable) { | |
| 76 | + return new WP_REST_Response(['message' => $throwable->getMessage()], $throwable->getCode() === 409 ? 409 : 500); | |
| 46 | 77 | } |
| 47 | - return new WP_REST_Response(['contents' => $contents]); | |
| 48 | 78 | } |
| 49 | 79 | } |