Commit graph

730 commits

Author SHA1 Message Date
Paulo Castellano
45da024fc6 fix(automations): code-review hardening for feed fetch & URL validation
- inspectFeed fetches via SafeHttpFetcher::get (SSRF guard + timeout + redirect
  cap + UA) instead of a raw, timeout-less Http::get; add a timeout to the node fetch.
- ResolvableUrl now requires an http(s) scheme, so it's no weaker than the plain
  url rule it replaced (rejects file://, javascript://).
- Apply ResolvableUrl to the HTTP Request and Webhook node URLs too, so templated
  {{ }} URLs validate consistently across every URL field.
- Clear a fetch_rss node's discovered_fields when its feed_url changes, so stale
  fields from the previous feed stop showing up as autocomplete suggestions.
2026-06-16 12:09:16 -03:00
Paulo Castellano
f55d35363f fix(automations): return null on any feed parse failure
Wrap FeedParser::parse in try/catch so an unexpected Throwable from third-party
feed content (not just invalid XML) maps to the clean fetch_rss_malformed
failure instead of bubbling up as an unhandled exception — matching the
failed()-not-throw pattern of the sibling HTTP node.
2026-06-16 12:09:16 -03:00
Paulo Castellano
b2932262cf fix(automations): reliably open a node's config when switching nodes
A click with a few px of jitter crossed Vue Flow's 1px drag threshold and was
treated as a drag, so onNodeClick never fired and the config panel didn't switch
between nodes. Raise nodeDragThreshold to 5 so normal clicks stay clicks, and
also select the node on drag start as a safety net.
2026-06-16 11:26:05 -03:00
Paulo Castellano
109ad7dfc0 fix(automations): preview shows the newest feed item, not the oldest
A manual/dry "test with real data" run surfaces a single item and never fans
out, but it was taking the oldest new item (items are sorted oldest-first for
production fan-out). Surface the newest item in preview instead — what the user
expects to test against. Real runs are unchanged. Works for every feed format.
2026-06-16 11:26:05 -03:00
Paulo Castellano
4bb7d450a3 chore(release): link GitHub release in changelog email + add v1.0.2 artifacts
Tweak the /release command's email template (GitHub release link in the byline,
"Paulo from TryPost.it" signature) and add the generated v1.0.2 release notes.
2026-06-16 11:09:25 -03:00
Paulo Castellano
3acaafb81e test(automations): extract inline feed XML into shared fixtures
Move the RSS sample feeds out of inline heredocs/constants in FetchRssNodeTest
into tests/fixtures/feeds/, matching the parser fixtures. Centralize the
feedFixture() loader in tests/Pest.php and use it across the feed tests.
2026-06-16 11:06:46 -03:00
Paulo Castellano
246a159f34 feat(automations): multi-format RSS/Atom feeds with dynamic variables
Replace the RSS-2.0-only SimpleXML parser with SimplePie so the Fetch RSS
node reads Atom 1.0 (YouTube, GitHub, The Verge…) and RSS 2.0 + namespace
extensions (dc:, content:, media:, yt:, itunes:). Each item exposes stable
cross-format aliases (title, link, date, content, author, …) plus every
namespaced field flattened for use as {{ fetched.* }}.

Add a feed-inspection endpoint that discovers a feed's real fields and feeds
them into the editor's expression autocomplete. Allow {{ }} expressions in the
feed URL via a ResolvableUrl rule. Parsing moves to a dedicated FeedParser
service; the fetch keeps the SSRF guard and gains XXE-safe parsing.
2026-06-16 10:56:08 -03:00
Paulo Castellano
b18b9b2303
Merge pull request #98 from trypostit/feat/discord-widget
feat(discord): global floating Discord widget
2026-06-15 13:28:20 -03:00
Paulo Castellano
f41b28c31d feat(discord): global floating Discord widget with PostHog tracking
- Fixed bottom-right Discord button (indies-club style) linking to
  https://trypost.it/discord, mounted globally in AppSidebarLayout
- Captures discord.clicked (placement: floating_widget) via captureEvent
- i18n label + aria-label in common.php (en, es, pt-BR)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 13:23:03 -03:00
Paulo Castellano
c71234337d
Merge pull request #96 from trypostit/fix/past-due-no-forced-subscribe
Past-due access, reliable checkout.completed, and complete morph map
2026-06-14 16:18:51 -03:00
Paulo Castellano
65116de859 Register all models in the morph map + lock it with a test
Add the five automation models to Relation::enforceMorphMap(), document the
all-models-must-be-mapped rule in CLAUDE.md, and add MorphMapTest to fail
when any app/Models class is missing from the map.
2026-06-14 16:12:49 -03:00
Paulo Castellano
f223cf14ce Fire checkout.completed reliably for trial-with-card checkouts
A trial-with-card subscription is already subscribed() (status trialing)
by the time the webhook lands, so /billing/processing usually mounts
already-active and the false->true poll transition the event depended on
never happened — only 3 of 66 real subscriptions emitted checkout.completed.

Complete the purchase from whichever path runs first (onMounted when already
active, or the poll transition), de-duplicated per checkout session via a
one-time Cache::add gate on session_id so back-button/refresh can't re-fire.
2026-06-14 16:12:49 -03:00
Paulo Castellano
8e334cd676 Keep past_due subscribers in-app instead of forcing re-subscribe
A past_due subscription made subscribed() return false (Cashier default),
so EnsureAccountReady redirected to /subscribe — which starts a brand-new
checkout and creates a second subscription. Past-due users already have a
subscription; they only need to update their payment method.

Enable Cashier::keepPastDueSubscriptionsActive() so past_due counts as
active and users keep navigating. Surface a past-due notice in the sidebar
footer linking to the Stripe billing portal (not /subscribe). Both trial
modes (subscription trial / generic trial) are unaffected.
2026-06-14 15:46:17 -03:00
Paulo Castellano
11c27dfd62
Merge pull request #95 from trypostit/feat/telegram-channel
Add Telegram publishing channel
2026-06-14 14:46:33 -03:00
Paulo Castellano
c9f086ea94 Use formatNumberCompact helper in post platform metrics 2026-06-14 14:09:39 -03:00
Paulo Castellano
389177c25b Soften Telegram preview bubble shadow (drop trypost ink shadow) 2026-06-14 14:07:36 -03:00
Paulo Castellano
0c3c837bd3 Polish Telegram preview: simulate channel reactions 2026-06-14 13:58:14 -03:00
Paulo Castellano
548138f277 Render Telegram subscribers as an icon pill with divider before reactions 2026-06-14 13:53:57 -03:00
Paulo Castellano
a5b33f5b0a Show Telegram subscribers on post metrics; render reactions as compact pills 2026-06-14 13:50:12 -03:00
Paulo Castellano
fc1d51ca60 Telegram connect dialog: drop footer Close button, top-align icon 2026-06-14 13:35:03 -03:00
Paulo Castellano
b9fa8be513 Group Telegram service classes under Services/Social/Telegram 2026-06-14 13:32:03 -03:00
Paulo Castellano
80b68088a0 Type Telegram media kinds with a TelegramMediaType enum 2026-06-14 13:23:05 -03:00
Paulo Castellano
b6a3cb886c Centralize Telegram Bot API URL/token construction in a TelegramApi helper 2026-06-14 12:41:19 -03:00
Paulo Castellano
e907ad087f Lock Telegram @mention passthrough in sanitizer test 2026-06-14 11:40:55 -03:00
Paulo Castellano
c719480cf1 Harden Telegram reactions/analytics from PR review; add scoping + edge-case tests 2026-06-14 11:37:04 -03:00
Paulo Castellano
ba246a3ddd Add Telegram analytics: per-post reactions (webhook) + channel subscribers 2026-06-14 11:18:40 -03:00
Paulo Castellano
ce5d59a149 Detect Telegram connection via broadcast instead of polling 2026-06-14 10:58:13 -03:00
Paulo Castellano
f5d6c4a186 Fetch Telegram channel photo synchronously in the connect action (drop avatar job) 2026-06-14 10:30:17 -03:00
Paulo Castellano
f194bde58d Fall back to display_name for disconnect confirmation when username is null (Telegram) 2026-06-14 10:21:52 -03:00
Paulo Castellano
b9cc12aef7 Fetch Telegram channel photo as avatar; extract ConnectTelegramChannel action 2026-06-14 10:18:44 -03:00
Paulo Castellano
58eab0619b Use TelegramConnectStatus enum for connect status instead of magic strings 2026-06-14 09:59:36 -03:00
Paulo Castellano
92e6425906 Use Platform enum instead of magic string in AddSocialDialog 2026-06-14 09:55:50 -03:00
Paulo Castellano
ec5a3a3b16 Add cursor-pointer and tooltip to Telegram copy button 2026-06-14 09:42:31 -03:00
Paulo Castellano
d5da44fd75 Fix Telegram dialog overflow (min-w-0 grid item), round icon, dedupe copy toast 2026-06-14 09:36:42 -03:00
Paulo Castellano
33bc001977 Truncate the long Telegram connect code so the dialog doesn't overflow 2026-06-14 09:32:09 -03:00
Paulo Castellano
e72bf51c38 Make webhook host configurable via WEBHOOK_URL (sendkit-style domain group) 2026-06-14 09:31:00 -03:00
Paulo Castellano
cc61adbac2 Move webhook routes into dedicated routes/webhook.php 2026-06-14 09:23:46 -03:00
Paulo Castellano
d729c34c0e Harden Telegram connect: one-time code, drop session, decode validation; switch icon to png; widen add-social dialog 2026-06-14 09:18:24 -03:00
Paulo Castellano
2284596f9d Make Telegram connect codes stateless: drop the model/table, use a signed code + session 2026-06-14 08:57:38 -03:00
Paulo Castellano
816d367dbe Add return type to telegram exception test helper 2026-06-13 22:58:04 -03:00
Paulo Castellano
c096092c7b Polish PR: status FormRequest, sanitizer anchor guard, webhook limit, dedupe OAuth popup 2026-06-13 22:48:21 -03:00
Paulo Castellano
2585d89cb4 Address PR review: connect-status enum, publisher cleanup, fill test gaps 2026-06-13 22:38:23 -03:00
Paulo Castellano
deb1c6fa69 Extract Telegram webhook registration into an action 2026-06-13 22:20:52 -03:00
Paulo Castellano
f378469842 Wire Telegram into the post composer (preview, content type, logo) 2026-06-13 22:10:12 -03:00
Paulo Castellano
b504243212 Document Telegram env vars in docker .env example 2026-06-13 22:02:02 -03:00
Paulo Castellano
b6605ea228 Document Telegram env vars in .env.example 2026-06-13 22:01:37 -03:00
Paulo Castellano
79acdccc45 Add Telegram connection UI (connect dialog + code polling) 2026-06-13 21:58:49 -03:00
Paulo Castellano
a4cf8aa4ce Add Telegram connection flow (controller + webhook)
Connect a channel by issuing a one-time code the user posts as /connect <code>
in their channel. A secret-token-guarded webhook matches the code, links the
channel as a SocialAccount (chat_id in meta), and records it on the request so
the connect endpoint can poll for completion. Adds the TelegramConnectRequest
model + migration, the connect/status endpoints, the public webhook route (CSRF
exempt), a ConnectionVerifier branch (getChat liveness), and a telegram:set-webhook
command. Tests cover the code issue, webhook link, secret rejection, expired/
unknown codes, status polling, and the command.
2026-06-13 21:39:03 -03:00
Paulo Castellano
9634e88e5d Add Telegram publishing (backend foundation)
Register Telegram as a platform: Platform/ContentType enum cases, a
platforms.telegram config block (shared bot token via env), TelegramPublisher
(sendMessage / sendPhoto|Video|Document / sendMediaGroup over the Bot API, HTML
parse mode, 4096 limit with long text split off a 1024 caption), wired into the
publisher dispatch. Add a Telegram ContentSanitizer branch (Telegram-allowed
HTML + ampersand escaping), MediaOptimizer/profile-url/factory support, and the
TelegramPublishException. Tests cover text, single media, album, long-text split,
overflow, API errors, private-channel URLs, and sanitization.
2026-06-13 21:25:31 -03:00
Paulo Castellano
986e2661f9
Merge pull request #94 from trypostit/fix/bluesky-publisher-hardening
Harden BlueskyPublisher: link facets, conventions, NSID lexicon, tests
2026-06-13 20:38:09 -03:00