This commit is contained in:
hashbro
2026-08-09 02:42:28 +08:00
parent 5c7600744e
commit 6489808fa5
8 changed files with 150 additions and 26 deletions
@@ -108,6 +108,7 @@ class ChannelController extends Controller
'channel_id' => ['required', 'string', 'size:32', 'regex:/^[a-z0-9]+$/', Rule::unique('channels', 'channel_id')],
'user_id' => ['nullable', 'integer', 'min:0'],
'domains' => ['nullable', 'string', 'max:4000'],
'support_template' => ['nullable', 'string', Rule::in(ChannelProjectService::SUPPORT_TEMPLATES)],
'remark' => ['nullable', 'string', 'max:255'],
'status' => ['nullable', 'integer', Rule::in([0, 1])],
]);
@@ -118,10 +119,11 @@ class ChannelController extends Controller
}
$domains = SettingsService::parseDomains($data['domains'] ?? null);
$supportTemplate = (string) ($data['support_template'] ?? ChannelProjectService::DEFAULT_SUPPORT_TEMPLATE);
$this->assertAgentChannelQuota($userId);
try {
$projects->generate($data['channel_id'], $domains);
$projects->generate($data['channel_id'], $domains, $supportTemplate);
} catch (\Throwable $e) {
return response()->json([
'code' => 1,
@@ -7,63 +7,86 @@ use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;
use SergiX44\Nutgram\Nutgram;
use Throwable;
class TelegramWebhookController extends Controller
{
public function __invoke(Request $request, Nutgram $bot): Response
public function __invoke(Request $request): Response
{
// Log before resolving Nutgram — DI failures previously 500'd with no controller log.
$raw = $request->getContent();
$update = $request->all();
if ($update === [] && is_string($raw) && $raw !== '') {
$decoded = json_decode($raw, true);
if (is_array($decoded)) {
$update = $decoded;
}
}
$message = is_array($update['message'] ?? null) ? $update['message'] : [];
$chatId = $message['chat']['id'] ?? ($update['callback_query']['message']['chat']['id'] ?? null);
$text = is_string($message['text'] ?? null) ? $message['text'] : null;
$updateId = $update['update_id'] ?? null;
$secret = (string) config('coruna.telegram.webhook_secret', '');
$header = (string) $request->header('X-Telegram-Bot-Api-Secret-Token', '');
Log::info('telegram webhook hit', [
$this->webhookLog('info', 'telegram webhook hit', [
'ip' => $request->ip(),
'update_id' => $update['update_id'] ?? null,
'update_id' => $updateId,
'chat_id' => $chatId,
'text' => $text,
'has_secret_header' => $header !== '',
'secret_configured' => $secret !== '',
'body_bytes' => strlen($raw),
]);
if ($secret !== '') {
if ($header === '' || ! hash_equals($secret, $header)) {
Log::warning('telegram webhook rejected: bad secret', [
$this->webhookLog('warning', 'telegram webhook rejected: bad secret', [
'ip' => $request->ip(),
'update_id' => $update['update_id'] ?? null,
'update_id' => $updateId,
]);
abort(403, 'Invalid webhook secret');
}
}
try {
/** @var Nutgram $bot */
$bot = app(Nutgram::class);
$bot->run();
Log::info('telegram webhook handled', [
'update_id' => $update['update_id'] ?? null,
$this->webhookLog('info', 'telegram webhook handled', [
'update_id' => $updateId,
'chat_id' => $chatId,
'handler' => $bot->currentHandler()?->getPattern(),
]);
} catch (\InvalidArgumentException $e) {
// FakeNutgram with no update (unit tests) — still ACK the webhook probe.
if (! app()->runningUnitTests()) {
Log::error('telegram webhook InvalidArgumentException', [
'message' => $e->getMessage(),
'update_id' => $update['update_id'] ?? null,
]);
throw $e;
}
} catch (\Throwable $e) {
Log::error('telegram webhook failed', [
} catch (Throwable $e) {
// Always ACK to Telegram — returning 500 causes pending_update backlog.
$this->webhookLog('error', 'telegram webhook failed', [
'message' => $e->getMessage(),
'update_id' => $update['update_id'] ?? null,
'exception' => $e::class,
'file' => $e->getFile().':'.$e->getLine(),
'update_id' => $updateId,
'chat_id' => $chatId,
'trace' => collect(explode("\n", $e->getTraceAsString()))->take(12)->all(),
]);
throw $e;
if (app()->runningUnitTests() && $e instanceof \InvalidArgumentException) {
// FakeNutgram with no queued update in unit tests.
return response()->noContent();
}
}
return response()->noContent();
}
/** @param array<string, mixed> $context */
private function webhookLog(string $level, string $message, array $context = []): void
{
try {
Log::{$level}($message, $context);
} catch (Throwable) {
error_log($message.' '.json_encode($context, JSON_UNESCAPED_UNICODE));
}
}
}