generated from boboko/starter
75 lines
2.6 KiB
PHP
75 lines
2.6 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Crypt;
|
|
use Illuminate\Support\Facades\Validator;
|
|
|
|
/**
|
|
* Stores the shopper's photo for a product custom field of type `file` (see
|
|
* boboko-core's Product::$custom_fields) the moment it's picked on the product
|
|
* page — before add-to-cart, see custom-field-upload-controller.js.
|
|
*
|
|
* Which files are acceptable is a per-site decision (this site: photographs),
|
|
* so it lives here in the storefront, not in the checkout module. The module's
|
|
* add-to-cart endpoint only ever receives the opaque `reference` returned
|
|
* below — an encrypted {disk, path, name, mime} payload, so a shopper can
|
|
* neither forge a reference to some other private file nor read the path —
|
|
* and copies it onto the cart line's meta (see Checkout\CartController::
|
|
* customFieldsMeta()).
|
|
*
|
|
* Stored on the private `local` disk: these are customers' personal photos,
|
|
* never reachable by a public URL. Uploads nobody added to a cart are removed
|
|
* by the custom-fields:prune-uploads command (see PruneCustomFieldUploads).
|
|
*/
|
|
class CustomFieldUploadController extends Controller
|
|
{
|
|
public const DISK = 'local';
|
|
|
|
public const DIRECTORY = 'custom-field-uploads';
|
|
|
|
public const MAX_KILOBYTES = 10240;
|
|
|
|
public const EXTENSIONS = ['jpg', 'jpeg', 'png', 'webp', 'heic', 'heif'];
|
|
|
|
/**
|
|
* For the file input's `accept` attribute — same list the server enforces.
|
|
*/
|
|
public static function accept(): string
|
|
{
|
|
return '.'.implode(',.', self::EXTENSIONS);
|
|
}
|
|
|
|
public function store(string $locale, Request $request): JsonResponse
|
|
{
|
|
// `label` is the admin-authored field label, only used as the
|
|
// :attribute in the validation message shown next to that field.
|
|
$validator = Validator::make(
|
|
$request->all(),
|
|
['file' => ['required', 'file', 'mimes:'.implode(',', self::EXTENSIONS), 'max:'.self::MAX_KILOBYTES]],
|
|
[],
|
|
['file' => (string) $request->input('label', 'file')],
|
|
);
|
|
|
|
if ($validator->fails()) {
|
|
return response()->json(['error' => $validator->errors()->first('file')], 422);
|
|
}
|
|
|
|
$file = $request->file('file');
|
|
$path = $file->store(self::DIRECTORY, self::DISK);
|
|
|
|
abort_if($path === false, 500);
|
|
|
|
return response()->json([
|
|
'reference' => Crypt::encryptString(json_encode([
|
|
'disk' => self::DISK,
|
|
'path' => $path,
|
|
'name' => $file->getClientOriginalName(),
|
|
'mime' => $file->getMimeType(),
|
|
])),
|
|
]);
|
|
}
|
|
}
|