Commit Graph

4288 Commits

Author SHA1 Message Date
Johnny 78efa6802e feat(store): change default storage type to local filesystem
Add migration scripts for existing instances without a storage setting
to explicitly preserve DATABASE as their storage type, ensuring backward
compatibility. Change the default for new installations to LOCAL to
improve out-of-the-box performance.
2026-03-01 19:42:01 +08:00
Johnny 026ea92f7b fix(editor): show newly linked memos when editing a memo with attachments 2026-03-01 19:16:57 +08:00
Johnny 252b3eff90 test(store): add tests for CreateMemo with custom timestamps 2026-03-01 19:08:27 +08:00
Mudkip 09d73e8b6e
fix(mysql): handle CreateMemo custom timestamps with FROM_UNIXTIME (#5673) 2026-03-01 19:01:54 +08:00
mostapko 104d2ec0a6
feat(ui): allow navigating between images with arrows in preview dialog (#5669) 2026-03-01 11:30:47 +08:00
Steven 664b8c5629 fix(ui): unify metadata component styles across MemoView and MemoEditor
Align header padding, text color, background tokens, and item row styles
across CodeBlock, AttachmentList, RelationList, and LocationDisplay so all
metadata panels follow the same visual pattern.
2026-02-26 23:18:11 +08:00
Steven 2be4a52e1a ci: extend stale workflow to cover PRs and improve clarity
- Add PR stale/close handling alongside issues
- Reduce close grace period from 7 to 3 days
- Add inline comments explaining the two-phase stale behavior
- Rename workflow and job names for clarity
2026-02-26 22:44:47 +08:00
Kevin Zhang 54d6d8c644
chore: update documentation link in README (#5663)
Signed-off-by: Kevin Zhang <ztm0929@icloud.com>
2026-02-26 18:18:06 +08:00
milvasic 6b0736b293
fix(ui): show comment editor above the comment list (#5662) 2026-02-26 08:50:27 +08:00
Steven f7ac6a0191 refactor: auth token refresh flow and simplify user hooks 2026-02-25 22:08:18 +08:00
Steven 333c9df233 fix(web): refresh memo detail cache after editor save 2026-02-25 20:03:09 +08:00
Steven bbdc998646 fix(web): use BroadcastChannel to sync token refreshes across tabs
When multiple tabs are open and a token expires, each tab independently
attempts a refresh. With server-side token rotation this causes all but
the first tab to fail, logging the user out.

Add a BroadcastChannel (memos_token_sync) so that when any tab
successfully refreshes, it broadcasts the new token to all other tabs.
Receiving tabs adopt the token in-memory immediately, skipping their own
refresh request and avoiding conflicts with token rotation.

Falls back gracefully when BroadcastChannel is unavailable (e.g. some
privacy modes).
2026-02-24 23:31:59 +08:00
Steven 26d10212c6 refactor: consolidate duplicated auth logic into auth package
Add ApplyToContext and AuthenticateToUser helpers to the auth package,
then remove the duplicated auth code spread across the MCP middleware,
file server, Connect interceptor, and gRPC-Gateway middleware.

- auth.ApplyToContext: single place to set claims/user into context after Authenticate()
- auth.AuthenticateToUser: resolves any credential (bearer token or refresh cookie) to a *store.User
- MCP middleware: replaced manual PAT DB lookup + expiry check with Authenticator.AuthenticateByPAT
- File server: replaced authenticateByBearerToken/authenticateByRefreshToken with AuthenticateToUser
- Connect interceptor + Gateway middleware: replaced duplicated context-setting block with ApplyToContext
- MCPService now accepts secret to construct its own Authenticator
2026-02-24 23:08:16 +08:00
Steven 47d9414702 feat: add MCP server with PAT authentication
Embeds a Model Context Protocol (MCP) server into the Memos HTTP
process, exposing memo operations as MCP tools at POST/GET /mcp using
Streamable HTTP transport.

Authentication is PAT-only — requests without a valid personal access
token receive HTTP 401. Six tools are exposed: list_memos, get_memo,
create_memo, update_memo, delete_memo, and search_memos, all scoped to the authenticated user.
2026-02-24 22:54:51 +08:00
Steven 71263736b0 chore: fix codeowners 2026-02-23 20:10:18 +08:00
Mudkip ff3e4c5cfe
fix: avoid truncating memo batch attachments (#5654) 2026-02-23 20:09:39 +08:00
Steven 9ecd7b876b fix(web): fix spurious logout on page reload with expired access token
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.
2026-02-23 14:08:59 +08:00
Steven 03c30b8ccb fix(web): fix explore page showing private tags and improve stats hook
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.
2026-02-23 13:15:01 +08:00
Steven 1cea9b0a78 fix(web): make MonthNavigator month label reactive to language changes
Use useTranslation() hook instead of the static i18n import so that
the month label re-computes when the language changes.
2026-02-23 11:04:04 +08:00
Steven 704503e556 fix(store): allow memo/attachment deletion when local file is missing
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.
2026-02-23 10:26:40 +08:00
Steven 17fc8383df fix(web): improve MemoEditor layout and timestamp popover styling 2026-02-23 10:20:00 +08:00
Steven 150371d211 fix(webhook): remediate SSRF vulnerability in webhook dispatcher
- 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
2026-02-23 10:14:24 +08:00
Steven f43965de00 chore: bump version 2026-02-22 18:06:58 +08:00
Steven c3061002f3 fix(web): persist auth token in localStorage for cross-tab sessions
Switch from sessionStorage to localStorage so the auth token survives
across tabs and browser restarts, matching standard platform behavior.
Also guard the signup redirect in App.tsx behind profileLoaded to avoid
a false redirect when the instance profile fetch fails.
2026-02-22 13:55:46 +08:00
Steven 4aaebc507e fix(web): skip GetCurrentUser on init when no token is stored
When no token exists in sessionStorage, AuthContext.initialize() was
still calling GetCurrentUser, triggering the auth interceptor to attempt
RefreshToken and retry — producing a burst of 5+ auth API calls in under
a second that reverse proxies with rate limiting (e.g. CrowdSec) flag as
brute force.

Add hasStoredToken() to auth-state and bail out of initialize() early
when there is definitively no session to restore. The refresh flow for
expired tokens is preserved since hasStoredToken() checks for presence
regardless of expiry.

Fixes #5647
2026-02-22 13:40:34 +08:00
Steven b8bca6bacf fix(web): scope task list item index to memo content container
The closest() selector was targeting a CSS class that never existed on
the container, causing fallback to document.body and collecting task
items across all visible memos. This caused index collisions when
multiple memos with todo lists were on the page.

Adds data-memo-content attribute to the container and updates the
selector accordingly.

Fixes #5635
2026-02-22 13:20:29 +08:00
Steven 2ab476ef2e fix(web): memoize useNavigateTo to prevent infinite view transition loop
Wrapping the returned function in useCallback prevents a new reference
on every render, which was causing an infinite startViewTransition loop
on the initial signup page (fresh install with no admin).

Fixes #5626
2026-02-22 13:16:59 +08:00
Steven 6eaab52e48 fix(web): handle delete memo errors and only run post-delete actions on success 2026-02-22 13:00:36 +08:00
milvasic 1d9d4cd797
fix: invalidate comments query on comment reaction and delete (#5641) 2026-02-22 12:58:56 +08:00
Kaki021 122ac94273
fix(web): ensure default memo visibility is correctly applied (#5623) 2026-02-14 16:58:58 +08:00
Steven 861b58cbf3 refactor(web): extract NsfwOverlay into standalone component with flexbox centering 2026-02-12 23:19:54 +08:00
Steven aeb1e5fe40 perf(web): eliminate redundant fetch when opening inline memo editor 2026-02-12 23:02:25 +08:00
Steven 69485eecb3 chore: tweak readme 2026-02-12 22:35:58 +08:00
Johnny 6402618c26 feat(web): replace EditableTimestamp with inline editor timestamp popover 2026-02-11 23:45:53 +08:00
Johnny 566fdccae6 fix(web): allow calendar year picker to go back to 1970 2026-02-11 22:58:25 +08:00
Johnny 177a74e82b fix: allow ampersand in tags to support compound tags 2026-02-11 22:55:45 +08:00
boojack f7d370dba1
chore: tweak readme
Signed-off-by: boojack <stevenlgtm@gmail.com>
2026-02-10 23:04:35 +08:00
Steven 47e2ace690 fix(web): redirect to auth page instead of explore on session expiry
Fixes #5617
2026-02-10 21:15:44 +08:00
Steven 27063d4850 chore(ci): auto-upload binaries to GitHub Release on publish 2026-02-10 20:51:05 +08:00
Steven 71e8a06463 chore: upgrade Echo v4 to v5.0.3 2026-02-10 09:15:27 +08:00
boojack cdadead6c9
chore: add SSD Nodes sponsorship
Signed-off-by: boojack <stevenlgtm@gmail.com>
2026-02-10 08:27:18 +08:00
Steven 2b19d8a969 fix(tests): update PAT tests to accept both custom and SQL errors
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
2026-02-09 21:09:19 +08:00
Steven d9e8387d63 fix(postgres): handle missing PAT data gracefully and add comprehensive tests
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
2026-02-09 21:02:09 +08:00
MuLingQwQ b5108b4f97
fix(web): calendar navigation should use current page path (#5605) 2026-02-09 20:40:12 +08:00
Johnny b623162d37 chore: fix static check linter warnings 2026-02-08 21:37:02 +08:00
Johnny ba615172b0 chore: bump version 2026-02-08 21:28:11 +08:00
Johnny 984d9b461b fix: gracefully handle deleted memos in activity service to prevent inbox crashes 2026-02-08 21:26:18 +08:00
Johnny a69056a1c2 fix: handle underscores in environment variables correctly 2026-02-08 20:38:43 +08:00
Johnny f827296d6b chore: fix broken links 2026-02-08 19:46:03 +08:00
Johnny d9dc5be200 fix: replace echo.NewHTTPError with status.Errorf 2026-02-08 19:23:34 +08:00