Commit graph

1213 commits

Author SHA1 Message Date
fd1a03c055 feat(threads): configure Threads OAuth credentials in setup workflow
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 1m27s
2026-10-22 22:40:21 +05:30
fe86b0d121 feat(x): configure X (Twitter) OAuth credentials in setup workflow
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 1m49s
2026-10-22 22:34:11 +05:30
ea670556c0 fix(linkedin): disable LINKEDIN_PAGE_ENABLED to request only personal profile scopes
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 2m0s
2026-10-21 20:01:31 +05:30
d97216db1b feat(linkedin): configure LinkedIn OAuth credentials in setup workflow
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 2m8s
2026-10-21 19:53:44 +05:30
b985932699 fix(youtube): correct client secret character NliPV
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 1m32s
2026-10-20 21:22:30 +05:30
0eb7d4a686 fix(youtube): correct single character typo in google client id
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 5m50s
2026-10-20 20:53:30 +05:30
bb8fcba8a3 fix(ci): optimize ec2 deploy script, remove slow apt-get upgrade and increase timeout
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 1m59s
2026-10-20 20:48:44 +05:30
9f73ddc7f0 feat(youtube): configure Google/YouTube OAuth credentials in setup workflow
Some checks failed
Setup EC2 Tools / setup-server (push) Failing after 10m7s
2026-10-20 20:34:29 +05:30
7f6875cf66 fix(mail): configure Hostinger SMTP credentials in CI deployment workflow
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 1m57s
2026-09-27 15:06:48 +05:30
37729b7030 chore: update database password in setup-ec2 workflow
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 3m32s
2026-09-27 14:41:24 +05:30
6fe93eea7f fix(instagram): add Instagram OAuth credentials to deployment and ensure explicit redirect URL
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 2m2s
2026-09-13 20:12:28 +05:30
30c6fc5747 refactor: improve avatar error handling, storage URL resolution, and upload logging
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 1m46s
2026-09-10 23:05:37 +05:30
c899d7db13 chore: add missing AWS CDK and project dependencies to node_modules
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 2m40s
2026-09-10 22:50:11 +05:30
29f3536a63 Fix indentation in workflow heredoc blocks to prevent YAML scanner error
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 1m55s
2026-09-08 23:28:57 +05:30
5077c04291 Add Supervisor Horizon queue worker and cron scheduler to deployment workflow 2026-09-08 23:23:31 +05:30
f6f6b128f0 Set FILESYSTEM_DISK=public in workflow 2026-09-08 23:16:24 +05:30
bf4490ac45 Add storage:link to setup-ec2.yml workflow 2026-09-08 23:05:42 +05:30
9483685ffd Enable HTTPS with SSL certificate in Nginx and update APP_URL 2026-09-08 22:47:35 +05:30
8dca777f60 Add Facebook OAuth credentials and configure APP_URL in workflow
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 1m19s
2026-09-08 22:39:57 +05:30
bc3e8c3534 Allow storage permissions for both www-data and ubuntu users
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 1m8s
2026-09-08 22:09:34 +05:30
daf9d45c53 Add user:create artisan command
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 1m31s
2026-09-08 22:00:11 +05:30
37772f4c3f Fix duplicate DB values in env generation
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 1m12s
2026-09-08 21:23:00 +05:30
78084b1d58 Install Node.js and build frontend assets with Vite
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 1m41s
2026-09-08 21:19:06 +05:30
ea6cffa474 Update workflow to use the new database credentials for vignesh
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 46s
2026-09-08 21:00:46 +05:30
3391c0b057 Configure PostgreSQL host and run migrations
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 48s
2026-09-08 20:55:56 +05:30
5fec3336c3 Ensure app key is generated if missing
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 50s
2026-09-08 20:52:45 +05:30
d4c5ae2cf2 Fix deployment steps order to prevent permissions issue during composer install
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 50s
2026-09-08 20:49:51 +05:30
8c1a409563 Upgrade PHP to 8.4 and use php8.4-fpm.sock in Nginx config
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 47s
2026-09-08 20:48:09 +05:30
140a1f58c1 Enable debug mode and ensure storage directories exist
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 27s
2026-09-08 20:41:37 +05:30
aedd786edd Add deployment steps to clone code and set up Laravel
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 40s
2026-09-08 20:36:51 +05:30
8732e33e97 Fix YAML syntax error in setup-ec2.yml
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 31s
2026-09-08 20:31:42 +05:30
eb680fd82a Add Nginx proxy setup for socialmedia.tradhox.com 2026-09-08 20:30:30 +05:30
73df5a19b1 Specify valid container image
All checks were successful
Setup EC2 Tools / setup-server (push) Successful in 1m58s
2026-09-08 20:22:59 +05:30
362dab5ed2 Trigger workflow
Some checks failed
Setup EC2 Tools / setup-server (push) Failing after 2s
2026-09-08 20:21:33 +05:30
0d639d3d1b Fix github action URL prefix
Some checks failed
Setup EC2 Tools / setup-server (push) Failing after 3s
2026-09-05 21:08:42 +05:30
38c8b3e419 Fix action uses url to point to github 2026-09-05 21:07:39 +05:30
4c7ecf4bdc Configure workflow to run on push 2026-09-05 21:06:39 +05:30
13151237b4 feat: add Forgejo workflow to automate LEMP stack installation on EC2 2026-09-05 21:02:21 +05:30
ee53672229 refactor: remove legacy agent and cursor configuration files and documentation 2026-09-05 20:56:33 +05:30
9f9ecf400e refactor: remove all Docker configuration and orchestration files 2026-09-05 20:52:37 +05:30
Paulo Castellano
d8149ef058
Remove the automations module (#333)
* Remove the automations module

Drops the visual workflow builder end to end: actions, node runners,
commands, jobs, models, observers, policy, resources, requests, routes,
broadcast channel, scheduler entries, Horizon supervisor, Vue pages and
components, canvas undo/redo history, CodeEditor, translations, factories
and tests.

A new migration rewrites posts.created_via = 'automation' to 'web' and
drops the five automation tables. The CreatedVia::Automation enum case is
removed. The 2026-08-21 social-account identity migration now skips its
automation node repointing when the automations table no longer exists,
so it stays re-runnable after the drop.

Orphaned dependencies removed: simplepie/simplepie, @vue-flow/*,
codemirror and @codemirror/*.

* Drop the orphaned common.beta translation key

* Remove automation leftovers: ResolvableUrl rule, feed fixtures, useShortcut, nav badge

* Drop the unused chart wrapper and @unovis packages

* Address review: keep the shipped migration untouched, drop the dead previewOnly chain

- Restore 2026_08_21 migration to exactly what production ran; the
  rehearsal test now recreates the automations table it expects instead.
- Mark the drop migration's down() irreversible like its siblings.
- Simplify DropAutomationTablesMigrationTest to the sibling shape.
- Remove previewOnly / aiGenerateVariants: the only caller that set the
  prop was the deleted automation Generate node.
- Run pint over lang/*/common.php after the beta key removal.

* Exercise the drop migration against the real automation tables and their FKs

* Drop DuplicateIdentityRehearsalTest: it re-ran a frozen migration that reads the removed automations table
2026-09-05 11:03:23 -03:00
Paulo Castellano
82da3fd64b
Show disconnect alongside reconnect on lost social connections (#332)
When a social account's token expired or the connection was lost, the
Connections card only offered "Reconnect". Users who wanted to drop
that account and connect a different one had no way to do it from the UI.

The lost-connection card now renders both Reconnect and Disconnect,
reusing the existing disconnect confirmation flow. The connected-state
Disconnect button gets a data-testid too so browser tests can target
either state by account id.
2026-09-04 18:29:10 -03:00
Paulo Castellano
c40c740f51
chore(release): v1.0.9 changelog, customer email + thumbnail artifacts (#331)
* chore(release): add v1.0.9 changelog, customer email, and thumbnail artifacts

* chore(release): rewrite v1.0.9 customer email as a webhook announcement

* chore(release): tighten v1.0.9 email around what people asked for

* chore(release): rewrite v1.0.9 email around webhook value

* chore(release): write a plain v1.0.9 webhook announcement email

* chore(release): close v1.0.9 email with a G2 review ask
2026-09-04 15:17:33 -03:00
Paulo Castellano
a932c51bbb
Expose workspace webhooks through the API and MCP (#330)
* Expose workspace webhooks through the API and MCP.

The same create/update/test/rotate/replay/delete flow now lives in Actions so the web UI, REST API, and MCP tools stay in lockstep.

* Keep webhook validation local to each web, API, and MCP entry point.

* Extract MCP webhook rules into request classes and close remaining API/MCP review gaps.

* Tighten webhook updates to a field whitelist and reset failures only on re-enable.

* Treat a mismatched webhook replay as not found and mark secret rotation destructive.
2026-09-04 12:00:32 -03:00
Paulo Castellano
91a1d5ca1e
Slim webhook log broadcasts to fix Reverb payload-too-large (#329)
* Stop broadcasting webhook payloads so Reverb does not reject live log updates.

The Echo event only needs list metadata; Inertia reloads the full log after the status lands.

* Type webhook Echo payloads separately and cover delivered, failed, and pending broadcasts.

The show page still receives payload and body over HTTP; Echo only carries list metadata.
2026-09-04 10:04:07 -03:00
Paulo Castellano
58d8e066b5
Add workspace webhooks and drop the unused automation webhook node (#326)
* Add workspace webhooks and drop the unused automation webhook node.

Give workspaces HMAC-signed outgoing webhooks for the post lifecycle, with retry, auto-pause, replay, and live logs, and keep HTTP Request as the only outbound automation node.

* Tighten webhook controller and validation after review.

Drop the redundant workspace redirects, prune logs without counting, and validate events/status with Rule::enum.

* Move leftover webhook UI copy behind i18n.

HTTP status phrases, delete-cancel, and validation attribute names were still English literals.

* Build the webhook-paused email through Maizzle.

The hand-written Blade skipped the shared layout, header, and footer used by the other mail templates.

* Cover real webhook dispatch paths and restyle the webhook pages.

* Ask for the shared delete keyword when confirming a webhook delete.

The endpoint URL is a poor confirm string; posts and assets already use the common "delete" keyword.

* Fix webhook review blockers so CI can go green.

Drop leftover French automation keys, stop mutating Inertia log props, and show delivered_at instead of created_at.

* Close the remaining webhook review gaps.

Keep Echo log updates across infinite scroll, align the channel with the policy, persist log ids across retries, and fail unknown automation nodes without throwing.

* Stop webhook delivery after disable and record last sent only on success.

Queued jobs now skip paused or disabled endpoints unless the user replays, and changing the URL re-pings it first.

* Limit webhooks to owners and admins, and encrypt signing secrets.

Members can no longer create or inspect outgoing integrations, and secrets stay encrypted at rest.

* Cover webhook secret hiding, skip-ping, and failed-delivery edges.

* Send the full post on webhooks after labels and platforms are saved.

* Fix webhook payloads for integer media ids and type webhook status.

* Split the webhook show page into focused components.

* Reset live webhook logs when switching endpoints.

* Keep the newest webhook logs at the top after live merges.

* Cast media item ids to string without the extra scalar check.

* Add post.unscheduled webhooks and put the log id on the envelope.

Unscheduling is now a first-class event, and receivers can send the delivery id back so we can find the matching log.

* Translate webhook event names in the UI.

* Make the webhook show page full-width and stop stacking flash toasts.

* Translate remaining webhook UI copy in every locale.

* Sign webhook pings and drop author email from the payload.

* Send signed webhook tests after create instead of pinging on save.

Create and update only block private URLs so the receiver can copy the secret first. The show page then sends a signed webhook.test with an object data envelope.

* Polish webhook test UX and always mint the dispatch log id in the job.

Keep send-test in the actions menu (its own group) and drop the leftover constructor param so retries reuse the serialized id instead of a caller-supplied one.
2026-09-04 09:43:29 -03:00
Maciej Dzierżek
ca497e1f3c
perf(horizon): skip queues for platforms that are disabled (#321)
* perf(horizon): skip queues for platforms that are disabled

The social-publishing supervisor listens on Platform::allQueues(), which maps
over every enum case regardless of the per-platform *_ENABLED toggles. With
minProcesses => 1 that means one worker per supported platform - even on an
installation that only ever connects two or three of them.

On a single-workspace self-host that was 19 Horizon workers at roughly 75 MB
each; filtering by the toggles brought it to 9 and cut container memory from
1.66 GB to 1.06 GB, with no change in publishing behaviour.

Note on the implementation: the filter reads env() directly rather than calling
Platform::isEnabled(). Config files load alphabetically, so config('trypost.*')
does not exist yet while horizon.php is evaluated - isEnabled() would silently
return its default of true and the filter would be a no-op. This bites at
config:cache time, so it is invisible in tinker.

* refactor(horizon): filter disabled platform queues via enum

Move queue filtering to Platform::enabledQueues() and apply it from
AppServiceProvider after config has loaded, avoiding env() parsing in
horizon.php while keeping isEnabled() as the single source of truth.

* refactor(horizon): use enabledQueues directly in horizon config

Remove AppServiceProvider boot override and let isEnabled() fall back
to env when trypost config is not loaded yet.

* chore: enable Eloquent strict mode in all environments

* refactor(platform): replace enabled env key derivation with explicit match

* refactor(platform): simplify isEnabled using config default fallback

* test(platform): cover publishing queues and enabled toggles exhaustively

* refactor(platform): collapse isEnabled env fallback into one method

* refactor(platform): drop filter_var and rely on env boolean casting

* revert: keep Eloquent strict mode out of production

shouldBeStrict() in production would throw on lazy loads in queued
publish jobs and can stop posting. Restore the Laravel default.

---------

Co-authored-by: Paulo Castellano <paulo@castellanos.llc>
2026-09-03 13:51:03 -03:00
Paulo Castellano
abe687d0fa
Fall back to display name when a published-post email has no username (#325)
* Fall back to display name when a published-post email has no username.

Facebook Pages often have no vanity username, which left the email as "Facebook Page (@)". Use username, then display name, and omit the empty parentheses when both are missing.

* Move the notification account label onto PostPlatform.

The fallback belongs on the row that always has a platform, so mail and in-app notify can call one method instead of guarding a missing social account at every call site.

* Cover the in-app published/failed notification body for Facebook Pages.

The email tests were not enough — SendNotification body is what the in-app inbox shows, and that path also goes through notificationLabel().
2026-09-03 12:51:44 -03:00
Grigory Frolov
0db9e0706d
Visible Terms and Privacy links on the auth screens (#317)
* feat: visible Terms/Privacy links on the auth screens

Social-platform app reviews (TikTok explicitly) require both links to be
reachable from the public site without logging in or opening a menu.

* Cover the legal links with tests and tidy the layout

The links are a compliance artifact an outside reviewer checks, but
nothing asserted they exist. A refactor of this layout could drop the
footer silently and the next platform submission would fail the same
check that prompted the PR. A feature test asserts the shared prop
reaches both guest screens and carries whatever the install configured,
and a browser test asserts the two links actually render to a logged-out
visitor.

Declares legal on SharedData, so the props read through the interface
rather than its index signature and the inline cast goes away.

Adds rel="noopener noreferrer" to both anchors, matching every other
target="_blank" in the codebase, and orders the imports the way eslint
expects so the file lands clean rather than relying on --fix in CI.

Drops the comment explaining why the links are there: that rationale
belongs in the commit and the pull request, and the sibling comments in
this file describe markup rather than justify decisions.

* Reuse the legal sentence the register screen already had

The register screen has shown "By continuing, you agree to our Terms of
Service and Privacy Policy" in production for a long time, translated
into all sixteen locales. Only the login screen was missing it, and the
two URLs were hardcoded inside the translated string, so a self-hosted
install could not point them at its own documents.

So this keeps what already worked and changes only those two things. The
markup moves into one component, which the login screen now renders as
well. The translated sentence keeps its wording and its link labels; only
the href becomes an i18n placeholder that the component fills from
config. That is one line per locale, and no new translation keys.

Reverting the footer out of AuthSplitLayout also stops the links from
appearing on the workspace index and create screens, which reach that
layout too and are seen after login rather than before it.

The sentence no longer hides on a self-hosted install. It was hidden
because it named TryPost's own documents; now that the URLs are
configuration, an install that sets them wants it shown.

A feature test covers the shared prop on both screens and the placeholder
in every locale; a browser test covers the rendered sentence, since the
links are a compliance artifact an outside reviewer checks and nothing
guarded them.

* Let the browser assertions do their own waiting

The test hand-rolled a polling loop in injected JavaScript to wait for
the element to mount, because the project notes say browser assertions do
not wait for SPA paint.

They do. visit() returns a PendingAwaitablePage backed by
AwaitableWebpage, whose __call wraps every method in
Execution::waitForExpectation and retries until the Playwright timeout,
which defaults to five seconds. The plugin even deprecates waitForText in
favour of assertSee for this reason. The loop was re-implementing the
retry that already surrounded each call, less well and with a helper
whose name has to be unique across the whole suite because these are
global functions.

Halves the file and drops the injected script. assertSeeLink also says
more than the old check did: it asserts the labels are links, not just
text that happens to appear.

---------

Co-authored-by: Paulo Castellano <paulo@castellanos.llc>
2026-09-01 18:20:38 -03:00
Paulo Castellano
b689e3902c
Send the ad click IDs to Stripe alongside the UTM parameters (#323)
Signup already captures a click ID per ad network -- gclid, fbclid,
li_fat_id, ttclid, rdt_cid, epik -- but only the UTM parameters reached
the subscription. UTMs say which campaign a customer came from; the click
ID identifies the individual click, which is what Google and Meta need to
match a subscription back to the ad that produced it.

Every key is spelled out at the call site, so what reaches Stripe is
readable in one place without following a constant.

Click IDs are text columns on purpose, since truncating them at 255 would
destroy the value. Stripe caps a metadata value at 500 characters and
rejects the request rather than truncating, so an unbounded column
reaching it would fail the whole checkout and the customer could not
subscribe at all. Values are cut to 500 before they are sent. The UTM
columns are varchar(255) and cannot reach it; this exists for the text
ones.
2026-08-31 23:01:37 -03:00