1. 23 Feb, 2026 7 commits
    • Mudkip's avatar
      ff3e4c5c
    • Steven's avatar
      fix(web): fix spurious logout on page reload with expired access token · 9ecd7b87
      Steven authored
      Two bugs caused users to be redirected to /auth too frequently:
      
      1. Race condition in Promise.all([initInstance(), initAuth()]):
         initInstance() makes a gRPC request whose auth interceptor calls
         getAccessToken() synchronously. When the access token was expired,
         getAccessToken() eagerly deleted it from localStorage as a "cleanup"
         side-effect. By the time initAuth() ran and checked hasStoredToken(),
         localStorage was already empty, so it skipped the getCurrentUser()
         call and the token refresh cycle entirely — logging the user out even
         when the refresh-token cookie was still valid. Fix: remove the
         localStorage deletion from getAccessToken(); clearAccessToken()
         (called on confirmed auth failure and logout) handles proper cleanup.
      
      2. React Query retry: 1 caused a second refresh+redirect attempt after
         auth failures. The auth interceptor already handles token refresh and
         request retry internally. If it still throws Unauthenticated, the
         redirect is already in flight — a React Query retry only fires another
         failed refresh and a redundant redirectOnAuthFailure() call. Fix: use
         a shouldRetry function that skips retries for Unauthenticated errors
         while keeping the existing once-retry behaviour for other errors.
      9ecd7b87
    • Steven's avatar
      fix(web): fix explore page showing private tags and improve stats hook · 03c30b8c
      Steven authored
      The explore page sidebar was showing tags from the current user's private
      memos because the default ListMemos query applies a server-side OR filter
      (creator_id == X || visibility in [...]), mixing private content in.
      
      Fix by using a visibility-scoped ListMemos request in the explore context
      so private memos are always excluded via the AND'd server auth filter.
      
      Also consolidate two always-firing useMemos calls into one context-aware
      query, unify activity stats computation with countBy across all branches,
      and extract a toDateString helper to remove duplicated formatting logic.
      03c30b8c
    • Steven's avatar
      fix(web): make MonthNavigator month label reactive to language changes · 1cea9b0a
      Steven authored
      Use useTranslation() hook instead of the static i18n import so that
      the month label re-computes when the language changes.
      1cea9b0a
    • Steven's avatar
      fix(store): allow memo/attachment deletion when local file is missing · 704503e5
      Steven authored
      Fixes two bugs reported in #5603:
      
      1. store/attachment.go: ignore os.ErrNotExist when removing a local
         attachment file so that a missing file on disk (broken state from
         failed uploads) no longer blocks deletion of the DB record, allowing
         memos referencing corrupt attachments to be deleted normally.
      
      2. memo_attachment_service.go: add nil guard on GetAttachment result
         before dereferencing it in SetMemoAttachments, preventing a nil
         pointer panic when an attachment UID no longer exists in the DB.
      704503e5
    • Steven's avatar
    • Steven's avatar
      fix(webhook): remediate SSRF vulnerability in webhook dispatcher · 150371d2
      Steven authored
      - Add plugin/webhook/validate.go as single source of truth for SSRF
        protection: reserved CIDR list parsed once at init(), isReservedIP(),
        and exported ValidateURL() used at registration/update time
      - Replace unguarded http.Client in webhook.go with safeClient whose
        Transport uses a custom DialContext that re-resolves hostnames at
        dial time, defeating DNS rebinding attacks
      - Call webhook.ValidateURL() in CreateUserWebhook and both
        UpdateUserWebhook paths to reject non-http/https schemes and
        reserved/private IP targets before persisting
      - Strip internal service response body from non-2xx error log messages
        to prevent data leakage via application logs
      150371d2
  2. 22 Feb, 2026 7 commits
  3. 14 Feb, 2026 1 commit
  4. 12 Feb, 2026 3 commits
  5. 11 Feb, 2026 3 commits
  6. 10 Feb, 2026 5 commits
  7. 09 Feb, 2026 3 commits
    • Steven's avatar
      fix(tests): update PAT tests to accept both custom and SQL errors · 2b19d8a9
      Steven authored
      The PostgreSQL implementation returns 'PAT not found' when no rows match,
      while SQLite/MySQL return 'sql: no rows in result set' from QueryRowContext.
      
      Both behaviors are correct - the key fix is that PostgreSQL no longer
      throws JSONB errors on missing/malformed data.
      
      Changes:
      - Update test assertions to accept either error type
      - Fix comment punctuation for godot linter
      - Maintain backward compatibility across all database drivers
      2b19d8a9
    • Steven's avatar
      fix(postgres): handle missing PAT data gracefully and add comprehensive tests · d9e8387d
      Steven authored
      Fixes #5612, #5611
      
      Root cause: PostgreSQL's jsonb_array_elements() throws errors when the
      'tokens' key is missing or malformed, while SQLite/MySQL return NULL
      gracefully. This caused:
      - 502 errors when creating admin after v0.25.3 → v0.26.0 upgrade
      - Settings not persisting and users unable to stay logged in
      
      Changes to store/db/postgres/user_setting.go:
      - Remove strict JSONB operations from GetUserByPATHash query
      - Fetch all PERSONAL_ACCESS_TOKENS rows and filter in Go
      - Skip malformed/invalid JSON rows with continue (error recovery)
      - Match SQLite/MySQL's forgiving error handling
      
      New integration tests (store/test/user_setting_test.go):
      - TestUserSettingGetUserByPATHashNoTokensKey
      - TestUserSettingGetUserByPATHashEmptyTokensArray
      - TestUserSettingGetUserByPATHashWithOtherUsers
      
      New PostgreSQL-specific tests (store/db/postgres/user_setting_test.go):
      - TestGetUserByPATHashWithMissingData (comprehensive edge cases)
      - TestGetUserByPATHashPerformance (100+ users)
      - TestUpsertUserSetting (basic upsert)
      
      Test coverage:
       Missing PERSONAL_ACCESS_TOKENS key
       Empty/malformed JSON data
       Multiple users with mixed valid/invalid data
       Performance with 100+ users
       Error recovery without crashes
      
      Benefits:
      - No database migration required (TEXT column works fine)
      - Backward compatible with v0.25.3 upgrades
      - Handles missing/corrupt data gracefully
      - Consistent behavior across all database drivers
      d9e8387d
    • MuLingQwQ's avatar
  8. 08 Feb, 2026 6 commits
  9. 07 Feb, 2026 1 commit
  10. 05 Feb, 2026 2 commits
    • Steven's avatar
      fix: prevent 401 errors on window focus when token expires · 81ef53b3
      Steven authored
      Fixes #5589
      
      When the page returns from background to foreground after the JWT
      token expires (~15 min), React Query's automatic refetch-on-focus
      triggers multiple API calls simultaneously. These all fail with 401
      Unauthorized, leaving the user with empty content.
      
      Solution:
      - Add useTokenRefreshOnFocus hook that listens to visibilitychange
      - Proactively refresh token BEFORE React Query refetches
      - Uses 2-minute buffer to catch expiring tokens early
      - Graceful error handling - logs error but doesn't block
      
      Changes:
      - Created web/src/hooks/useTokenRefreshOnFocus.ts
      - Updated isTokenExpired() to accept optional buffer parameter
      - Exported refreshAccessToken() for use by the hook
      - Integrated hook into AppInitializer (only when user authenticated)
      81ef53b3
    • Steven's avatar
      chore: tweak security report email · 86f780d1
      Steven authored
      86f780d1
  11. 04 Feb, 2026 2 commits
    • Steven's avatar
      refactor: remove hide-scrollbar utility · cf65f086
      Steven authored
      Removed the hide-scrollbar CSS class and all its usages throughout the
      codebase. Hiding scrollbars can hurt UX by making it unclear when
      content is scrollable.
      
      Changes:
      - Removed hide-scrollbar CSS definition from index.css
      - Removed hide-scrollbar class from Navigation component (2 instances)
      - Removed hide-scrollbar class from MemoDetailSidebar (2 instances)
      - Removed hide-scrollbar class from TagsSection
      - Removed hide-scrollbar class from ShortcutsSection
      
      Components now use standard browser scrollbar behavior, which provides
      better visual feedback to users about scrollable content. Modern
      browsers already handle scrollbar appearance elegantly.
      cf65f086
    • Steven's avatar
      perf: disable tooltips in year calendar to fix lag · 74b63b27
      Steven authored
      Fixed issue #5579 where the calendar selection dialog was very laggy.
      
      The root cause was rendering ~365 individual Tooltip components when
      opening the year calendar view (one per day with activity). This created
      a huge number of DOM nodes and event listeners that caused significant
      performance issues.
      
      Changes:
      - Added disableTooltips prop to MonthCalendar and CalendarCell components
      - Disabled tooltips in YearCalendar's small month views
      - Removed unnecessary TooltipProvider wrapper in YearCalendar
      - Tooltips remain enabled in the default month calendar view
      
      Performance improvements:
      - Eliminates ~365 tooltip instances when dialog opens
      - Reduces initial render time significantly
      - Makes dialog interactions smooth and responsive
      
      Users can still click on dates to drill down for details if needed.
      74b63b27