Commit Graph

151 Commits

Author SHA1 Message Date
30cf46370c fix(cards): duplicate card entries after adding card through stripe webhook fixed 2026-05-22 21:21:02 +02:00
31f0698d63 chore(gitignore): update gitignore 2026-05-22 21:19:32 +02:00
910b7f3141 chore(gitignore): update .gitignore with the claude skill to map codebase 2026-05-22 20:05:20 +02:00
19e493a3d5 feat(bring): send X-Bring-Client-URL header on booking reqs, defaulting to https://sshops.uk and overridable via BRING_CLIENT_URL env var 2026-05-22 20:02:43 +02:00
2e6dda190f feat(audit): persist every order-scoped audit.X(...) call to a new order_events table alongside the existing slog line, builds the foundation for an admin order timeline view without changing the slog primary path 2026-05-22 20:01:29 +02:00
96e76f7e5b feat(slack): add slack mark status buttons for bring orders, post-label thread now block kit, synthesize shippo webhook over loopback to reuse state-date guard and delivered transition 2026-05-20 18:09:34 +02:00
9d3ac9761d feat(tui): add tracking redirect endpoint, render the short uril in the tui order detail instead of the old broken stuff ive been trying. this change is made by claude code and has not been reviewed carefully, this might break 2026-05-20 15:26:38 +02:00
e49fa01ca9 feat(tui): use BEL terminator for tracking url osc 8 link - hopefully this works 2026-05-20 15:12:56 +02:00
d9e9895913 revert(tui): render raw tracking url, last change wouldnt render the url for some reason 2026-05-20 15:07:14 +02:00
f73e1d18da feat(tui): make tracking url clickable osc 8 hyperlink 2026-05-20 14:55:32 +02:00
29f57000a7 feat(notify): pin paid order messages in the #orders channel in slack, unpin on label purchase 2026-05-20 14:01:28 +02:00
1aaf82d7d6 feat(tui): poll orders every 10 seconds so that the order status refreshes live in the tui without logging out and in 2026-05-19 20:12:03 +02:00
9515603b32 feat(tui): split account orders into active and history tabls 2026-05-19 19:50:21 +02:00
bfd183bfbf fix(ci): remove go module cache step since its redundant 2026-05-15 15:34:21 +02:00
e43040f702 feat(notify): post order vents into per-order slack threads - made by claude 2026-05-15 12:25:45 +02:00
2966474ff5 feat(notify): render slack order notification as block kit receipt with line items 2026-05-15 12:11:16 +02:00
0e4d267adb fix(orders): write NULL instead of empty string for unpopulated shipp_transaction_id 2026-05-15 11:27:00 +02:00
c1b3ccf302 feat(notify): add buy label button via slack 2026-05-15 10:57:23 +02:00
f6eed76456 feat(notify): post Slack message when an order is paid 2026-05-15 08:54:23 +02:00
e5ec247a6b feat(orders): customer-facking tracking labels and webhook response body
the big colored status on the order detail page used to just say
"Shipped" the moment i bought the label, which didnt really feel right
since the carrier hadnt actually picked anything up yet. now it walks
through "Label created" -> "In transit" -> "Delivered" as real scans
come in, and keeps the "Delivery Issue" / "Returned to sender" labels
for unhappy paths

also wired up the existing tracking_status_details column to the tui. i
skipped it before but the response actually includes a description of
the delivery, that is now subtext under the colored statu.

and the shippo webhook now returns a small json body instead of an empty
200. this is just for the operator that runs the curls commands to
     update the shipment, which makes debugging a lot less guesswork
2026-05-14 21:17:50 +02:00
e29dd6b30d fix(middleware): redact query strings from access logs 2026-05-14 13:47:20 +02:00
ee6e14a424 feat(shipping): shippo webhook fro tracking status updates 2026-05-14 13:30:37 +02:00
387758aa13 feat(shipping): add Bring booking client and country dispatch for label purchase 2026-05-13 22:14:32 +02:00
ff9615b75a fix(shippo): fix rate-filter 2026-05-13 08:58:09 +02:00
a2afc47f32 refactor(shipping): check ALREADY_LABELED before INVALID_STATE in PurchaseLabel 2026-05-12 21:47:06 +02:00
37ad51949b fix(shipping): require sender email on shippo address_from
Shippo's transaction validation rejects labels where the from-address
has no email — fails at the poll step with
"Attribute address_from.email must not be empty."

Add Email to Address, plumb it into addrPayload, and read
SHIP_FROM_EMAIL alongside the other SHIP_FROM_* env vars.
2026-05-12 21:17:32 +02:00
99f34788ea fix(shipping): round weights before posting to shippo
Shippo's /v1/orders endpoint rejects >2 decimal places on parcel
weight and >4 on line_items[].weight. Converting ounces to kg
(oz * 0.0283495) routinely produces 6-decimal values, so the first
real label purchase 400'd with "Ensure that there are no more than
N decimal places".

Round at the payload boundary in createShippoOrder rather than in
the caller — keeps the precision of the source (Coffee.Ounces) and
only loses it when the wire format demands it.
2026-05-12 21:12:31 +02:00
c22b64d6fa feat(shipping): shippo label purchase endpoint
Adds POST /api/v1/orders/{id}/label (admin-only) that buys a shipping
  label through Shippo and writes carrier + tracking + label fields back
  onto the order, transitioning paid -> shipped.
2026-05-12 20:57:11 +02:00
a71f72ad71 chore: update typing 2026-05-10 21:08:57 +02:00
700ad4926b feat(models): derive order state from status + tracking 2026-05-09 16:50:33 +02:00
c5f4e492c0 refactor(orders): embed tracking on orders, drop shipments table
realized per-shipment table is kinda bad, switched to single embedded tracking blob. since im only
shipping from my house  no split is needed. adds tracking_status, tracking_status_details,
tracking_status_updated_at columns. reverse-backfills any historical shipments rows back onto orders
(idempotent, table-guarded). UpdateTracking rewritten to write to otder columns direcly
2026-05-09 10:08:32 +02:00
f88fe1d8d3 fix: preload shipments on view init endpoint 2026-05-08 15:15:53 +02:00
c6ff75f2a5 chore. ignore local DB backups and prod backups 2026-05-08 14:59:47 +02:00
add6e573b3 feat: extract shipments to dedicated table 2026-05-08 14:54:17 +02:00
9b45fb5138 updating 2026-05-08 13:43:03 +02:00
1849e324fc feat: add admin tracking updates 2026-05-08 10:47:57 +02:00
c90dadf9ad test was failing after some AI change. this should fix it 2026-05-06 13:53:38 +02:00
38cdda3d54 cleanup 2026-05-06 13:41:23 +02:00
c037a6e9f9 removing unused param 2026-05-05 22:05:29 +02:00
ffb476ec2d updating variable to camelcase 2026-05-05 22:04:24 +02:00
177b080117 some change 2026-05-05 21:04:02 +02:00
9bf57b4048 perf(loadtest): retune overnight scenarios under rate limits
Document and respect per-IP rate limits (200/min global, 20/min auth,
60/min webhooks) by holding scenarios well below the ceilings so the
histogram captures real handler latency instead of 429s. Total combined
~148 req/min over ~10h. Adds summaryTrendStats so reports include
p95/p99/max alongside avg.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 09:51:27 +02:00
c96f922c70 refactor(tui): rebuild header with lipgloss/table
Replaces the manual string-concat + separator layout with a
lipgloss/table row, mirroring the reference TUI structure. Drops the
parallel bold-vs-gray styling for a single-weight accent/base scheme,
extracts a small tab() closure to remove the duplicated active/inactive
branches, and uses m.renderer.NewStyle() for the border so per-session
color profiles render correctly under SSH.

Cart label still tracks inCartFlow() (covers shipping/payment/review/
confirm), which the reference doesn't need since it has no multi-step
checkout.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 09:51:21 +02:00
4e28b812bd chore: gitignore runtime artifacts, drop orphan 00 and AI roadmap
Add loadtest/run.pid and loadtest/run.err to .gitignore so k6 runtime
state stops tagging along on git add. Also ignore the orphan /00 file
(stray redirect of pkg/observability/tracing.go) and ROADMAP.md (AI-
synthesised audit doc, not part of the project canon).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 09:51:14 +02:00
7b92636b64 add video 2026-05-02 18:16:21 +02:00
17df27ae20 some updates 2026-05-02 17:48:07 +02:00
52d58583e9 db telemetry 2026-05-02 16:29:07 +02:00
327bb9c867 cleanup 2026-05-02 11:28:28 +02:00
9a5fff81bf fix: defer component logger creation past package init
Package-level slog.With() vars are evaluated at init time, before
observability.InitLogger() runs in main. They captured the original default
slog handler and bypassed the countingHandler that increments
log_messages_total — so warn/error calls from these packages weren't being
counted.

Convert to functions so each call resolves slog.Default() at use time and
inherits the active counting handler.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 22:26:03 +02:00
1c10cf4390 feat: structured logging with component tags + severity levels
Convert log.Printf calls in webhook, reconcile, addresses, and auth handlers
to slog with a consistent `component` attribute. Severity now reflects
operational urgency:

- warn: signature failures, declined cards, unknown fingerprints, vendor
  validation rejections, reconciler patching a missed charge
- error: DB write failures, malformed Stripe payloads, token generation
  failures
- info: successful order/card events, normal reconciler ticks

Lets Loki/Prometheus filter by component (e.g. {component="webhook"}) and
gives the log-level dashboard panel real warn/error series instead of a
flat info line.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-01 22:14:06 +02:00