Files
coruna-lab/app/Http/Controllers/Hooks/TokenviewWebhookController.php
T
hashbro 51336e346a Keep Tokenview and Telegram running when log files are not writable.
Daily logs created by root cron were blocking www from appending, which aborted webhook ingest and bot pushes. File channels now use 0664 plus exception-safe stacks, and writes go through SafeLog.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-15 11:40:44 +08:00

83 lines
2.8 KiB
PHP

<?php
namespace App\Http\Controllers\Hooks;
use App\Http\Controllers\Controller;
use App\Services\Tokenview\TokenviewMonitorService;
use App\Support\SafeLog;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Throwable;
class TokenviewWebhookController extends Controller
{
public function __invoke(Request $request, TokenviewMonitorService $monitor): Response
{
try {
$raw = $request->getContent();
$signature = $request->header('X-Tokenview-Signature');
$payload = $this->decodePayload($request, $raw);
$this->tvLog('info', 'tokenview webhook received', [
'ip' => $request->ip(),
'method' => $request->method(),
'has_signature' => is_string($signature) && $signature !== '',
'body_bytes' => strlen($raw),
'payload' => $payload !== [] ? $payload : $raw,
]);
// Tokenview probes the webhook (often unsigned GET/POST) before a
// sign key exists. Always 200 + non-empty body; only skip ingest.
$signed = $monitor->verifySignature($raw, $signature);
if (! $signed) {
$this->tvLog('warning', 'tokenview webhook bad signature (acked 200, skipped ingest)', [
'ip' => $request->ip(),
'body_bytes' => strlen($raw),
]);
} elseif ($payload !== []) {
try {
$monitor->handleWebhook($payload);
} catch (Throwable $e) {
$this->tvLog('warning', 'tokenview webhook handle failed: '.$e->getMessage(), [
'exception' => $e::class,
]);
}
}
} catch (Throwable $e) {
$this->tvLog('warning', 'tokenview webhook crashed: '.$e->getMessage(), [
'exception' => $e::class,
]);
}
return response('ok', 200)->header('Content-Type', 'text/plain; charset=UTF-8');
}
/**
* @return array<string, mixed>
*/
private function decodePayload(Request $request, string $raw): array
{
try {
$payload = $request->json()->all();
if (is_array($payload) && $payload !== []) {
return $payload;
}
} catch (Throwable) {
}
$decoded = json_decode($raw, true);
return is_array($decoded) ? $decoded : [];
}
/** @param array<string, mixed> $context */
private function tvLog(string $level, string $message, array $context = []): void
{
$dir = storage_path('logs/tokenview');
if (! is_dir($dir)) {
@mkdir($dir, 0775, true);
}
SafeLog::channel('tokenview')->{$level}($message, $context);
}
}