Commit Graph
831 Commits
Author SHA1 Message Date
Matthew Meszaros 3bf8d4bdfa Merge remote-tracking branch 'origin/main' into feature/mailbox-fair-use-allowance 2026-09-04 21:25:53 -07:00
Matthew Meszaros c7da25d126 fix: never leave a per-IP rate-limit counter without a TTL, which would block that address forever once it passed the limit, by dropping the key and failing open when EXPIRE fails and repairing a missing expiry on the reject path, and assert the exit status as well as the message when the CLI installer rejects a flag 2026-09-04 21:12:27 -07:00
Matthew Meszaros bf46839fb7 feat: address the CodeRabbit review on the mailbox allowance work by reserving the mailbox slot inside the account insert transaction under a per-organization lock so concurrent connects cannot both take the last one, resolving the storage limit under the quota lock so a plan change cannot be raced past, deleting refused attachment objects on a context that outlives the request, settling already-connected rows before the bulk batch spends allowance, leaving password columns out of the failed-rows CSV, stopping click propagation from the portalled allowance dialog, and counting bulk progress once 2026-09-04 21:11:25 -07:00
Matthew Meszaros b35e111705 fix: address the review on the CLI PR: give the sign-in handshake its own per-IP budget so a 200-poll login cannot lock the address out of the browser login, keep https for a remote host that names a port instead of sending a bearer token in cleartext, report truncation when a paginated walk stops at max-pages, accept a piped secret with no trailing newline, normalise WEBSOCKET_URL on its suffix so a bare /socket becomes a real endpoint, destroy a minted secret the moment its code expires, gate cli-installer-ci on the required status check, print a rejected flag instead of an unbound-variable error, and use a portable sha256 so the packaging runs on macOS 2026-09-04 21:03:58 -07:00
Matthew Meszaros 3596a9770f feat: make mailboxes truly unlimited under a fair-use allowance of one mailbox per daily send the plan includes (15,000 on Business), drop the 200 hard cap and the 5-per-day connect throttle, open a request-more dialog with inline limit requests and plan upgrade when the allowance is reached, add bulk CSV mailbox import with live progress and a downloadable failed-rows file, reserve attachment storage atomically under a per-org lock so concurrent uploads cannot pass the quota (#326), report storage and the allowance on the billing usage meters, and honor approved daily-send overrides in the sender 2026-09-04 21:00:26 -07:00
Matthew Meszaros d80816efa1 feat: make a successful warmbly auth login always the active host instead of deciding it from three overlapping conditions, and say why the CLI state file tolerates an unwritable home 2026-09-04 20:16:56 -07:00
Matthew Meszaros f68918d7f6 Merge remote-tracking branch 'origin/main' into feature/warmbly-cli 2026-09-04 20:14:46 -07:00
Matthew Meszaros f46fc86a7c feat: add the warmbly CLI at cmd/cli, a signed-in multi-host client of the public REST API with 30 command families over one spec table, gh-shaped api passthrough, live event tailing, tables on a terminal and JSON in a pipe, and a confirmation gate in front of everything that puts real mail on the wire 2026-09-04 20:14:33 -07:00
Matthew Meszaros 028689fd2e feat: device-code sign-in for the CLI, with a browser approval page at /cli that mints a scoped API key, self-revocation at DELETE /api-keys/self so a read-only credential can always end itself, and app_url plus websocket_url on /auth/config so a client can find the dashboard and the realtime gateway on a self-hosted layout 2026-09-04 20:14:27 -07:00
Matthew Meszaros 5fd9aedb80 Merge remote-tracking branch 'origin/main' into feature/plan-upgrade-paywall-modal 2026-09-04 09:11:51 -07:00
Matthew Meszaros e5360713e7 Merge remote-tracking branch 'origin/main' into feature/install-script-setup-wizard 2026-09-04 06:43:12 -07:00
Matthew Meszaros d78e4423e4 fix: address the review of the notifications work — bound detached alert delivery to a fixed pool that drops and logs rather than spawning a goroutine per event, never echo a transport error that embeds the webhook URL, treat an emptied channel target as cleared so switching transport cannot post Slack payloads to a Discord URL, emit user.registered for invited signups and the worker alert after a successful reassignment, format Stripe amounts by currency exponent, refuse an upgrade while the plan list is still loading instead of silently opening the portal, apply a fixed discount to the annual invoice rather than to each month, convert proration from minor units, contain focus in the nested enterprise dialog, hide the odometer wheels from assistive tech, and drop the last dedicated-IP row from the pricing matrix 2026-09-04 06:33:20 -07:00
Matthew Meszaros 9a2184fda7 Merge remote-tracking branch 'origin/main' into fix/step-scoped-attachments 2026-09-04 06:28:26 -07:00
Matthew Meszaros 77058a2cb4 feat: address the review on the installer branch by keeping the database password out of pg_dump's argv, excluding backup bundles from the blob root they are written into, tolerating blobs that change or vanish mid-archive, making the instance-settings bootstrap a single atomic insert, and validating the release tag before it is written into .env 2026-09-04 06:24:05 -07:00
Matthew Meszaros fb868dfe40 feat: delete the storage objects of a step's attachments when the step is deleted, since the attachment rows cascade away with the sequence and left their bytes counted against the organization's storage quota with no row left to reach them 2026-09-04 06:20:26 -07:00
Matthew Meszaros a54774c0a5 feat: install the attachment upload's body cap before the first form field is read, since reading step_id parses the whole multipart body and left the MaxBytesReader that followed it with nothing to limit 2026-09-04 06:19:29 -07:00
Matthew Meszaros 85f3dad215 feat: send the files a step actually carries by honouring campaign_attachments.sequence_id in the send, test-send and preview paths instead of attaching every campaign file to every step, count them per step in the preflight content score, and refuse an upload naming another campaign's step 2026-09-04 06:16:16 -07:00
Matthew Meszaros 05e19ffb69 feat: scope the campaign attachment routes to the caller's organization, so listing, uploading to or deleting from a campaign id belonging to another workspace is a 404 instead of leaking presigned download URLs, adding a file that workspace then sends, or deleting their stored objects 2026-09-04 06:16:16 -07:00
Matthew Meszaros 81d6ba4520 Merge remote-tracking branch 'origin/main' into feature/plan-upgrade-paywall-modal 2026-09-04 06:09:14 -07:00
Matthew Meszaros a0c9d5a5b0 feat: add operator notification channels an admin configures in the panel (Discord, Slack, HMAC-signed webhook, or email) with per-channel event subscriptions, a synchronous test probe, SSRF-guarded URLs and credentials redacted on read, wire nine instance events including the enterprise inquiry that previously wrote a row nobody was told about, and reword the plan-specific limits copy so it reads correctly without billing 2026-09-04 06:02:40 -07:00
Matthew Meszaros d68bbcd2ab feat: add a one-command self-host installer at warmbly.com/install.sh with an interactive data-control wizard, give docker-compose.yml image keys and per-store volume variables, add an image-mode updater, move engagement/form/audit retention into instance settings, and add warmblyctl backup/restore 2026-09-04 05:49:54 -07:00
Skylinerffm97 1749d5c48e feat: persist schedule_windows on POST /campaigns by adding the field to models.CreateCampaign, validating it with CampaignScheduleWindows before the transaction and writing it in the create INSERT, with a unit test for rejected windows, a live repository test for the round trip and the create field table in the campaigns API reference (issue #307 item 5) 2026-09-04 12:58:09 +02:00
Matthew Meszaros 483d6c2fe7 Merge remote-tracking branch 'origin/main' into fix/issue-307-test-send-preview-from-name 2026-09-04 03:34:35 -07:00
Matthew Meszaros 915390f42a feat: match the closing body tag case-insensitively when placing the mailbox signature so a pasted document written with </BODY> still gets it inside the body, drop a step preview response whose request has been superseded so an out-of-order reply cannot repaint stale copy, and keep pulling mailbox pages in the preview sender picker while an enabled campaign sender is still unaccounted for 2026-09-04 03:11:53 -07:00
Matthew Meszaros 64e6e3a502 Merge remote-tracking branch 'origin/main' into fix/campaign-daily-budget-deferrals 2026-09-04 03:06:45 -07:00
Matthew Meszaros de33b03891 feat: give the HTML mailbox signature its own block with a top margin instead of a hard double line break that stacked against the body's trailing margin and rendered as two to three blank lines in Apple Mail and Outlook, insert it inside the document when the body carries a closing body tag as the tracking pixel and opt-out footer already do, and say so in the mailboxes guide 2026-09-04 03:01:19 -07:00
Matthew Meszaros 2a19bf0519 Merge remote-tracking branch 'origin/main' into fix/campaign-daily-budget-deferrals 2026-09-04 02:59:01 -07:00
Matthew Meszaros 5c9d365163 feat: pick a closed-hours mailbox that stays closed for the next ten minutes so the scheduling pass cannot run after it opens, and assert the mixed-pool deferral lands on the reopening from both sides instead of accepting any earlier time 2026-09-04 02:59:00 -07:00
Matthew Meszaros 997aa2c748 Merge remote-tracking branch 'origin/main' into fix/issue-307-test-send-preview-from-name 2026-09-04 02:58:40 -07:00
Matthew Meszaros 8ce331a7ac feat: fix the campaign test-email endpoint answering 404 for every caller by scoping its campaign lookup to the organization instead of the user, let it send from any mailbox of the organization and render a real contact through a new contact_id, attach the campaign's files, and extend the template preview with contact_id, campaign_id and account_id so it applies the signature, opt-out footer and plain-text rule the send path does 2026-09-04 02:58:36 -07:00
Matthew Meszaros f844c14804 feat: carry the mailbox display name in the emsg body blob of every send so a renamed sender reaches the worker on its next message instead of waiting for an inactive-active toggle, read it into the From header of the Gmail, Graph and SMTP clients with the name cached at load time as the fallback, and say so in the mailboxes guide 2026-09-04 02:58:36 -07:00
Matthew Meszaros eb85224cc8 Merge remote-tracking branch 'origin/main' into fix/contact-timeline-opaque-cursor 2026-09-04 02:57:59 -07:00
Matthew Meszaros 850ff2b6a2 feat: lock the campaign row for update while replacing its linked segments so two concurrent replacements cannot commit the union of their sets, and return the status read under that lock from ReplaceForCampaign so the wake or restart reaction no longer depends on a post-commit link lookup that a concurrent detach could empty 2026-09-03 20:46:27 -07:00
Matthew Meszaros ab0c756aa2 feat: show a campaign's linked segments on the Leads tab as chips with live enrolled and held-out counts that filter the list, explain an empty list caused by an empty segment or hand-removed members and add them back in one click, add a scope-aware Export to campaign leads and segment members with a campaign-ready preset that carries lead status and engagement, write the links and the enrolment of PUT /campaigns/:id/segments in one transaction so a failed enrolment changes nothing instead of answering added 0, restart a completed campaign when a one-shot enrol adds leads, clear the manual-removal record when a contact is added by hand, count every link's members in one scan, audit sweep enrolments so open Leads tabs refresh, and document it in the campaigns and segments guides and the API reference 2026-09-03 20:38:27 -07:00
Matthew Meszaros 4bc971eecb feat: resume a campaign whose pool mixes capped and hours-closed mailboxes at the earlier of tomorrow and the closed mailbox's reopening, log the daily cap only when every usable mailbox is capped, and add the mixed-pool live regression 2026-09-03 20:26:21 -07:00
Matthew Meszaros 0981635c3a feat: reject a contact timeline cursor whose source rank names no merged table with a 400 in the handler through ContactTimelineSource.Valid, since rank zero is reserved for the legacy before bound and a rank past the last source would re-admit the events at the cursor's instant 2026-09-03 20:21:42 -07:00
Matthew Meszaros 630dd4cab1 feat: add a live repository test that walks ten timeline events, eight of them at one instant across progress stamps, notes and activities, in pages of three and proves nothing is skipped or repeated, each cursor is the last event's key, the final page carries no cursor, and before still means strictly older 2026-09-03 20:19:16 -07:00
Matthew Meszaros a4665579de feat: accept an opaque cursor on GET /contacts/:id/timeline, keep before as a deprecated alias that maps onto the keyset at rank zero, and answer an invalid cursor, before or limit with a 400 instead of silently ignoring it 2026-09-03 20:19:16 -07:00
Matthew Meszaros 88d8a2ba8a feat: page every contact timeline source on a row comparison against the (at, source, id) cursor, unnest campaign progress stamps to one row per event so the limit and the logged-open and logged-click suppression apply per event, fetch one row past the page from every source, return all matching suppression entries, and encode the next cursor from the last event's key 2026-09-03 20:19:16 -07:00
Matthew Meszaros 18a1862d82 feat: rank the contact timeline's sources in models.ContactTimelineSource, give every event a ContactTimelineKey (at, source, id) with the newest-first comparison the merged sort uses, and add the standard pagination envelope to ContactTimelineResult while keeping has_more as a mirror 2026-09-03 20:19:16 -07:00
Matthew Meszaros 407a85ecf9 feat: cover the merged-feed cursor codec with a round-trip test that keeps sub-second precision and rejects a bare timestamp, a wrong-version token, bad base64 and trailing garbage 2026-09-03 20:19:16 -07:00
Matthew Meszaros 0cb4f312a0 feat: add a versioned opaque cursor codec for merged feeds to internal/utils/paging that carries an (at, source rank, id) keyset position and decodes a malformed or wrong-version token to a 400 2026-09-03 20:19:16 -07:00
Matthew Meszaros 1c88c2196b feat: add live tests proving wake-ups leave the daily budget and min-gap untouched, real sends still count, and a capped campaign parks a wake-up with a single daily log line instead of pausing 2026-09-03 20:18:43 -07:00
Matthew Meszaros 6bc6bf4345 feat: reword the no-eligible-mailbox pause on the start endpoint, the task auto-pause, and its test to name the only cause left (a sending behaviour profile with no working days) now that budget and window gates defer 2026-09-03 20:18:43 -07:00
Matthew Meszaros 711be1f8c2 feat: defer a campaign whose mailboxes are all at their daily cap, outside their own hours, resting, or held by warmup health instead of auto-pausing it, replace the unreachable push-to-tomorrow recompute, and log the daily-cap and unavailable-pool decisions once per UTC day 2026-09-03 20:18:43 -07:00
Matthew Meszaros 620e89d528 feat: exclude action and wait steps from the workspace daily email limit by joining the step kind, since they stamp sent_at for routing without sending mail 2026-09-03 20:18:43 -07:00
Matthew Meszaros 3204cf41bd feat: apply the reserved-or-confirmed send filter to the workspace sent-today count, the advisor mailbox volume windows, and the deliverability dashboard denominator so campaign wake-ups stop inflating sent totals 2026-09-03 20:18:43 -07:00
Matthew Meszaros ec9a3147b6 feat: count a completed campaign task as a mailbox send only when it holds a step reservation or a worker-confirmed Message-ID, so the chain's deferral and pause wake-ups no longer spend the daily budget or reset the min-gap and rotation clocks (issue #306) 2026-09-03 20:18:43 -07:00
Matthew Meszaros 37d328c86a feat: add a partial index on campaign_contact_progress.dispatch_task_id so the daily send counters can look a task's reservation up without scanning progress 2026-09-03 20:18:43 -07:00
Matthew Meszaros b9e81b6947 feat: fetch one row past the page from the per-event open and click sources of the contact timeline so a page filled by either source alone still reports that more events follow 2026-09-03 06:14:14 -07:00