Commit Graph

361 Commits

Author SHA1 Message Date
Matthew Meszaros 57adf4f9b2 feat: route notification.deliverEmail through GenerateNotificationHTML and drop the hand-rolled htmlEscape/inline-fragment email body 2026-06-28 05:27:28 +00:00
Matthew Meszaros 18486ecfd4 feat: add a generic branded notification email template with an optional CTA button for the in-app notification email channel 2026-06-28 05:27:28 +00:00
Matthew Meszaros 789da671d6 feat: send organization member invitations through GenerateInvitationHTML so they match the rest of the transactional mail 2026-06-28 05:27:18 +00:00
Matthew Meszaros 6b2ee63593 feat: add a branded team-invitation email template with html/template-escaped org and inviter names and a slate CTA button 2026-06-28 05:27:18 +00:00
Matthew Meszaros 7dcd5ac83f feat: render the trial-expiration job notice via GenerateTrialExpiredHTML instead of the raw unstyled h2/ul HTML fragment 2026-06-28 05:27:18 +00:00
Matthew Meszaros f57f211d20 feat: add a branded trial-expired email template rendered through the shared transactional base shell in internal/notify/templates 2026-06-28 05:27:18 +00:00
Matthew Meszaros 2c910dcdeb feat: make a campaign step's Original a first-class weighted A/B arm driven by a single draggable traffic-split bar, persisting the control share as an is_control variant row 2026-06-15 08:11:53 +02:00
Matthew Meszaros 03acdb87e3 fix: thread organization_id through contact, CRM, and unibox reads and writes so non-owner org members see and edit their workspace data, and backfill contacts.organization_id for single-membership users (migration 000049) 2026-06-15 08:11:53 +02:00
Matthew Meszaros c5dfa5e4e7 feat: remove the HTTP-request action from campaign steps and automations in favor of signed webhooks, keep fire_event for custom payloads, and drop the now-unused outbound quota plumbing 2026-06-15 08:11:35 +02:00
Matthew Meszaros 660d717456 feat: register the webhook and oauth-app-webhook routes and wire the delivery worker, audit-to-webhook bridge, and app domain resolver into the backend 2026-06-15 08:11:20 +02:00
Matthew Meszaros 9d0c432be7 feat: let OAuth apps subscribe to webhooks by declaring a url/events/secret and materializing per-org endpoints gated by each grant's scopes, with secret rotation and delivery observability (migration 000050) 2026-06-15 08:11:20 +02:00
Matthew Meszaros 173736a004 feat: add a Stripe-grade webhook platform with a typed event catalog, HMAC-signed delivery, retries with backoff, endpoint ownership verification, per-endpoint throttling, an audit-spine event bridge, and firehose emit sites (migration 000048) 2026-06-15 08:11:20 +02:00
Matthew Meszaros 0a81fd361b feat: add safehttp dial-time SSRF guards and the whdomain subdomain-aware allowlist matcher used to harden outbound webhook delivery 2026-06-15 08:11:20 +02:00
Matthew Meszaros fcafac4195 refactor: remove the campaign 'notify (webhook)' step type now that the HTTP request step covers outbound calls and there is no webhook setup surface 2026-06-14 10:48:06 +02:00
Matthew Meszaros ab24d8bbc8 feat: add a 'Fire event' action and campaign step that publish custom events to the realtime gateway (no public URL), an HTTP-request campaign step, a configurable automation dry-run test with per-step toggles, and fix the false 'updated by a teammate' toast on your own save 2026-06-14 09:52:49 +02:00
Matthew Meszaros fcdb31cda7 feat: OAuth apps always issue a client secret (drop the public/PKCE-only client type, secret required for the token exchange), add an app-logo upload endpoint, and align the docs to OAuth2 with optional PKCE 2026-06-14 09:52:49 +02:00
Matthew Meszaros 8c175d4221 feat: add an OAuth 2.1 authorization server (migration 000047 apps/codes/grants, app registration CRUD, authorization-code-with-PKCE authorize+token+refresh-rotation+revoke endpoints, RFC 8414 discovery, bearer-token validation wired into the auth middleware reusing the API-permission gates, scopes mapped to API permission bits) 2026-06-13 13:56:39 +02:00
Matthew Meszaros 20935ef061 feat: add a nil-safe per-org daily outbound-action quota (Redis daily counter, anti-abuse ceiling on the HTTP-request automation node, wired in both backend and consumer, fail-open) to bound webhook relay abuse 2026-06-13 13:41:20 +02:00
Matthew Meszaros 1fa9c65ada fix: harden every user-supplied-URL outbound path against SSRF with a shared dial-time guard (resolves the host, blocks private/loopback/link-local/metadata IPs, pins the validated IP to defeat DNS rebinding, re-validates redirects) and log automation HTTP requests + blocked attempts with org attribution 2026-06-13 13:34:19 +02:00
Matthew Meszaros 501b5009b4 feat: add an on-error branch to automation action nodes (try/catch routing, rose on-error handle, executor follows the error edge and treats the failure as handled instead of failing the run) 2026-06-13 13:15:41 +02:00
Matthew Meszaros 9aa3300f46 feat: capture per-action output in automation run history (HTTP status/ok, set-variables values, rendered channel/url/message) and render it under each action in the builder History panel 2026-06-13 13:11:58 +02:00
Matthew Meszaros 331745ecc5 feat: add a generic per-automation inbound webhook trigger (inbound.webhook event, token-gated POST /api/v1/integrations/inbound/automation/:token, migration 000046 inbound_token, background graph run with the JSON body as event payload) 2026-06-13 13:04:10 +02:00
Matthew Meszaros f1cf470121 feat: render Discord notifications as sky-themed rich embeds and Slack notifications as sky-accented attachment cards with contact and subject fields instead of plain text lines 2026-06-13 12:41:26 +02:00
Matthew Meszaros 534928cfdd feat: add a safe Set-variables (transform) automation node — computes named values from Go templates against the event + prior step output and writes them back for later steps to reuse, running the same sandboxed text/template engine as every other action value (no I/O, no arbitrary code execution) 2026-06-13 12:18:45 +02:00
Matthew Meszaros f4a2b87c50 feat: add a configurable warmbly.http_request automation action (the generic send-a-webhook/call-any-API node) — templated method/url/headers/query/body, SSRF-guarded + bounded retry, captures the response back into the event data under an output key so downstream nodes use {{.response.body...}} and conditions branch on {{.response.ok}} 2026-06-13 12:02:11 +02:00
Matthew Meszaros 3c7c0b6411 fix: finish the opaque-cursor doc sweep — change the offset query params to cursor on the CRM search operations in the OpenAPI spec, and drop the dead SearchContacts.Offset field and its docs (clamped but never used in SQL) 2026-06-13 11:00:59 +02:00
Matthew Meszaros aed4a06190 feat: unify pagination by making the offset-based CRM deals/tasks search and meetings endpoints expose the same opaque next_cursor and {total, next_cursor, has_more} envelope as every keyset list (offset hidden inside the token), updating web clients, the OpenAPI spec, and the reference docs 2026-06-13 10:47:17 +02:00
Matthew Meszaros 57e8705a82 feat: make public API list cursors opaque base64 tokens (internal/utils/paging) instead of raw record UUIDs, decoding on input with a 400 on a bad cursor; admin endpoints keep transparent UUID cursors 2026-06-13 07:18:23 +02:00
Matthew Meszaros 587eae774e feat: serve the entire customer API (auth + resources) only under /v1 with no unversioned alias, and repoint the web and admin clients to the versioned base accordingly 2026-06-13 07:18:23 +02:00
Matthew Meszaros 49b01c6b67 feat: version the public API under /v1 by mounting the customer surface under both /v1 and the bare paths, adding an API-Version response header and Deprecation/Sunset headers nudging API-key callers off the unversioned aliases 2026-06-13 06:27:01 +02:00
Matthew Meszaros 71abb12a38 feat: stop campaigns silently stalling on a transient scheduler error by retrying instead of completing the task, end past-end-date campaigns cleanly, record scheduler failures to the campaign log, add a campaign-chain reconciler, and surface failures live in the dashboard activity panel 2026-06-13 06:27:01 +02:00
Matthew Meszaros 919af47d29 feat: select the realtime transport by PUBSUB_ENABLED in backend/consumer and auto-provision the Pub/Sub topics and per-topic subscriptions on boot so prod never publishes to a subscription the Elixir fanout never created 2026-06-13 06:26:48 +02:00
Matthew Meszaros c5911fe37a feat: fire run_automation on campaign instant branches (reply/open/click) through a wired AutomationRunner instead of stamping the action node sent without ever running the flow 2026-06-13 06:26:48 +02:00
Matthew Meszaros ba89a932cd feat: run the label_email action on reply — campaign instant and scheduled paths via advanced LabelThread/LabelLatestThreadForContact, automations via execNativeAction reading thread_id and _user_id from the reply payload — and move the native-actions adapter to a shared package wired into the consumer too so reply/bounce/warmup automation actions stop silently failing 2026-06-12 16:44:16 +02:00
Matthew Meszaros 4762256c49 feat: add the warmbly.label_email native action type and the label_ids action config field across the campaign ActionConfig and the IntegrationAction enum + IsNativeAction 2026-06-12 16:44:07 +02:00
Matthew Meszaros 496e474756 feat: add additive unibox thread-label repo methods — AddThreadLabels (insert-only, category-ownership guarded) and LatestThreadIDForContact (exact from_addr address match, never an ILIKE substring) — to back a reply-only label-email action 2026-06-12 16:44:07 +02:00
Matthew Meszaros a7a43e0c4c feat: rename the campaign sequences resource to steps across the API and URL (/campaigns/:id/steps), the JSON fields (target_step_id, step_id/step_name/step_index, analytics steps[], create body steps), the web client/models/route segment/labels, the audit step entity type, and the wire-contract docs; internal Go type names and the Kafka avro schema stay 2026-06-12 09:38:11 +02:00
Matthew Meszaros 8de7e32857 feat: treat a campaign step's own email as the A/B control arm so contacts split across the original plus active variants by weight, and redesign the step variants editor to show the original and variants as one weighted set with live split percentages 2026-06-12 08:04:03 +02:00
Matthew Meszaros 0fd3d03f55 feat: add org-wide team presence privacy columns (presence_show_online, presence_show_activity) with update plumbing and a PRESENCE_POLICY_UPDATED realtime event so a settings change re-gates connected sockets live 2026-06-12 05:59:01 +02:00
Matthew Meszaros 8547fe97eb feat: scope unibox MarkSeenBulk by organization so a non-owner member opening a thread clears the shared org-wide unread state instead of updating zero rows 2026-06-12 05:14:50 +02:00
Matthew Meszaros 4db027a342 feat: scope unibox GetByThread by organization instead of user_id so non-owner members see the full conversation they already see in the org-scoped inbox list, not an empty thread 2026-06-12 05:10:00 +02:00
Matthew Meszaros ba3ab98e1e feat: bridge realtime events over Redis pub/sub when Google Pub/Sub is unconfigured (RedisBus publisher in backend/consumer plus Realtime.Redis.EventSubscriber and a shared EventBroadcaster) so dashboard live updates and presence-driven collaboration actually fire in local dev 2026-06-12 04:49:58 +02:00
Matthew Meszaros 21b4ba4ea0 fix: let members with the integration permission save automations (write was gated on manage-settings while read used use-integrations, so a manager could open the builder but 403 on save) and surface the backend's real error in the save toast instead of a generic message 2026-06-11 18:15:55 +02:00
Matthew Meszaros 6a3b8ac0eb fix: scope the unibox unread-count endpoint by organization_id so the sidebar Inbox badge counts the workspace's unread mail instead of always showing 0 for non-owner members 2026-06-11 18:05:18 +02:00
Matthew Meszaros 594c696bf6 fix: scope the unibox inbox list and overview by organization_id (via the workspace's email accounts) instead of the logged-in user_id, so every member sees the org's incoming mail; per-user thread labels and snoozes stay personal 2026-06-11 17:57:52 +02:00
Matthew Meszaros 21032a8fce fix: scope the contacts list by organization_id instead of user_id so all members see the workspace's contacts (and per-contact campaign badges); categories badge still user-scoped pending an organization_id column on categories 2026-06-11 17:52:36 +02:00
Matthew Meszaros 88e241d536 fix: scope the analytics dashboard (overall stats, recent activity, top campaigns, daily trend, account health) by organization_id instead of user_id, so every workspace member sees the org's analytics rather than an empty dashboard 2026-06-11 17:49:39 +02:00
Matthew Meszaros a21dc57e07 fix: scope campaign list/detail/count queries by organization_id instead of user_id, so all workspace members see the org's campaigns rather than only the creator 2026-06-11 17:47:04 +02:00
Matthew Meszaros 6ae5e52c4e fix: scope email-account list/detail queries by organization_id instead of the logged-in user_id, so every member of a workspace sees the org's mailboxes (not just the owner who connected them) 2026-06-11 17:43:56 +02:00
Matthew Meszaros 709b77cdc6 fix: notification email links use the configured APP_URL instead of a hardcoded app.warmbly.com, so dev and self-hosted deployments link to the right host 2026-06-11 12:43:17 +02:00