18 Commits

Author SHA1 Message Date
ordinarthur
3aaa319264 feat(web): pricing page + plan section in settings (Phase 3)
- web/src/pages/Pricing.tsx: tier comparison, monthly/yearly toggle,
  stripe checkout CTA
- web/src/pages/Settings.tsx: Plan section shows Free/Pro + Upgrade or
  Manage Subscription (opens Stripe Customer Portal)
- web/src/lib/api.ts: startCheckout(), openBillingPortal() + extended ApiUser
- footer on Home gets a discreet "Pricing →" link
- k8s/secrets.example.yml: documents STRIPE_* env vars
- .gitignore: exclude .env files to prevent leaking credentials

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 13:28:18 +02:00
ordinarthur
35aca309c9 refactor: remove cross-network device pairing
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 48s
Remove dead pair-code flow end-to-end (client, server, protocol) and
the orphaned PublicRoomPanel/DevicePairingPanel/Pair components that
were no longer reachable from the UI.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 13:18:02 +02:00
ordinarthur
2452f2642a feat: inbox of received transfers in /settings
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 47s
Auto-claim on receive so /settings shows transfers others sent you.
Filename decryption stays client-side using the key stored in localStorage
when the share link is opened.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 12:47:10 +02:00
ordinarthur
c46b23b8ec feat: shared-links history in /settings with revoke + copy
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 39s
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 12:26:04 +02:00
ordinarthur
dbd500b0b5 feat: simpler Home + password-protected cloud links
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 51s
Home is now two sections, no clutter:

- Nearby — tap a device on the same Wi-Fi and the composer takes its
  place (drop files, optionally write a note, send). Pair-across-networks
  and public-room panels moved off the main page into the footer as a
  single discrete link (/pair).
- Share a link — inline WeTransfer-style card. Pick a file, optionally
  expand email / password / expiry, upload. Result shows QR + copy link
  + the password to share out-of-band.

The password gate is a server-side access control layer on top of the
existing E2E encryption: scrypt-hashed on create, verified on consume.
The encryption key still lives only in the URL fragment; the password
does not participate in the crypto.

- server: password_hash column (migration 0002), scrypt+timingSafeEqual
  verify on /consume, requiresPassword surfaced on the HEAD response.
- web: Composer merges drop zone + text note into one surface; Receive
  shows a password prompt when requiresPassword is true and recovers in
  place on a wrong attempt.
- responsive: mobile-first paddings, DeviceChip shrinks on narrow widths,
  no 2-col grids on the main page.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 11:54:05 +02:00
ordinarthur
0b639dfc3c feat: encrypted cloud relay (Phase 2)
Adds a "Via AnyDrop" flow for senders who need to reach someone not
present on the mesh. The file is sealed client-side (XChaCha20-Poly1305),
uploaded directly to an in-cluster MinIO bucket via a presigned PUT, and
handed off to the recipient as a URL whose fragment carries the key.
The server only ever sees ciphertext, opaque metadata blobs, and sizes.

- server: transfers table (drizzle migration), /api/transfers CRUD +
  consume endpoint, presigned PUT/GET via @aws-sdk/client-s3, cleanup
  loop that purges expired + exhausted blobs.
- web: @noble/ciphers sealFile/openFile, high-level sendCloud/receive
  helpers, CloudSharePanel on Home, /r/:id receive page, /inbox page
  for signed-in users (sent + received tabs).
- k8s: MinIO StatefulSet with bucket-init initContainer, S3 env vars
  on the server Deployment (credentials pulled from minio-credentials
  Secret).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 11:09:58 +02:00
ordinarthur
c18d995c3f feat(web): Paper & Envelope design system
Replace generic slate/indigo dark theme with a custom editorial
direction: warm paper neutrals, oxblood signal, Fraunces serif
display, Inter body, JetBrains Mono for codes. SVG paper-texture
noise overlay and thin rules across the app.

Refactored: Home, Settings, JoinRoom, Pair, Share, plus every
modal and panel (DropZone, DevicePairingPanel, PublicRoomPanel,
ProfileSetup, TextShareModal, ReceiveDialog, TransferProgress,
PeerList, PeerAvatar).

Also drops three pre-existing Uint8Array/BlobPart strictness
errors so the production build is green again.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 10:49:15 +02:00
ordinarthur
2913618ee6 feat: stealth accounts + data layer (Phase 1)
Some checks failed
Build & Deploy / build-and-deploy (push) Failing after 1m47s
2026-04-20 09:57:22 +02:00
ordinarthur
dd37b49ce4 fix: pairing flow + code review bugfixes + remove notifications
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 45s
Pairing:
- Always request fresh code when opening "Mon code" tab
- Clear stale pairCode on modal close/tab switch
- Show errors inline for invalid codes (don't close modal)
- Delete pair code after first use (server)
- Validate code length server-side

Notifications removed:
- Remove all showLocalNotification calls (peer-joined, transfer, text)
- Push setup now runs only once per session (no memory leak)

Other fixes:
- Clear pendingFilesRef on disconnect
- Set transfer status to "transferring" immediately on send start
- Select offline peer when tapping to wake
- Fix file receiver blob restoration race condition (await arrayBuffer)
- Clear selectedPeerId after share-target send
- Add returnValue to beforeunload handler

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 13:22:33 +02:00
ordinarthur
d8721b6d0c feat: pairing via short code instead of QR only
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 45s
iOS PWA (home screen) has separate localStorage from Safari, so
QR-based pairing breaks. Now pairing uses a 6-character code:
- Device A taps "Appairer" → shows a code like "A7K9XB"
- Device B taps "Appairer" → "Rejoindre" tab → enters the code
- Server resolves the code to the groupId, devices are linked

Codes expire after 5 minutes. Pairing is permanent.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 13:04:58 +02:00
ordinarthur
8212bc7391 refactor: simplify layout flow
Some checks failed
Build & Deploy / build-and-deploy (push) Has been cancelled
1. Peers (available devices)
2. Pair device + Public link (side by side buttons, modals for details)
3. File drop + Text send (only when a peer is selected)
4. Transfer progress

Removed the always-visible drop zone and join-code input from
PeerList. Pairing and public room now open as overlay modals.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 12:52:54 +02:00
ordinarthur
d6f7a2374b feat: permanent device pairing for cross-network sharing
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 43s
Adds a groupId-based pairing system so devices can always see
each other regardless of network. Scan a QR code once from the
other device, and they're permanently linked via a shared group
stored in localStorage. No account, no email — just one-time QR
scan like Bluetooth pairing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 12:39:21 +02:00
ordinarthur
2e3408e8d7 fix: local IP detection regex + add join code UI when no peers found
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 38s
The ICE candidate regex was matching the wrong part of the candidate
string. Also, iOS Safari blocks local IP detection via WebRTC (mDNS
obfuscation), so when no peers are found, show a prominent "create
link" button and a join-by-code input for easy cross-network pairing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 12:34:01 +02:00
ordinarthur
d1c4b3d196 feat: offline peers visible + push wake + iOS share sheet + no self-notification
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 44s
- Offline push-subscribed devices appear dimmed in peer list
- Tap offline peer to send wake push notification
- Skip self-notification (own deviceId excluded)
- iOS/Android share sheet via Web Share Target API
- Online/offline indicator dot on peer avatars
2026-04-14 12:12:08 +02:00
ordinarthur
0034e91672 feat: push notifications + iOS share sheet integration
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 37s
- Web Push API for offline device notifications (VAPID)
- Custom service worker with push + share target handlers
- iOS/Android share sheet support via Web Share Target API
- Dedicated /share page with one-tap send to nearby peer
- Background tab notifications for incoming transfers
- Persistent deviceId per device
2026-04-14 12:05:59 +02:00
ordinarthur
d8d747276a fix: gate signaling behind profile setup, fix SW cache
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 37s
2026-04-14 11:55:51 +02:00
ordinarthur
4dbdfedae0 feat: local device profile (name + photo) replaces animal naming
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 43s
- Users set their own device name and optional profile picture
- Profile persisted in localStorage (no account needed)
- Auto-detect device type from user agent (iPhone, Mac, Android...)
- Server uses client-provided profile instead of generating names
- PeerAvatar shows photo or device icon instead of animal emojis
- ProfileSetup modal on first visit + editable from header
2026-04-14 11:47:38 +02:00
ordinarthur
9d6e4da4ae feat: initial commit with full deployment setup
Some checks failed
Build & Deploy / build-and-deploy (push) Failing after 8s
Includes React PWA frontend, WebSocket signaling server, shared types,
K8s manifests, Gitea CI/CD workflow, nginx config, and Dockerfiles.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 10:30:45 +02:00