summaryrefslogtreecommitdiff
path: root/apps/web/lib
Commit message (Collapse)AuthorAgeFilesLines
* fix: include feed name in unsubscribe notificationHEADmainFuwn2026-02-121-2/+3
|
* feat: strip tracking parameters from entry URLs and filter tracking pixel imagesFuwn2026-02-122-0/+61
| | | | | | Strip UTM, fbclid, gclid, and other tracking query parameters from entry URLs at parse time in the Go worker. Filter out sub-3px tracking pixel images from sanitized HTML content via exclusiveFilter.
* fix: prioritise unread entries server-side so they appear in all-entries viewFuwn2026-02-122-8/+20
|
* fix: use singular "entry" in mark-as-read toast when count is 1Fuwn2026-02-121-1/+1
|
* feat: add drag-and-drop reordering for feeds, folders, and custom feedsFuwn2026-02-127-3/+241
|
* fix: invalidate unread counts when toggling individual entry read stateFuwn2026-02-121-0/+1
|
* fix: prevent read entries from reverting to unread on re-fetchFuwn2026-02-121-1/+13
| | | | | | | | | | | | | | | | | Root cause: cleanup_stale_entries deleted read-but-unsaved entries from active feeds, then the Go worker re-inserted them with new UUIDs, orphaning the user_entry_states rows and making entries appear unread. - cleanup_stale_entries: skip feeds with active subscribers and preserve entries that have been read (not just saved) - Go parser: normalize GUIDs by trimming whitespace and stripping tracking query parameters from URL-based identifiers - Go writer: preserve original published_at on upsert instead of overwriting, preventing old entries from jumping to timeline top - get_unread_counts: apply same time boundary as get_timeline so ancient re-inserted entries don't inflate counts - Realtime listener: ignore INSERT events for entries older than 48h to suppress misleading "new entries" notifications from re-inserts
* fix: mark-all-read undo only reverts affected entriesFuwn2026-02-101-6/+10
| | | | | RPC now returns uuid[] of affected entry IDs. Undo passes those IDs back via p_entry_ids so only entries that actually changed are reverted.
* fix: query entry state directly instead of relying on unfiltered timelineFuwn2026-02-102-2/+12
| | | | | | The detail panel called useTimeline() with no args, creating a separate cache from the entry list's filtered query. Entries not in the first 50 of the global timeline had isSaved/isRead stuck at false.
* feat: add scrollbar style setting (themed/native/hidden)Fuwn2026-02-101-0/+8
| | | | | Themed scrollbars (default) use 6px thin bars matching the existing colour palette. Synced to body via class toggle in Providers.
* feat: add unread priority option to push unread entries to topFuwn2026-02-101-0/+7
| | | | | | Adds a persisted appearance setting (disabled by default) that partitions the entry list into unread-first, preserving original order within each group.
* fix: harden CI and close remaining test/security gapsFuwn2026-02-103-8/+49
| | | | | | | | | - Make webhook URL tests deterministic with injectable DNS resolver - Wire tier parity checker into CI and root scripts - Add rate_limits cleanup cron job (hourly, >1hr retention) - Change rate limiter to fail closed on RPC error - Add Go worker tests: parser, SSRF protection, error classification, authentication, and worker pool (48 test functions)
* feat: gate offline reading to pro and developer plansFuwn2026-02-101-0/+24
| | | | | | | Service worker now only caches Supabase REST responses when the user's tier allows offline reading. Client syncs tier status to SW via postMessage after profile loads. Free users see a descriptive offline banner instead of stale cached data.
* feat: add automatic timeline refresh with scroll position preservationFuwn2026-02-102-3/+31
| | | | | | | New appearance setting (disabled by default) that silently refreshes the entry list when new entries arrive, provided the user is scrolled to the top. Falls back to notification when scrolled down to avoid disrupting reading position.
* feat: scoped mark-all-read, share enhancements, notification z-indexFuwn2026-02-103-2/+38
| | | | | | | | | - Mark all as read now scopes to current feed/folder instead of all - Added undo button to mark-all-read toast notification - Share notes can be toggled between public and private visibility - Track share view count and display in shares list - Activity-based share expiry: views reset the expiry timer - Fixed notification panel z-index layering behind content area
* refactor: use Supabase Auth display name instead of custom columnFuwn2026-02-101-2/+2
| | | | | | Migrate existing display names from user_profiles to auth.users user_metadata. Drop display_name column from user_profiles. Frontend now reads from auth user metadata and updates via auth.updateUser().
* feat: add vitest tests and GitHub Actions CIFuwn2026-02-103-0/+155
| | | | | | 24 tests covering webhook URL validation (SSRF), API key generation/ hashing, and HTML sanitization. CI workflow runs lint, typecheck (build), and test on push/PR to main.
* fix: reduce lint warnings from 34 to 0Fuwn2026-02-103-5/+4
| | | | | | | | | Disable no-img-element (RSS reader needs <img> for arbitrary external URLs). Remove unused variables/imports and redundant getUser() calls guarded by middleware. Fix exhaustive-deps by adding stable deps, wrapping handlers in useCallback, and suppressing intentional omissions. Fix ref cleanup in use-realtime-entries. Allow triple-slash TS reference directives in no-comments rule.
* fix: P2 security hardening and tier limit parityFuwn2026-02-101-0/+124
| | | | | | Webhook routes switched from admin client to server client (RLS). Added DNS-resolution SSRF protection for webhook URLs with private IP blocking. Added tier limit parity check script.
* fix: P0 correctness/security fixes and P1 lint error resolutionFuwn2026-02-091-17/+15
| | | | | | | | | | | P0: add missing 'developer' case to check_custom_feed_limit trigger, scope user_entry_states join to authenticated user in API v1 entries, replace in-memory rate limiting with Supabase-backed check_rate_limit RPC. P1: fix all 9 ESLint errors — useSyncExternalStore for useIsMobile, restructure WebhookSection to avoid set-state-in-effect, move ref mutations into useEffect, replace <a> with <Link> on shared page, ignore generated public/sw.js in eslint config.
* fix: P0 correctness and security fixesFuwn2026-02-092-19/+19
| | | | | | - Add missing 'developer' case to check_custom_feed_limit trigger (was falling through to else 1) - Scope user_entry_states join to authenticated user in /api/v1/entries (admin client bypasses RLS) - Replace in-memory rate limiting with Supabase-backed solution (UNLOGGED table + check_rate_limit RPC + pg_cron cleanup)
* fix: use online networkMode for offline mutations instead of offlineFirstFuwn2026-02-091-1/+1
| | | | | | | offlineFirst fires the mutation once even when offline, causing a network error that triggers onError and rolls back the optimistic update. online mode pauses the mutation without calling mutationFn, keeping the optimistic state intact until reconnection.
* feat: offline support tier 3 — mutation queue and image cachingFuwn2026-02-092-0/+12
| | | | | | | Paused mutations (read/save toggles) are now persisted to IndexedDB and automatically resumed on reconnection or page reload via TanStack Query's offlineFirst networkMode. Service worker caches images with CacheFirst strategy (500 entries, 7-day expiry) for offline reading.
* feat: offline support tier 2 — prefetch entry content and SW runtime cachingFuwn2026-02-091-0/+45
| | | | | | | Prefetch content_html for the first 10 timeline entries in the background so they are available offline without needing to click each one. Add NetworkFirst runtime caching in service worker for Supabase REST GET requests (24h expiry, 200 entry limit).
* feat: offline support tier 1 — IndexedDB query persistence and offline bannerFuwn2026-02-092-0/+21
| | | | | | Persist React Query cache to IndexedDB via idb-keyval so timeline, entry details, subscriptions, and other read data survive page reloads and brief offline periods. Add network status banner in reader layout.
* feat: add per-feed "hide from timeline" optionFuwn2026-02-093-1/+34
|
* security: harden API routesFuwn2026-02-081-1/+5
| | | | | | | | | | | | | | | | - Add rate limiting to /api/share (30/min), /api/export (5/hr), /api/account/data (3/day) - Add client-side 30s throttle to forgot-password form - Remove immediate tier upgrade on plan change; let invoice.paid webhook handle tier promotion to prevent free upgrades on payment failure - Add SSRF validation to webhook URLs: block localhost, private IPs, link-local, and metadata endpoints - Log Stripe webhook signature verification errors instead of swallowing silently - Mask webhook secret in GET response (show first/last 4 chars only) - Add error logging to API key last_used_at update - Remove internal error message leaking from checkout session route
* feat: add toolbar position setting (top or bottom)Fuwn2026-02-081-0/+8
|
* feat: add feed management features and fix subscribe_to_feed bugsFuwn2026-02-081-0/+65
| | | | | | | | | | | | | - Fix subscribe_to_feed overload ambiguity by dropping old 4-param version - Fix vault permission error by using vault.create_secret instead of direct INSERT - Add duplicate subscription check with clear error message - Add unmute confirmation dialog matching unsubscribe pattern - Add feed button in subscriptions settings page - Add inline rename for feeds, folders, and custom feeds from reader header - Add drag and drop feeds between folders in sidebar - Add credential management UI (add/update) for pro/developer tier - Add add_feed_credentials RPC to convert public feeds to authenticated - Enable pgsodium extension for vault crypto operations
* feat: implement authenticated feed support across worker and web appFuwn2026-02-083-1/+8
| | | | | | | | | | Wire up the full authenticated feeds pipeline: - Worker resolves credentials from Supabase Vault for authenticated feeds - Worker sets owner_id on entries for per-user dedup - query_param auth now parses name=value format - Add-feed dialog shows auth type + credential fields for pro/developer - Subscribe mutation passes credentials to RPC - Sidebar and settings show [auth] indicator for authenticated feeds
* feat: add feed URL editing in subscription settings, fix notification badge ↵Fuwn2026-02-081-0/+29
| | | | styling and HTML entity decoding
* feat: add option to show favicons next to feed names in entry listFuwn2026-02-081-0/+6
|
* feat: add appearance option to toggle folders above/below ungrouped feeds in ↵Fuwn2026-02-081-0/+6
| | | | sidebar
* feat: display folders above ungrouped feeds in sidebar, add ↵Fuwn2026-02-081-0/+30
| | | | delete-all-custom-feeds to danger zone
* fix: space/shift+space scrolls focused panel, revert content font, fix share ↵Fuwn2026-02-072-24/+24
| | | | | | | | | modal text Space/Shift+Space now scrolls whichever panel is focused (entry list, detail panel, or sidebar) instead of only working in the detail panel. Removed content font setting. Fixed share modal placeholder casing and ellipsis spacing.
* feat: resolve 7 pre-ship QoL itemsFuwn2026-02-072-0/+22
| | | | | | | | | | | | | | | - Space/Shift+Space: page down/up in detail panel (80% scroll) - Content font: sans-serif/serif/monospace selector in appearance settings, applied to article content in detail panel - Accessibility: entry-list-item uses button instead of div, folder toggles have aria-expanded, shortcut keys have aria-labels - Share notes: replaced window.prompt with proper modal dialog matching existing UI patterns - Worker .env.example: template with all 10 environment variables - Worker poisoned messages: archive unprocessable queue messages instead of leaving them stuck forever - Worker pool Submit: check return value, reschedule dropped feeds 30s into the future, log warnings for rejected submissions
* fix: resolve 6 pre-ship audit bugsFuwn2026-02-072-1/+23
| | | | | | | | | | | | | | - Webhook entry identifier: use entry GUID instead of feed identifier - Optimistic rollback: add previousTimeline snapshot and onError handler to both useToggleEntryReadState and useToggleEntrySavedState - Rate limiter memory leak: delete Map entries when window expires, use else-if to avoid re-setting after delete - Entries API limit param: use Number.isFinite guard instead of falsy coercion that treats 0 as default - PWA manifest: add PNG raster icon routes (192x192, 512x512) for devices that don't support SVG icons - Billing webhook: throw on DB errors and return 500 so Stripe retries failed events instead of silently losing them
* fix: reset panel sizes without page reload, prevent partial-data max width ↵Fuwn2026-02-071-0/+12
| | | | | | | | | | | | clamping Use imperative groupRef API to resize panels instantly instead of writing to localStorage and calling window.location.reload(). Register reset callbacks in Zustand store from layout components. Change sidebarMaxWidth early return from && to || so the generous 35% fallback is used until both subscriptions and custom feeds have loaded, preventing intermittent clamping to minimum size.
* fix: api key prefix rename, revoke fix, and webhook validationFuwn2026-02-072-2/+2
| | | | | | Rename API key prefix from asn_ to asa_, fix key revoke by aligning response property names with frontend interface, and add server/client validation to prevent enabling webhooks without a URL.
* feat: pre-ship polish — UI improvements, keyboard shortcuts, appearance ↵Fuwn2026-02-079-25/+245
| | | | | | | | | | | | | | | | | | settings - Rename "muted keywords" to "muted phrases" throughout settings UI - Add header with navigation to auth pages (sign-in, sign-up, etc.) - Merge security tab (TOTP setup) into account settings tab - Fix TOTP name input truncation on Safari (w-64 → flex-1 min-w-0) - Add appearance settings: font size, time display format, entry images toggle, reading time toggle - Add keyboard shortcuts dialog (? key) with all keybindings documented - Add extended vim shortcuts: gg, G, n/N (next/prev unread), Ctrl+h/l (panel focus) - Add command palette shortcut (⌘K) to shortcuts dialog - Add icon URL fields for folders and custom feeds (DB + queries + settings UI) - Add data-has-unreads attribute for sidebar keyboard navigation - Fix SSR prerendering crash from Zustand persist and react-resizable-panels localStorage access - Add detail panel layout persistence via useDefaultLayout - Update marketing copy to advertise vim-like keyboard navigation
* style: lowercase all user-facing strings and add custom eslint ruleFuwn2026-02-0710-17/+17
| | | | | | | | Comprehensive sweep of all user-facing text to enforce lowercase convention, including acronyms (api, rest, http, opml, json, totp, mfa, qr, hmac). Added asa-lowercase/lowercase-strings eslint rule that reports uppercase in notify() calls, error messages, jsx text, and checked attributes (placeholder, alt, title).
* feat: asa.news RSS reader with developer tier, REST API, and webhooksFuwn2026-02-0745-0/+3057
Full-stack RSS reader SaaS: Supabase + Next.js + Go worker. Includes three subscription tiers (free/pro/developer), API key auth, read-only REST API, webhook push notifications, Stripe billing with proration, and PWA support.