diff --git a/app/Http/Controllers/Auth/TelegramController.php b/app/Http/Controllers/Auth/TelegramController.php index 9e7d7530..a6367822 100644 --- a/app/Http/Controllers/Auth/TelegramController.php +++ b/app/Http/Controllers/Auth/TelegramController.php @@ -6,6 +6,7 @@ use App\Enums\SocialAccount\Platform as SocialPlatform; use App\Enums\SocialAccount\TelegramConnectStatus; +use App\Http\Requests\App\Auth\TelegramStatusRequest; use App\Models\TelegramConnectRequest; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -47,14 +48,14 @@ public function connect(Request $request): JsonResponse /** * Poll whether the channel has been linked yet. */ - public function status(Request $request): JsonResponse + public function status(TelegramStatusRequest $request): JsonResponse { $workspace = $request->user()->currentWorkspace; abort_if($workspace === null, SymfonyResponse::HTTP_CONFLICT, 'No active workspace.'); $connectRequest = TelegramConnectRequest::query() ->where('workspace_id', $workspace->id) - ->where('code', (string) $request->query('code')) + ->where('code', $request->validated('code')) ->first(); return response()->json([ diff --git a/app/Http/Controllers/Webhooks/TelegramWebhookController.php b/app/Http/Controllers/Webhooks/TelegramWebhookController.php index 1ebbb066..6b281df4 100644 --- a/app/Http/Controllers/Webhooks/TelegramWebhookController.php +++ b/app/Http/Controllers/Webhooks/TelegramWebhookController.php @@ -6,10 +6,13 @@ use App\Enums\SocialAccount\Platform as SocialPlatform; use App\Enums\SocialAccount\Status; +use App\Features\SocialAccountLimit; use App\Http\Controllers\Controller; use App\Models\TelegramConnectRequest; +use App\Models\Workspace; use Illuminate\Http\Request; use Illuminate\Http\Response; +use Laravel\Pennant\Feature; use Symfony\Component\HttpFoundation\Response as SymfonyResponse; class TelegramWebhookController extends Controller @@ -49,7 +52,19 @@ public function handle(Request $request): Response $chatId = (string) data_get($chat, 'id'); $username = data_get($chat, 'username'); - $account = $connectRequest->workspace->socialAccounts()->updateOrCreate( + $workspace = $connectRequest->workspace; + + // Mirror the controller's limit gate: block only brand-new accounts, never reconnects. + $isNewAccount = ! $workspace->socialAccounts() + ->where('platform', SocialPlatform::Telegram->value) + ->where('platform_user_id', $chatId) + ->exists(); + + if ($isNewAccount && $this->workspaceAtAccountLimit($workspace)) { + return response()->noContent(); + } + + $account = $workspace->socialAccounts()->updateOrCreate( [ 'platform' => SocialPlatform::Telegram->value, 'platform_user_id' => $chatId, @@ -76,4 +91,15 @@ public function handle(Request $request): Response return response()->noContent(); } + + private function workspaceAtAccountLimit(Workspace $workspace): bool + { + if (config('trypost.self_hosted')) { + return false; + } + + $limit = Feature::for($workspace->account)->value(SocialAccountLimit::class); + + return $workspace->socialAccounts()->count() >= $limit; + } } diff --git a/app/Http/Requests/App/Auth/TelegramStatusRequest.php b/app/Http/Requests/App/Auth/TelegramStatusRequest.php new file mode 100644 index 00000000..3810e203 --- /dev/null +++ b/app/Http/Requests/App/Auth/TelegramStatusRequest.php @@ -0,0 +1,25 @@ + + */ + public function rules(): array + { + return [ + 'code' => ['required', 'string'], + ]; + } +} diff --git a/app/Services/Social/ContentSanitizer.php b/app/Services/Social/ContentSanitizer.php index 79a3ba0b..d9fa3df0 100644 --- a/app/Services/Social/ContentSanitizer.php +++ b/app/Services/Social/ContentSanitizer.php @@ -35,6 +35,9 @@ private function toTelegramHtml(string $content): string $content = preg_replace(['/<(\/?)strong>/i', '/<(\/?)em>/i'], ['<$1b>', '<$1i>'], $content); $content = strip_tags($content, ['b', 'i', 'u', 's', 'a', 'code', 'pre']); + // Telegram requires every to carry an href; drop bare anchors so the parser doesn't reject the whole message. + $content = preg_replace('/]*\shref=)[^>]*>(.*?)<\/a>/is', '$1', $content); + // Escape bare ampersands while leaving existing entities intact. $content = preg_replace('/&(?!(?:amp|lt|gt|quot|#\d+);)/', '&', $content); diff --git a/resources/js/components/SocialAccountsGrid.vue b/resources/js/components/SocialAccountsGrid.vue index 40b54a9e..6e2bcc63 100644 --- a/resources/js/components/SocialAccountsGrid.vue +++ b/resources/js/components/SocialAccountsGrid.vue @@ -1,13 +1,25 @@