refactor(posts): tighten content-length caps and simplify contentOverflow
- Platform::contentOverflow returns int (0 = fits) via max(), drops the nullable-int + ternary pattern; HasSocialHttpClient and the validation rule updated to compare against 0 instead of null. - Drop the hardcoded 63206-char limit to 10000 across all entry points (Platform enum, MCP CreatePostTool/UpdatePostTool, FacebookRules, all three FormRequests). Facebook's API accepts up to 63206 but nobody writes 63k-char posts and emoji-heavy content risks overflowing the TEXT column's 65535-byte ceiling. - Compiled i18n JSON regenerated with the content_exceeds_platform key.
This commit is contained in:
parent
953be22b5b
commit
b10f25f94d
13 changed files with 23 additions and 23 deletions
|
|
@ -16,7 +16,7 @@ public function platform(): Platform
|
|||
public function specs(): array
|
||||
{
|
||||
return [
|
||||
'max_content_length' => 63206,
|
||||
'max_content_length' => 10000,
|
||||
'max_images' => 10,
|
||||
'text_only_allowed' => true,
|
||||
'formats' => [
|
||||
|
|
@ -30,7 +30,7 @@ public function specs(): array
|
|||
public function summary(): string
|
||||
{
|
||||
return <<<'TXT'
|
||||
Facebook: caption up to 63k chars but short posts perform better. Text-only allowed.
|
||||
Facebook: caption up to 10k chars but short posts perform better. Text-only allowed.
|
||||
- Post: flexible aspect ratio, up to 10 images/videos.
|
||||
- Reel: vertical 9:16 video only, up to 90s.
|
||||
- Story: vertical 9:16, single image/video, ephemeral.
|
||||
|
|
|
|||
|
|
@ -99,7 +99,9 @@ public function maxImages(): int
|
|||
* (publisher derives title from the first line via `buildTitle`), and
|
||||
* Shorts UX only shows ~100 chars before "more" — capping at 100 keeps
|
||||
* posts appropriate for the format.
|
||||
* - Facebook text status: 63206
|
||||
* - Facebook text status: 10000 (API allows 63206; we cap below
|
||||
* that — 63k-char posts are unrealistic and emoji-heavy content
|
||||
* risks overflowing the TEXT column's 65535-byte ceiling)
|
||||
* - Instagram feed caption: 2200
|
||||
* - Threads: 500
|
||||
* - Pinterest pin description: 800 (title is 100, not modeled here)
|
||||
|
|
@ -113,7 +115,7 @@ public function maxContentLength(): int
|
|||
self::X => 280,
|
||||
self::TikTok => 2200,
|
||||
self::YouTube => 100,
|
||||
self::Facebook => 63206,
|
||||
self::Facebook => 10000,
|
||||
self::Instagram, self::InstagramFacebook => 2200,
|
||||
self::Threads => 500,
|
||||
self::Pinterest => 800,
|
||||
|
|
@ -123,16 +125,14 @@ public function maxContentLength(): int
|
|||
}
|
||||
|
||||
/**
|
||||
* Number of characters the given content exceeds this platform's hard cap,
|
||||
* or null when it fits. Single source of truth for content-length checks —
|
||||
* used both at schedule/publish-validation time and at publish time itself
|
||||
* so the two paths can never drift apart.
|
||||
* Number of characters by which the given content exceeds this platform's
|
||||
* hard cap. Returns 0 when it fits. Single source of truth for content-
|
||||
* length checks — used both at schedule-validation time and at publish
|
||||
* time itself so the two paths can never drift apart.
|
||||
*/
|
||||
public function contentOverflow(string $content): ?int
|
||||
public function contentOverflow(string $content): int
|
||||
{
|
||||
$over = mb_strlen($content) - $this->maxContentLength();
|
||||
|
||||
return $over > 0 ? $over : null;
|
||||
return max(0, mb_strlen($content) - $this->maxContentLength());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public function rules(): array
|
|||
'content' => [
|
||||
'nullable',
|
||||
'string',
|
||||
'max:63206',
|
||||
'max:10000',
|
||||
Rule::when(
|
||||
$this->filled('scheduled_at'),
|
||||
[new ContentFitsPlatformLimits($this->resolveSelectedPlatforms($workspaceId))]
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public function rules(): array
|
|||
'content' => [
|
||||
'nullable',
|
||||
'string',
|
||||
'max:63206',
|
||||
'max:10000',
|
||||
Rule::when(
|
||||
$enforcesPlatformLimits,
|
||||
[new ContentFitsPlatformLimits($this->resolveSelectedPlatforms())]
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ public function rules(): array
|
|||
'content' => [
|
||||
'nullable',
|
||||
'string',
|
||||
'max:63206',
|
||||
'max:10000',
|
||||
Rule::when(
|
||||
$enforcesMediaCompatibility,
|
||||
[new ContentFitsPlatformLimits($this->resolveSelectedPlatforms())]
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public function handle(Request $request): ResponseFactory
|
|||
$workspace = $request->user()->currentWorkspace;
|
||||
|
||||
$validated = $request->validate([
|
||||
'content' => ['nullable', 'string', 'max:63206'],
|
||||
'content' => ['nullable', 'string', 'max:10000'],
|
||||
'scheduled_at' => ['nullable', 'date', 'after:now'],
|
||||
'label_ids' => ['sometimes', 'array'],
|
||||
'label_ids.*' => ['uuid', Rule::exists('workspace_labels', 'id')->where('workspace_id', $workspace->id)],
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public function handle(Request $request): Response|ResponseFactory
|
|||
|
||||
$validated = $request->validate([
|
||||
'post_id' => ['required', 'uuid'],
|
||||
'content' => ['nullable', 'string', 'max:63206'],
|
||||
'content' => ['nullable', 'string', 'max:10000'],
|
||||
'scheduled_at' => ['nullable', 'date', 'after:now'],
|
||||
'status' => ['sometimes', 'string', Rule::in([Status::Draft->value, Status::Scheduled->value])],
|
||||
'label_ids' => ['sometimes', 'array'],
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ public function validate(string $attribute, mixed $value, Closure $fail): void
|
|||
}
|
||||
|
||||
$over = $platform->contentOverflow($content);
|
||||
if ($over === null) {
|
||||
if ($over === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ protected function validateContentLength(PostPlatform $postPlatform): void
|
|||
{
|
||||
$content = $postPlatform->post->content ?? '';
|
||||
|
||||
if ($postPlatform->platform->contentOverflow($content) === null) {
|
||||
if ($postPlatform->platform->contentOverflow($content) === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -60,7 +60,7 @@
|
|||
expect(Platform::X->maxContentLength())->toBe(280);
|
||||
expect(Platform::TikTok->maxContentLength())->toBe(2200);
|
||||
expect(Platform::YouTube->maxContentLength())->toBe(100);
|
||||
expect(Platform::Facebook->maxContentLength())->toBe(63206);
|
||||
expect(Platform::Facebook->maxContentLength())->toBe(10000);
|
||||
expect(Platform::Instagram->maxContentLength())->toBe(2200);
|
||||
expect(Platform::Threads->maxContentLength())->toBe(500);
|
||||
expect(Platform::Pinterest->maxContentLength())->toBe(800);
|
||||
|
|
|
|||
Loading…
Reference in a new issue