Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 1
CRAP
0.00% covered (danger)
0.00%
0 / 1
FinishUploadEndpoint
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 1
90
0.00% covered (danger)
0.00%
0 / 1
 handle
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 1
90
1<?php
2
3namespace Olz\Api\Endpoints;
4
5use Olz\Api\OlzTypedEndpoint;
6use PhpTypeScriptApi\HttpError;
7
8/**
9 * @extends OlzTypedEndpoint<
10 *   array{
11 *     id: non-empty-string,
12 *     numberOfParts: int<1, 1000>,
13 *   },
14 *   array{
15 *     status: 'OK'|'ERROR',
16 *   }
17 * >
18 */
19class FinishUploadEndpoint extends OlzTypedEndpoint {
20    protected function handle(mixed $input): mixed {
21        $this->checkPermission('any');
22
23        $data_path = $this->envUtils()->getDataPath();
24        $upload_id = $input['id'];
25        if (!$this->uploadUtils()->isUploadId($upload_id)) {
26            throw new HttpError(400, "Invalid upload ID");
27        }
28        $upload_path = "{$data_path}temp/{$upload_id}";
29        if (!is_file($upload_path)) {
30            $this->log()->error("Could not finish upload. Invalid ID: '{$upload_id}'.");
31            return ['status' => 'ERROR'];
32        }
33
34        $num_parts = $input['numberOfParts'];
35        $first_part_path = "{$upload_path}_0";
36        if (!is_file($first_part_path)) {
37            $this->log()->error("Upload with ID {$upload_id} is missing the first part.");
38            return ['status' => 'ERROR'];
39        }
40        $first_content = file_get_contents($first_part_path) ?: '';
41        @unlink($first_part_path);
42        $res = preg_match("/^data\\:([^\\;]*)\\;base64\\,(.+)$/", $first_content, $matches);
43        if (!$res) {
44            $this->log()->error("Upload with ID {$upload_id} does not have base64 header.");
45            return ['status' => 'ERROR'];
46        }
47        $mime_type = $matches[1];
48        $base64 = $matches[2];
49        $missing_parts = [];
50        for ($part = 1; $part < $num_parts; $part++) {
51            $part_path = "{$upload_path}_{$part}";
52            if (!is_file($part_path)) {
53                $missing_parts[] = $part;
54                continue;
55            }
56            $part_content = file_get_contents($part_path);
57            $base64 .= $part_content;
58            @unlink($part_path);
59        }
60        if (count($missing_parts) > 0) {
61            $pretty_missing_parts = implode(', ', $missing_parts);
62            $this->log()->error("Upload with ID {$upload_id} is missing parts {$pretty_missing_parts}.");
63            return ['status' => 'ERROR'];
64        }
65        $binary_data = base64_decode(str_replace(" ", "+", $base64));
66
67        file_put_contents($upload_path, $binary_data);
68
69        return ['status' => 'OK'];
70    }
71}