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.
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.
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.
- 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
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.
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
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
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
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
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
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)
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.
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.
Fixed issue #5576 where clicking the edit button on a shortcut would
incorrectly open a create dialog instead of an edit dialog.
The root cause was an incorrect useEffect that was watching the shortcut
state itself instead of the initialShortcut prop. When the dialog was
opened for editing, the state wasn't properly reinitializing with the
existing shortcut data.
Changed the useEffect to:
- Watch initialShortcut as the dependency
- Reinitialize the shortcut state when initialShortcut changes
- Properly distinguishes between create (initialShortcut undefined) and
edit (initialShortcut has data) modes
- Add validation check for loading state before allowing save
- Prevents false "Content, attachment, or file required" error
- Occurs when user presses CTRL+Enter immediately after opening edit mode
- Editor state may still be loading when keyboard shortcut fires
Closes#5581
- Fix nested task lists not showing proper indentation
- Use simple CSS cascade with [&_ul.contains-task-list]:ml-6
- Fix checkbox clicks toggling wrong tasks in nested lists
- Search from memo root container for global task indexing
- Remove complex selectors in favor of standard approach
- Match behavior of GitHub, Notion, and other platforms
Closes#5575
Fixes issue where OAuth sign-in fails with 'Cannot read properties of
undefined (reading 'digest')' when accessing Memos over HTTP.
The crypto.subtle API is only available in secure contexts (HTTPS or
localhost), but PKCE (RFC 7636) is optional per OAuth 2.0 standards.
Changes:
- Make PKCE generation optional with graceful fallback
- Use PKCE when crypto.subtle available (HTTPS/localhost)
- Fall back to standard OAuth flow when unavailable (HTTP)
- Log warning to console when PKCE unavailable
- Only include code_challenge in auth URL when PKCE enabled
The backend already supports optional PKCE (empty codeVerifier), so no
backend changes needed. This fix aligns frontend behavior with backend.
Benefits:
- OAuth sign-in works on HTTP deployments (reverse proxy scenarios)
- Enhanced security (PKCE) still used when HTTPS available
- Backward compatible with OAuth providers that don't support PKCE
Fixes#5570
- Add workflow_dispatch trigger for manual binary builds
- Build for linux (amd64, arm64, arm/v7), darwin (amd64, arm64), windows (amd64)
- Package as tar.gz (Unix) and zip (Windows)
- Generate build summary with artifact sizes
- Remove unused showNsfwContent prop (was only used in MemoDetail to pre-reveal NSFW, which defeats the purpose)
- Inline useNsfwContent hook logic directly into MemoView.tsx (only 3 lines, no reusability benefit)
- Delete web/src/components/MemoView/hooks/useNsfwContent.ts
- NSFW content now consistently starts hidden across all pages
- Maintains same user experience: click to reveal, no toggle back
This removes unnecessary indirection and prop threading while preserving functionality.
Root cause: enabled={isInitialized && !!user} prevented displaying cached
data when user auth state transitioned during token refresh.
Changes:
- Remove !!user check from Home page enabled condition
- Add clearAccessToken() in redirectOnAuthFailure for clean logout
Fixes#5565
Add custom memos_unicode_lower() SQLite function to enable proper
case-insensitive text search for non-English languages (Cyrillic,
Greek, CJK, etc.).
Previously, SQLite's LOWER() only worked for ASCII characters due to
modernc.org/sqlite lacking ICU extension. This caused searches for
non-English text to be case-sensitive (e.g., searching 'блины' wouldn't
find 'Блины').
Changes:
- Add store/db/sqlite/functions.go with Unicode case folding function
- Register custom function using golang.org/x/text/cases.Fold()
- Update filter renderer to use custom function for SQLite dialect
- Add test for Unicode case-insensitive search
- Make golang.org/x/text a direct dependency
Fixes#5559
Fixes#5551
The Docker image now runs as non-root (UID 10001) for security, but this
breaks upgrades from 0.25.3 where data files were owned by root.
Changes:
- Dockerfile: Keep USER as root, install su-exec
- entrypoint.sh: Fix ownership of /var/opt/memos, then drop to non-root
- Supports custom MEMOS_UID/MEMOS_GID env vars for flexibility
This allows seamless upgrades without manual chown on the host.
Defense-in-depth fix: Add missing nil check before accessing
currentUser.ID and currentUser.Role in DeleteUser function.
While the auth interceptor should block unauthenticated requests,
this check prevents potential nil pointer panic if fetchCurrentUser
returns (nil, nil).
- Add serveMediaStream() to stream video/audio without loading into memory
- Use http.ServeFile for local files (zero-copy, handles range requests)
- Redirect to S3 presigned URLs for S3-stored media files
- Refactor for better maintainability:
- Extract constants and pre-compile lookup maps
- Consolidate duplicated S3 client creation logic
- Split authentication into focused helper methods
- Group code by responsibility with section comments
- Add setSecurityHeaders() and setMediaHeaders() helpers
- Remove menu item and dialog from MemoActionMenu
- Remove removeCompletedTasks() and hasCompletedTasks() utilities
- Remove translation keys from all 34 locale files
- Feature was not aligned with standard note-taking UX patterns
- Add same value check before updating createTime/updateTime
- Skip request if new timestamp equals current timestamp
- Simplify callback handlers and improve code readability
- Use .some() instead of .filter().length for cleaner code
- Changed InstanceProfile to include admin user field
- Updated GetInstanceProfile method to retrieve admin user
- Modified related tests to reflect changes in admin user retrieval
- Removed owner cache logic and tests, introducing new admin cache tests
- Removed the owner field from InstanceProfile as it is no longer needed.
- Added an initialized field to InstanceProfile to indicate if the instance has completed first-time setup.
- Updated GetInstanceProfile method to set initialized based on the existence of an admin user.
- Modified tests to reflect changes in InstanceProfile and ensure correct behavior regarding instance initialization.
- Adjusted frontend logic to redirect users based on the initialized status instead of the owner field.
- Updated the isSuperUser function to only check for ADMIN role.
- Added SQL migration scripts for MySQL, PostgreSQL, and SQLite to change user roles from HOST to ADMIN.
- Created a new SQLite migration to alter the user table structure and ensure data integrity during the migration process.
Allow API users to set custom create_time, update_time, and display_time
when creating memos and comments. This enables importing historical data
with accurate timestamps.
Changes:
- Update proto definitions: change create_time and update_time from
OUTPUT_ONLY to OPTIONAL to allow setting on creation
- Modify CreateMemo service to handle custom timestamps from request
- Update database drivers (SQLite, MySQL, PostgreSQL) to support
inserting custom timestamps when provided
- Add comprehensive test coverage for custom timestamp functionality
- Maintain backward compatibility: auto-generated timestamps still
work when custom values are not provided
- Fix golangci-lint issues in plugin/filter (godot and revive)
Fixes#5483
Add support for CEL exists() comprehension with startsWith, endsWith, and
contains predicates to enable powerful tag filtering patterns.
Features:
- tags.exists(t, t.startsWith("prefix")) - Match tags by prefix
- tags.exists(t, t.endsWith("suffix")) - Match tags by suffix
- tags.exists(t, t.contains("substring")) - Match tags by substring
- Negation: !tags.exists(...) to exclude matching tags
- Works with all operators (AND, OR, NOT) and other filters
Implementation:
- Added ListComprehensionCondition IR type for comprehension expressions
- Parser detects exists() macro and extracts predicates
- Renderer generates optimized SQL for SQLite, MySQL, PostgreSQL
- Proper NULL/empty array handling across all database dialects
- Helper functions reduce code duplication
Design decisions:
- Only exists() supported (all() rejected at parse time with clear error)
- Only simple predicates (matches() excluded to avoid regex complexity)
- Fail-fast validation with helpful error messages
Tests:
- Comprehensive test suite covering all predicates and edge cases
- Tests for NULL/empty arrays, combined filters, negation
- Real-world use case test for Issue #5480 (archive workflow)
- All tests pass on SQLite, MySQL, PostgreSQL
Closes#5480
- Suppress revive redundant-test-main-exit warning with //nolint
- Fix data race in cache.Clear() by using Delete() instead of map replacement
- Both changes maintain correct behavior while fixing CI failures
Fix all data races detected by -race flag in parallel store tests.
Problems fixed:
1. TestMain not propagating exit code
- Was: m.Run(); return
- Now: os.Exit(m.Run())
2. Data races on global DSN variables
- mysqlBaseDSN and postgresBaseDSN written in sync.Once
- Read outside Once without synchronization
- Race detector: write/read without happens-before relationship
3. Data races on container pointers
- mysqlContainer, postgresContainer, testDockerNetwork
- Same pattern: write in Once, read in cleanup
Solution: Use atomic operations
- atomic.Value for DSN strings (Store/Load)
- atomic.Pointer for container pointers (Store/Load)
- Provides proper memory synchronization
- Race-free reads and writes
Why sync.Once alone wasn't enough:
- sync.Once guarantees function runs once
- Does NOT provide memory barrier for variables written inside
- Reads outside Once have no synchronization with writes
- Race detector correctly flags this as violation
Technical details:
- atomic.Value.Store() provides release semantics
- atomic.Value.Load() provides acquire semantics
- Guarantees happens-before relationship per Go memory model
- All 159 parallel tests can safely access globals
Impact:
- Tests now pass with -race flag
- No performance degradation (atomic ops are fast)
- Maintains parallel execution benefits (8-10x speedup)
- Proper Go memory model compliance
Related: Issues #2, #3 from race analysis
Problem:
- store/test/TestMain checks if DRIVER env var is set
- If not set, it runs tests for all 3 drivers (sqlite, mysql, postgres)
by spawning child 'go test' processes
- This conflicts with t.Parallel() in individual tests
- CI workflow didn't set DRIVER, triggering multi-driver execution
Solution:
- Set DRIVER=sqlite in GitHub Actions workflow
- TestMain will run tests once with SQLite driver
- Tests run in parallel with t.Parallel() as intended
- Avoids spawning child processes and race conditions
Why SQLite:
- Fastest test execution (no container startup)
- Sufficient for CI validation
- MySQL/Postgres can be tested locally when needed
This fixes the 'table already exists' errors and test flakiness
in CI while maintaining parallel execution benefits.
Add t.Parallel() to all 159 test functions in store/test/
Benefits:
- Tests run in parallel (8-16 concurrent on typical CI)
- Each test already has isolated database (safe to parallelize)
- No shared state between tests
- Go test runner handles synchronization
Expected performance:
- SQLite tests: 4-6min → 30-45sec (87% faster)
- MySQL tests: 6-8min → 45-60sec (88% faster)
- Better CPU utilization (10-15% → 80-95%)
Why it's safe:
- NewTestingStore() creates isolated DB per test
- No global state mutations
- Each test uses t.TempDir() for file isolation
- Container-based tests use unique database names
Impact on CI workflow:
- Backend tests workflow: 4-6min → 1-2min total
- Store test group: 3-4min → 20-30sec
- 8-10x speedup from parallelization alone
- Use `textarea-caret` for precise cursor position calculation instead of line approximation
- Update `scrollToCursor` to scroll to the actual cursor position
- Fix Safari double-enter issue with IME in list completion
This fixes a critical data loss issue where users editing the same memo
on multiple devices would overwrite each other's changes due to aggressive
browser caching, particularly in Chromium-based browsers and PWAs.
Changes:
- Backend: Add Cache-Control headers to all API responses to prevent
browser HTTP caching
- Frontend: Force fresh fetch from server when opening memo editor by
invalidating React Query cache
- Frontend: Reduce memo query staleTime from 60s to 10s for better
collaborative editing support
Fixes#5470
Fixes#5472
Move cleanup logic to store.DeleteMemo to ensure data consistency:
- Delete memo_relation records where memo is source (MemoID) or target (RelatedMemoID)
- Delete attachments linked to the memo (including S3/local files)
This prevents stale COMMENT records in memo_relation after deleting
a memo that has comments.
- Filter out COMMENT type relations, only show REFERENCE type
- When editing a memo, only show relations where current memo is the source
- Pass memoName through EditorMetadata to RelationList for filtering
Add ListMemoComments to public endpoints whitelist so unauthenticated
users can see public comments. The service layer already filters
comments by visibility (only PUBLIC for guests).
Fixes#5471
- Fix linting issues and address testcontainers deprecation in store/test/containers.go
- Extract MemosStartupWaitStrategy for consistent container health checks
- Refactor migrator_test.go to use a consolidated testMigration helper, reducing duplication
- Add store/test/Dockerfile for optimized local test image builds
Reverts only the system_setting → instance_setting rename related changes from commit d326c71.
Keeps the resource → attachment rename changes intact.
- Reverts table name back to system_setting in all database drivers (MySQL, PostgreSQL, SQLite)
- Removes migration files for the system_setting rename
- Reverts LATEST.sql files to use system_setting table
This fixes an issue where the theme selector on the login screen would not update its display value after selection because the component was not re-rendering. Added local state to track the current theme. Validated that this pattern is unique to the unauthenticated context.
- Remove shared memo-metadata folder
- Move metadata display components (AttachmentList, LocationDisplay, RelationList) to MemoView/components/metadata
- Move attachment types and utilities (LocalFile, AttachmentItem, toAttachmentItems) to MemoEditor/types/attachment
- Simplify AttachmentList and AttachmentCard to work directly with Attachment proto
- Update all imports across MemoEditor and MemoView components
- Better separation of concerns: MemoView handles display, MemoEditor handles local files + attachments
- Deleted MemoContentContext and its associated types.
- Updated Tag and TaskListItem components to use MemoViewContext instead.
- Refactored MemoContent component to eliminate context provider and directly use derived values.
- Simplified MemoViewContext to only include essential data.
- Enhanced error handling in various components by introducing a centralized error handling utility.
- Improved type safety across components and hooks by refining TypeScript definitions.
- Updated remark plugins to enhance tag parsing and preserve node types.
Replace dynamic hook mapping with useMemoComments hook to fetch all comments
via listMemoComments API, ensuring consistent hook order across renders and
fixing page load failure after comment creation.
The translation files ca.json and gl.json existed but weren't registered in the locales array, preventing users from selecting these languages.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Optimizes React Query migration with performance and consistency improvements:
Performance:
- Memoize AuthContext and InstanceContext provider values to prevent unnecessary re-renders
- Convert InstanceContext getter functions to useMemo hooks
- Fix refetchSettings to avoid state dependency that caused frequent recreations
Type Safety:
- Replace 'any' types in useAttachmentQueries with proper protobuf types
- Add Attachment and ListAttachmentsRequest type imports
Query Key Consistency:
- Replace hardcoded ["users", "stats"] with userKeys.stats() factory function
- Ensures consistent cache key management across mutations
Developer Experience:
- Rename unused useCurrentUser to useCurrentUserQuery to avoid confusion
- Add documentation explaining AuthContext-based vs React Query current user hooks
- Update internal references in useNotifications and useTagCounts
All changes verified with TypeScript compilation and build tests.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Wrap actions.toggleFocusMode() with dispatch() call
- Create toggleFocusMode helper function for callbacks
- Fix focus mode not toggling due to action not being dispatched
- Add useFocusMode hook to lock body scroll in focus mode
- Add 'f' key keyboard shortcut to toggle focus mode
- Add FocusModeExitButton inside editor
- Import and wire up all focus mode components and hooks
- Update useKeyboard to handle focus mode toggle
- Import and use FOCUS_MODE_STYLES from constants
- Apply proper fixed positioning, shadow, and spacing in focus mode
- Add transition animation for smooth mode switching
- Replace empty 'focus-mode' class with actual Tailwind utilities
- Add back Tailwind utility classes for proper layout
- Restore padding, border, background, and flex styles
- Fix missing styles on memo-editor-wrapper element
- Add back descriptive comments removed by editor refactoring commit
- Restore original timestamp calculation format in MemoHeader
- Improve code readability with section comments
Complete refactoring of MemoView components for better code quality:
- Split useMemoViewState into individual hook files (useMemoActions,
useKeyboardShortcuts, useNsfwContent, useImagePreview, useMemoCreator)
for single responsibility principle
- Consolidate types closer to usage - move hook-specific types to
respective files, keep only component props in shared types.ts
- Optimize context with separate static/dynamic memoization to reduce
unnecessary re-renders
- Simplify barrel exports to only expose public API (MemoView component
and MemoViewProps type)
- Add comprehensive JSDoc documentation to all public APIs with usage
examples
Benefits:
- Better maintainability: each hook file has one clear purpose
- Improved performance: context optimization prevents unnecessary re-renders
- Enhanced developer experience: clear documentation and encapsulation
- Cleaner architecture: public API is minimal, internal details hidden
All automated checks pass (TypeScript compilation, linter, production build).
- Adjust CodeBlock styling for better layout and spacing
- Reorganize copy button positioning with absolute positioning
- Simplify button styling and reduce icon sizes
- Wrap MermaidBlock in pre tag for consistent structure
- Format useDropdownMenuSubHoverDelay hook call for readability
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Add request deduplication to getOrFetchUser using RequestDeduplicator
- Consolidates multiple simultaneous calls for same user into single API request
- Prevents duplicate 401 errors and wasted network traffic
- Matches pattern already used by fetchUsers and fetchUserStats
- Remove backwards compatibility aliases (getOrFetchUserByName, getOrFetchUserByUsername)
- Update all call sites to use canonical getOrFetchUser method
Fixes issue where PagedMemoList, useMemoViewState, MainLayout, and UserProfile
were making duplicate user fetch requests when loading user data.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Added `scheduler` package with core functionalities for job scheduling.
- Implemented job registration, execution, and graceful shutdown mechanisms.
- Introduced middleware support for logging, recovery from panics, and timeout handling.
- Developed cron expression parser to support both 5-field and 6-field formats.
- Created comprehensive integration and unit tests for scheduler, job definitions, middleware, and parser functionalities.
- Enhanced logging capabilities to track job execution and errors.
- Implemented the Email Plugin for self-hosted Memos instances, providing SMTP email sending capabilities.
- Created configuration structure for SMTP settings with validation.
- Developed message structure for email content with validation and formatting.
- Added synchronous and asynchronous email sending methods.
- Implemented error handling and logging for email sending processes.
- Included tests for client, configuration, and message functionalities to ensure reliability.
- Updated documentation to reflect new features and usage instructions.
- Add custom zoom controls with modern glassmorphism styling
- Add "Open in Google Maps" button for location markers
- Refactor to React component architecture with proper Leaflet integration
- Create reusable GlassButton component for maintainability
- Use React 18 createRoot for portal rendering
- Replace imperative DOM manipulation with declarative React patterns
- Add coordinate display to location metadata
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Remove ListSessions and RevokeSession RPC endpoints
- Remove Session message and SessionsSetting from UserSetting
- Remove ACCESS_TOKENS key and AccessTokensSetting
- Update references to use RefreshTokensUserSetting with its own ClientInfo
- Remove UserSessionsSection frontend component
- Clean up user store to remove session and access token settings
- Regenerate protobuf files
The system now uses:
- REFRESH_TOKENS for session management with sliding expiration
- PERSONAL_ACCESS_TOKENS for long-lived API tokens
- Remove SessionCookieName and SessionSlidingDuration constants
- Remove ExtractSessionCookieFromHeader() function
- Remove SessionIDContextKey and GetSessionID() function
- Remove sessionID parameter from SetUserInContext()
- Remove SessionID field from AuthResult struct
- Remove session cookie extraction from middleware
- Update documentation to reflect JWT + PAT only auth
Session cookies were never being set since migration to refresh token
authentication. This change removes ~50 lines of dead code and clarifies
that the system uses JWT access tokens, refresh tokens, and PATs only.
- Extract route paths to router/routes.ts as single source of truth
- Refactor connect.ts auth interceptor with better structure and error handling
- Add TokenRefreshManager class to prevent race conditions
- Implement smart redirect logic for public/private routes
- Support unauthenticated access to explore and user profile pages
- Add proper error handling for missing access tokens
- Extract magic strings to named constants
- Maintain backward compatibility by aliasing Routes to ROUTES
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Fix cookie expiration timezone to use GMT (RFC 6265 compliance)
- Use Connect RPC client for token refresh instead of fetch
- Fix error code checking (numeric Code.Unauthenticated instead of string)
- Prevent infinite redirect loop when already on /auth page
- Fix protobuf Timestamp conversion using timestampDate helper
- Store access token in sessionStorage to avoid unnecessary refreshes on page reload
- Add refresh token cookie fallback for attachment authentication
- Improve error handling with proper type checking
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Rename grpcweb.ts to connect.ts to reflect ConnectRPC usage
- Enable binary protobuf format for improved performance
- Update all imports across 26 files from @/grpcweb to @/connect
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add CreateUser to PublicMethods ACL whitelist to fix "authentication required"
error during first-time setup. The CreateUser method already has proper security
logic that automatically assigns HOST role to the first user and enforces
DisallowUserRegistration setting for subsequent users.
Fixes#5352🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The CodeBlock component was refactored in v0.25.3 to use navigator.clipboard.writeText(),
which requires HTTPS or localhost. This caused the copy button to fail silently for users
accessing Memos over HTTP.
This fix adds a fallback to the copy-to-clipboard library (already used by all other
copy operations in the codebase) when the native clipboard API is unavailable or fails,
ensuring the copy button works reliably in all deployment scenarios.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Extract shared utilities and constants to eliminate code duplication
- Create dedicated Calendar page with year view and month grid
- Add date filter navigation with bidirectional URL sync
- Fix useTodayDate memoization bug causing stale date references
- Standardize naming conventions (get vs generate functions)
- Add comprehensive type exports and proper store encapsulation
- Implement size variants for compact calendar display
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Added UpdateSessionLastAccessed method to update session access time.
- Enhanced Authenticate method to support both session cookie and JWT token authentication.
- Introduced AuthResult struct to encapsulate authentication results.
- Added SetUserInContext function to simplify context management for authenticated users.
refactor(auth): streamline gRPC and HTTP authentication
- Removed gRPC authentication interceptor and replaced it with a unified approach using GatewayAuthMiddleware for HTTP requests.
- Updated Connect interceptors to utilize the new authentication logic.
- Consolidated public and admin-only method checks into service layer for better maintainability.
chore(api): clean up unused code and improve documentation
- Removed deprecated logger interceptor and unused gRPC server code.
- Updated ACL configuration documentation for clarity on public and admin-only methods.
- Enhanced metadata handling in Connect RPC to ensure consistent header access.
fix(server): simplify server startup and shutdown process
- Eliminated cmux dependency for handling HTTP and gRPC traffic.
- Streamlined server initialization and shutdown logic for better performance and readability.
Problem:
The withHeaderCarrier generic function had a type mismatch that caused compilation
errors in CI. The function used `T proto.Message` constraint, but Connect's Response
type expects the non-pointer message type while protobuf methods return pointers.
Error from CI:
type T of resp does not match *T (cannot infer T)
This occurred because:
- Connect methods expect: *connect.Response[v1pb.CreateSessionResponse]
- Service methods return: (*v1pb.CreateSessionResponse, error)
- Old signature: fn func(context.Context) (T, error) with T proto.Message
- This caused T to be inferred as *v1pb.CreateSessionResponse
- Leading to return type: *connect.Response[*v1pb.CreateSessionResponse] (wrong!)
Solution:
Changed generic signature to explicitly handle the pointer/non-pointer distinction:
- New signature: fn func(context.Context) (*T, error) with T any
- T is now the non-pointer type (e.g., v1pb.CreateSessionResponse)
- fn returns *T (e.g., *v1pb.CreateSessionResponse)
- Return type is correctly: *connect.Response[T] (e.g., *connect.Response[v1pb.CreateSessionResponse])
Also removed unused "google.golang.org/protobuf/proto" import and improved documentation
to clarify the T vs *T distinction.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Problem:
The Explore page filter was sending visibility filter as:
visibility in ["3", "2"]
when it should send:
visibility in ["PUBLIC", "PROTECTED"]
The backend CEL filter parser expects string enum names, not numeric values.
This caused the Explore page to return no memos even when public memos existed.
Solution:
- Added getVisibilityName() helper to convert Visibility enum values to string names
- Updated useMemoFilters to use getVisibilityName() when building visibility filter
- Follows same pattern as existing getInstanceSettingKeyName() and getUserSettingKeyName()
- Added validation to warn on invalid enum values
Files changed:
- web/src/store/common.ts: Add getVisibilityName() helper with validation
- web/src/hooks/useMemoFilters.ts: Use getVisibilityName() in visibility filter
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Problem:
The codebase supports both native gRPC and Connect-RPC protocols, but auth
service was using grpc.SetHeader() which only works for native gRPC. This
caused "failed to set grpc header" errors when using Connect-RPC clients
(browsers using nice-grpc-web).
Solution:
- Created HeaderCarrier pattern for protocol-agnostic header setting
- HeaderCarrier stores headers in context for Connect-RPC requests
- Falls back to grpc.SetHeader for native gRPC requests
- Updated auth service to use SetResponseHeader() instead of grpc.SetHeader()
- Refactored Connect wrappers to use withHeaderCarrier() helper to eliminate
code duplication
Additional fixes:
- Allow public methods when gRPC metadata is missing in ACL interceptor
- Properly handle ParseSessionCookieValue errors instead of ignoring them
- Fix buildSessionCookie to gracefully handle missing metadata
Files changed:
- server/router/api/v1/header_carrier.go: New protocol-agnostic header carrier
- server/router/api/v1/auth_service.go: Use SetResponseHeader, handle missing metadata
- server/router/api/v1/connect_services.go: Use withHeaderCarrier helper
- server/router/api/v1/acl.go: Allow public methods without metadata
- server/router/api/v1/connect_interceptors.go: Handle ParseSessionCookieValue errors
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Frontend was incorrectly using numeric enum values (e.g., 1, 2, 3) instead
of string names (e.g., "GENERAL", "STORAGE") when constructing API resource
paths. This caused the backend to fail with "unsupported instance setting
key: INSTANCE_SETTING_KEY_UNSPECIFIED" errors during initialization.
Changes:
- Add helper functions in store/common.ts to convert enum values to names
- getInstanceSettingKeyName() and buildInstanceSettingName()
- getUserSettingKeyName() and buildUserSettingName()
- Update instance store to use string enum names in API calls
- Update user store to use string enum names in API calls
- Update all components to use new helper functions for setting names
Fixes enum string conversion for:
- InstanceSetting_Key (6 locations)
- UserSetting_Key (2 locations)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixes#5319. Checkboxes inside code blocks were incorrectly counted when
toggling tasks, causing the wrong checkbox to be checked. Replaced regex-based
task detection with mdast AST parsing which properly ignores code block content.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Extract preference logic into dedicated hooks (useUserLocale, useUserTheme)
- Add applyLocaleEarly() for consistent early application
- Remove applyUserPreferences() from user store (now redundant)
- Simplify App.tsx by moving effects to custom hooks
- Make locale/theme handling consistent and reactive
- Clean up manual preference calls from sign-in flows
Fixes locale not overriding localStorage on user login.
Improves maintainability with better separation of concerns.
Add custom remark plugin to prevent setext headers (headers using === or --- underlines) from being recognized by the markdown parser. The plugin disables the setextUnderline construct at the micromark parser level.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Migrates attachment and avatar binary serving from gRPC endpoints to a new dedicated HTTP fileserver package, fixing Safari video playback issues and improving architectural separation.
Key changes:
- Created server/router/fileserver package for all binary file serving
- Removed GetAttachmentBinary and GetUserAvatar gRPC endpoints from proto
- Implemented native HTTP handlers with full range request support
- Added authentication support (session cookies + JWT) to fileserver
- New avatar endpoint supports lookup by user ID or username
- Eliminated duplicate auth constants (imports from api/v1)
HTTP endpoints:
- Attachments: /file/attachments/:uid/:filename (unchanged URL)
- Avatars: /file/users/:identifier/avatar (new URL format)
This fixes Safari video/audio playback by using http.ServeContent() which properly handles HTTP 206 Partial Content responses and range request headers.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Integrates remark-math and rehype-katex plugins to enable LaTeX mathematical expressions in memos. Users can now write inline math ($...$) and display math ($$...$$) using standard LaTeX syntax.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove theme and locale from instance settings to eliminate duplication and
simplify the codebase. These are user-specific preferences and should only
exist in user settings, not instance-wide settings.
Backend changes:
- Remove theme from InstanceGeneralSetting proto
- Remove locale from InstanceCustomProfile proto
- Update instance service converters to remove theme/locale handling
- Simplify RSS feed to use static locale
Frontend changes:
- Remove theme/locale from instanceStore state
- Create unified initialization flow with clear fallback priority:
* Theme: user setting → localStorage → system preference
* Locale: user setting → browser language
- Add applyUserPreferences() to centralize theme/locale application
- Simplify App.tsx by removing redundant state synchronization
- Update all components to use new helper functions:
* getThemeWithFallback() for theme resolution
* getLocaleWithFallback() for locale resolution
- Remove theme/locale selectors from instance profile dialog
Theme utilities refactor:
- Organize code into clear sections with JSDoc comments
- Extract localStorage operations into getStoredTheme/setStoredTheme helpers
- Split DOM manipulation into focused functions
- Improve type safety with Theme and ResolvedTheme types
- Reduce code duplication and improve maintainability
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Previously, clicking multiple tags would add them all as active filters. Now clicking a new tag automatically clears any existing tag filters before applying the new one, ensuring only one tag can be filtered at a time. Clicking an already-active tag still deselects it.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Complete removal of migration_history system in favor of instance_setting based schema versioning.
Changes:
- Remove migration_history table creation from all LATEST.sql files
- Delete all migration_history model and implementation files (~300 lines)
- Remove FindMigrationHistoryList and UpsertMigrationHistory from Driver interface
- Replace complex backward compatibility functions with simple version check
- Update health check to use instance_setting instead of migration_history
- Simplify checkMinimumUpgradeVersion to detect pre-v0.22 installations
Breaking change:
Users on versions < v0.22.0 (May 2024) must upgrade to v0.25.x first before upgrading to this version.
Clear error message with upgrade instructions will be shown for old installations.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Wrap all setter functions in useMemoEditorState with useCallback to ensure stable references
This prevents infinite loops when setters are used in useEffect dependencies (fixes "Maximum update depth exceeded" error)
- Extract MobX observable values in useMemoFilters and useMemoSorting before using them in useMemo dependencies
This prevents React from tracking MobX observables directly, improving reliability
- Add comprehensive documentation explaining the design decisions for future maintainability
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add MessageType filter to FindInbox to exclude legacy VERSION_UPDATE
notifications from inbox queries. This resolves the issue where users
saw notification counts but no items displayed, as VERSION_UPDATE
entries cannot be rendered in the new UserNotification API.
Changes:
- Add MessageType field to FindInbox struct for database-level filtering
- Implement JSON extraction filters in SQLite, MySQL, and PostgreSQL drivers
- Update ListUserNotifications to filter MEMO_COMMENT type at store level
This approach improves performance by filtering at the database rather
than in application code, reducing unnecessary data transfer for users
with many legacy inbox entries.
Fixes#5278🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Major performance and standards compliance improvements to RSS feed generation:
Performance optimizations:
- Fix N+1 query problem by batch loading attachments (101 queries → 2-3)
- Add in-memory caching with 1-hour TTL and LRU eviction
- Implement ETag-based conditional requests (304 Not Modified)
- Add database-level pagination with LIMIT clause
- Clean up expired cache entries to prevent memory leaks
RSS 2.0 compliance:
- Add item titles extracted from memo content
- Include both description and content:encoded fields
- Add author information (name and email)
- Set proper Last-Modified headers
- Use specific application/rss+xml content type
Code quality:
- Fix potential index out of bounds panic in title generation
- Improve markdown heading stripping with regex (handles # to ######)
- Add proper HTTP caching headers (Cache-Control, ETag, Last-Modified)
- Thread-safe cache implementation with RWMutex
- Better error handling and edge case coverage
The RSS backend now follows industry best practices with optimal
performance, full standards compliance, and production-ready reliability.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements critical OAuth 2.0 security improvements to protect against authorization code interception attacks and improve provider compatibility:
- Add PKCE (RFC 7636) support with SHA-256 code challenge/verifier
- Fix access token extraction to use standard field instead of Extra()
- Add OAuth error parameter handling (access_denied, invalid_scope, etc.)
- Maintain backward compatibility for non-PKCE flows
This brings the OAuth implementation up to modern security standards as recommended by Auth0, Okta, and the OAuth 2.0 Security Best Current Practice (RFC 8252).
Backend changes:
- Add code_verifier parameter to ExchangeToken with PKCE support
- Use token.AccessToken for better provider compatibility
- Update proto definition with optional code_verifier field
Frontend changes:
- Generate cryptographically secure PKCE parameters
- Include code_challenge in authorization requests
- Handle and display OAuth provider errors gracefully
- Pass code_verifier during token exchange
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove enable_link_preview field from proto definitions
- Remove setting UI from MemoRelatedSettings component
- Remove translations from all 33 locale files
- Regenerate proto files
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove disable_markdown_shortcuts field from proto definitions
- Remove setting UI from MemoRelatedSettings component
- Enable markdown shortcuts permanently in MemoEditor
- Remove translations from all 32 locale files
- Fix TypeScript error in useMemoSave hook by using typed translation function
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added MemoView component to display a single memo card with full functionality including creator info, memo content, attachments, reactions, and comments.
- Created MemoBody and MemoHeader subcomponents to separate concerns and improve maintainability.
- Introduced custom hooks for managing memo actions, keyboard shortcuts, NSFW content visibility, and image preview.
- Implemented reaction handling with new ReactionSelector and ReactionView components.
- Added TypeScript types for better type safety and clarity.
- Established constants for memo card styling and keyboard shortcuts.
- Removed legacy ReactionSelector and ReactionView components from the previous structure.
- Implemented LocationDialog component for selecting and entering location coordinates.
- Created useLocation hook to manage location state and updates.
- Added LocationState type for managing location data.
- Introduced useLinkMemo hook for linking memos with search functionality.
- Added VisibilitySelector component for selecting memo visibility.
- Refactored MemoEditor to integrate new hooks and components for improved functionality.
- Removed obsolete handlers and streamlined memo save logic with useMemoSave hook.
- Enhanced focus mode functionality with dedicated components for overlay and exit button.
Tags were missing CSS styles, appearing as plain text. Added text-primary
styling to Tag component and updated default theme primary color to match
other themes (blue hue 250, chroma 0.08) for consistency.
Fixes#5282🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Resolves issue where tag filtering in PostgreSQL databases failed with "operator does not exist: jsonb ~~ unknown" error. The hierarchical tag filtering feature introduced in commit 5e47f25b generated SQL with implicit type placeholders that PostgreSQL couldn't infer.
The fix explicitly casts the LIKE comparison placeholder to text (::text) in the PostgreSQL dialect, ensuring proper type resolution for the query parameter.
Fixes#5275🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The truncateAtWord function was slicing strings by byte position instead of
character position. When truncating text with multi-byte UTF-8 characters
(like CJK), this could cut in the middle of a character, creating invalid
UTF-8 and causing gRPC marshaling errors.
Fixed by converting to runes before truncation to ensure we always cut at
proper character boundaries. Added test cases for CJK characters.
Fixes#5276🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This fixes multiple issues with the tag sidebar and activity calendar:
1. Tag disappearing bug: When filtering by a tag, the sidebar now shows all tags instead of only the selected tag
2. Activity calendar filtering: Calendar now shows full activity history instead of filtered results
3. Auto-update on memo changes: Sidebar tags and calendar now update automatically when creating/editing memos without requiring manual page refresh
Technical changes:
- Modified useFilteredMemoStats to fetch unfiltered UserStats from backend API for Home/Profile pages
- Fixed key mismatch bug in userStore where stats were stored with inconsistent keys
- Added statsStateId update in fetchUserStats to trigger reactivity
- Updated MainLayout to pass appropriate userName based on page context
- Archived/Explore pages continue to compute from cached memos (correct behavior)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Pass i18n.language to time display components to ensure locale updates
when the user switches languages in UserMenu. Changes:
- MemoView: Pass i18n.language to toLocaleString() and <relative-time> lang attribute
- MonthNavigator: Wrap with observer to make component reactive to i18n.language changes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace custom character whitelist with Unicode standards-based validation:
- Use unicode.IsLetter/IsNumber/IsSymbol instead of hardcoded lists
- Remove manual UTF-8 byte checking for CJK punctuation
- Add proper rune-based length limiting (MAX_TAG_LENGTH = 100)
- Improve international character support (CJK, Arabic, Cyrillic, etc.)
- Add emoji support via unicode.IsSymbol
Benefits:
- Cleaner, more maintainable code (~50 lines removed)
- Standards-based approach following Unicode categories
- Better UTF-8 safety with utf8.DecodeRune
- Consistent validation between Go backend and TypeScript frontend
All existing tests pass with improved Unicode handling.
- Direct security reports to email only instead of public GitHub issues
- Set clear expectations: no CVEs during beta (v0.x) phase
- Add security best practices for self-hosted deployments
- Plan formal vulnerability disclosure program for v1.0+
Addresses #5255
The sidebar tag list wasn't updating when users created new memos with tags
or modified existing memo tags. This was because useFilteredMemoStats hook
only refetched when filter/state/orderBy changed.
Now the hook observes memoStore.state.stateId, which changes whenever memos
are created, updated, or deleted. This triggers automatic refetch and the
sidebar updates immediately with the latest tag counts.
Fixes tag refresh issue in sidebar
- Add MapCleanup component to properly remove Leaflet map instances on unmount
- Fix LocationMarker initialization with useRef to prevent re-initialization
- Remove problematic key prop in LocationDialog that caused unnecessary remounts
- Fix goimports formatting in tag parser
Fixes#5260
Fixes#5264
Chinese, Japanese, Korean, and other Unicode characters are now
properly recognized in hashtags, following the standard hashtag
parsing conventions used by Twitter, Instagram, and GitHub.
Changes:
- Updated tag parser to allow Unicode letters and digits
- Tags stop at whitespace and punctuation (both ASCII and CJK)
- Allow dash, underscore, forward slash in tags
- Added comprehensive tests for CJK characters and emoji
Examples:
- #测试 → recognized as tag '测试'
- #日本語 → recognized as tag '日本語'
- #한국어 → recognized as tag '한국어'
- #测试。→ recognized as tag '测试' (stops at punctuation)
- #work/测试/项目 → hierarchical tag with Unicode
Removed 3 markdown files that provided no useful documentation:
- web/README.md: Single line "The frontend of Memos" (redundant)
- web/src/components/kit/README.md: Single line "Base components" (minimal)
- web/MARKDOWN_STYLE_GUIDE.md: Outdated styling guide (no longer applicable)
Kept comprehensive documentation files:
- web/src/components/MasonryView/README.md (implementation details)
- web/src/themes/COLOR_GUIDE.md (design system guide)
- web/src/components/ConfirmDialog/README.md (component API docs)
- web/src/store/README.md (architecture patterns)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add system theme listener to detect OS theme changes in real-time
- Refactor to eliminate duplicate theme preference extraction
- Simplify getMermaidTheme function from switch statement to ternary
- Move render guard outside async function for better readability
- Update comments to be more concise and focused
The component now properly re-renders Mermaid diagrams when the OS theme changes while using "system" theme preference.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Streamlines the command suggestion interface and fixes list auto-completion behavior:
- Remove command descriptions for cleaner suggestion popup UI
- Replace PaperclipIcon with FileIcon for semantic accuracy
- Fix list auto-completion to avoid extra newline when exiting list mode
- Add explanatory comments for cursor offset positions
- Improve dependency array in useListAutoCompletion hook
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Improve bug report and feature request templates to reduce duplicate submissions and gather better information:
Bug Report Template:
- Add pre-submission checklist requiring users to search existing issues and test on latest version/demo
- Add dropdown for issue location (stable, dev, demo site, older version)
- Restructure fields with clearer labels and better placeholders
- Add "Expected Behavior" section for clarity
- Combine screenshots and context with helpful prompts
Feature Request Template:
- Add pre-submission checklist to confirm issue search
- Expand feature type categories (API/Backend, Integrations/Plugins, Security/Privacy, Performance)
- Add "Problem or Use Case" field to understand the underlying need
- Add "Alternatives Considered" section
- Improve placeholders with specific examples
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Backend changes:
- Fix ListAllUserStats to calculate and return tag statistics
- Previously only returned name and timestamps, missing TagCount
- Now properly aggregates tags, pinned memos, and memo type stats
Frontend changes:
- Initialize user stats on app startup to populate tag data
- Show all tags when typing just '#' (fix empty Fuse.js search)
- Auto-refresh stats after creating/updating/deleting memos
- Fix Checkbox component ref warning with forwardRef
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Address high memory usage when opening resource tab (fixes#5183) by implementing:
1. Concurrency control: Limit thumbnail generation to 3 concurrent operations using semaphore to prevent memory exhaustion when many images are requested simultaneously
2. S3 optimization: Skip server-side thumbnail generation for S3-stored images by default. S3 images now use presigned URLs directly, avoiding:
- Downloading large images from S3 into server memory
- Decoding and resizing images on the server
- High memory consumption during batch requests
3. Memory management improvements:
- Explicitly clear blob and decoded image from memory after use
- Restructure thumbnail cache check to avoid unnecessary semaphore acquisition
- Double-check pattern to prevent duplicate generation while waiting
This restores the original S3 behavior before commit e4f6345 while maintaining thumbnail support for local/database storage.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Replace native input with Radix UI Checkbox in TaskListItem for better accessibility and consistent styling
- Remove memoTypeStats tracking and display (link count, todo count, code count)
- Remove StatCard component and related type definitions
- Simplify statistics to only track activity calendar data and tags
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Changed first-user detection to check for any users instead of only HOST users
- Moved registration setting check before role assignment to properly block unauthorized registrations
- Fixed role assignment logic to ensure unauthenticated users always get USER role
- Updated test cases to create host user first when not testing first-user scenario
This ensures the first user is always created as HOST and registration settings are properly enforced for subsequent user creation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace non-existent GetWorkspaceGeneralSetting with GetInstanceGeneralSetting
to properly check if user registration is allowed. This fixes a compilation
error that was preventing tests from running.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Polish inbox notification items with improved visual hierarchy
- Add original memo snippet with left border indicator
- Redesign comment preview with gradient background and primary accent
- Increase spacing and improve typography with consistent sizing
- Add ring borders to avatars and refined icon badges
- Enhance loading and error states with better skeleton designs
- Improve hover states and transitions throughout
- Redesign user profile header layout
- Create full-width centered header with avatar and user info
- Add horizontal layout for profile actions
- Improve responsive design with proper flex wrapping
- Allow memo list to use full width for masonry layout
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Nominatim's usage policy requires a User-Agent header to identify the application. Added User-Agent and Accept headers to the reverse geocoding fetch request, and improved error handling to check HTTP response status.
Fixes#5222🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove work-related terminology by renaming "workspace" to "instance"
across the entire application. This change better reflects that Memos
is a self-hosted tool suitable for personal and non-work use cases.
Breaking Changes:
- API endpoints: /api/v1/workspace/* → /api/v1/instance/*
- gRPC service: WorkspaceService → InstanceService
- Proto types: WorkspaceSetting → InstanceSetting
- Frontend translation keys: workspace-section → instance-section
Backend Changes:
- Renamed proto definitions and regenerated code
- Updated all store layer methods and database drivers
- Renamed service implementations and API handlers
- Updated cache from workspaceSettingCache to instanceSettingCache
Frontend Changes:
- Renamed service client: workspaceServiceClient → instanceServiceClient
- Updated all React components and state management
- Refactored stores: workspace.ts → instance.ts
- Updated all 32 locale translation files
All tests pass and both backend and frontend build successfully.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Create unified architecture for memo statistics, filters, and sorting
across all pages (Home, Explore, Archived, Profile) with proper
visibility filtering and consistent data flow.
Key changes:
- Rename HomeLayout → MainLayout to reflect broader usage
- Create useFilteredMemoStats hook for unified stats computation
- Create useMemoFilters/useMemoSorting hooks to eliminate duplication
- Refactor all pages to use unified hooks (~147 lines removed)
- Move Explore route under MainLayout (was sibling before)
- Fix masonry column calculation threshold (1024px → 688px+)
Architecture improvements:
- MainLayout computes filter/stats per route context
- Stats/tags based on same filter as memo list (consistency)
- Proper visibility filtering (PUBLIC/PROTECTED) on Explore
- MemoExplorer/StatisticsView accept stats as required props
- Eliminated optional fallbacks and redundant data fetching
Benefits:
- Single source of truth for stats computation
- Stats remain static (don't change with user filters)
- Reduced code duplication across 4 pages
- Better maintainability and type safety
- Proper security (no private memo leakage on Explore)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed a bug where clicking checkboxes in task lists would toggle the wrong
checkbox when a memo contained multiple sections with separate task lists.
The issue was that TaskListItem was counting tasks only within the immediate
parent list (ul/ol), but the toggleTaskAtIndex function counts all tasks
globally across the entire memo. This caused index misalignment.
Changes:
- Add containerRef to MemoContentContext for proper task scoping
- Pass memoContentContainerRef through context in MemoContent component
- Update TaskListItem to count all tasks within the container scope
This ensures task indices are calculated consistently with the markdown
manipulation logic, fixing checkbox toggling in complex multi-section memos.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Replace multiple action buttons with unified InsertMenu dropdown
- Consolidate upload, link memo, and location into single + button
- Redesign VisibilitySelector with text-based dropdown UI
- Unify badge styling for location, attachments, and links
- Consistent height (h-7), padding, gaps, and border styles
- Secondary foreground text color with hover states
- Max width with truncation for long content
- Add image thumbnails in attachment badges
- Simplify button hierarchy with ghost variant for save/cancel
- Remove obsolete components (TagSelector, MarkdownMenu, etc.)
- Extract LocationView to separate component for better organization
Fixes#5196
- Remove standalone InboxService and move functionality to UserService
- Rename inbox to user notifications for better API consistency
- Add ListUserNotifications, UpdateUserNotification, DeleteUserNotification methods
- Update frontend components to use new notification endpoints
- Update store layer to support new notification model
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Refactored MemoFilters.tsx for better maintainability:
* Centralized filter configuration with FILTER_CONFIGS object
* Added TypeScript interfaces for type safety
* Removed separate FactorIcon component
* Extracted handleRemoveFilter function
* Improved imports organization
- Polished MemoFilters UI styles:
* Changed to modern pill/badge design with rounded-full
* Enhanced spacing and color schemes
* Added smooth transitions and hover effects
* Improved interactive remove button with destructive color hints
* Better text readability with font-medium
- Added comprehensive filter support to all pages:
* Explore page: Added full filter support (was missing)
* Archived page: Enhanced from basic to full filter support
* UserProfile page: Enhanced from basic to full filter support
* All pages now support: content search, tag search, pinned, hasLink, hasTaskList, hasCode, and displayTime filters
- Consistency improvements:
* All pages using PagedMemoList now have identical filter logic
* Respects workspace settings for display time (created/updated)
* Unified filter behavior across Home, Explore, Archived, and UserProfile pages
Tag filters now support hierarchical matching where searching for a tag (e.g., "book") will match both the exact tag and any tags with that prefix (e.g., "book/fiction", "book/non-fiction"). This applies across all database backends (SQLite, MySQL, PostgreSQL) with corresponding test updates.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove extra blank line in memo_service.go (goimports)
- Remove invalid fields from CreateMemoRequest call (validateOnly, requestId)
- Clean up unnecessary comments
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
BREAKING CHANGE: Removed DeleteMemoTag and RenameMemoTag API endpoints
for better API consistency. Tags should now be managed by updating memo
content directly via UpdateMemo endpoint.
Backend changes:
- Remove RenameMemoTag and DeleteMemoTag RPC methods from proto
- Remove backend implementations in memo_service.go
- Regenerate protocol buffers (Go, TypeScript, OpenAPI)
Frontend changes:
- Remove RenameTagDialog component
- Simplify TagsSection to remove rename/delete functionality
- Improve tag styling with active state highlighting
- Add smooth transitions and better hover interactions
- Polish TagTree component for consistency
- Tags now only support click-to-filter (no inline editing)
Style improvements:
- Active tags highlighted with primary color and font-medium
- Consistent hover states across flat and tree views
- Better spacing and visual hierarchy
- Improved empty state styling
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add language and theme selector submenus to UserMenu component for quick access
- Refactor shared utilities: extract THEME_OPTIONS constant and getLocaleDisplayName() function
- Update LocaleSelect and ThemeSelect to use shared utilities, eliminating code duplication
- Make UserMenu reactive with MobX observer for real-time setting updates
- Fix language switching reactivity by immediately updating workspaceStore.state.locale
- Add scrollable menu support for language selector (max-h-[90vh])
- Apply same instant locale update to PreferencesSection for consistency
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add custom code block renderer with language display and copy functionality. Links now open in new windows, and clicking image links no longer triggers both link navigation and image preview.
- Add CodeBlock component with copy-to-clipboard button and language label
- Configure all markdown links to open in new windows with target="_blank"
- Fix image link behavior to prevent duplicate actions when clicked
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove disruptive error toasts when reverse geocoding or geolocation fails.
Instead, silently fall back to using coordinates as the location placeholder.
This improves UX for users in regions where OpenStreetMap is restricted
(e.g., China) or when CSP blocks external API calls.
Fixes#5198, #5197🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed issue #5194 where changing the layout (List/Masonry) or sort direction
didn't update the UI until page refresh. The root cause was that ViewState
fields weren't marked as MobX observables, so the UI didn't react to changes
even though values were being persisted to localStorage.
Solution: Added constructor to ViewState that marks orderByTimeAsc and layout
fields as observable, following the same pattern used in other stores like
MemoFilterState.
Fixes#5194🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed Cache-Control max-age from 7 days to 1 hour with immutable directive.
This prevents users from experiencing blank pages or JS errors when accessing
frequently redeployed instances (e.g., demo environments) where old cached
assets may reference files that no longer exist after redeployment.
Since Vite generates content-hashed filenames, the immutable directive prevents
unnecessary revalidation while the shorter cache duration ensures fresh assets
are served within an hour of redeployment.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add comprehensive inline documentation for auth services
- Document session-based and token-based authentication flows
- Clarify authentication priority and validation logic
- Add detailed comments for JWT token structure and claims
- Fix reactions seed data to use memo UIDs instead of numeric IDs
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Removed the wikilink extension from markdown services in test and API service.
- Deleted the DefaultLink and WikiLink components, simplifying link handling.
- Updated ConditionalComponent to remove wikilink checks.
- Adjusted MemoContent to exclude wikilink handling in markdown rendering.
- Refined markdown styles for compact rendering, enhancing readability.
- Added a Markdown Styling Guide to document the new compact styling approach.
- Removed the `nodes` field from the `Memo` interface in `memo_service.ts`.
- Updated the `createBaseMemo` function and the `Memo` message functions to reflect the removal of `nodes`.
- Cleaned up the serialization and deserialization logic accordingly.
chore: remove code-inspector-plugin from Vite configuration
- Deleted the `codeInspectorPlugin` from the Vite configuration in `vite.config.mts`.
- Simplified the plugins array to include only `react` and `tailwindcss`.
Fixes the root cause of non-reactive filtering. The MemoFilterState class
was not marking its fields as observable in MobX, so changes to the filters
array were not being tracked.
Added makeObservable configuration to mark:
- filters and shortcut as observable
- addFilter, removeFilter, removeFiltersByFactor, clearAllFilters, setShortcut as actions
This ensures that when tags are clicked and filters are added/removed,
MobX observer components will re-render and fetch new data.
Related to #5189🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes filtering functionality that was broken due to improper use of
useMemo with MobX observables. The issue occurred because useMemo's
dependency array uses reference equality, but MobX observable arrays
are mutated in place (reference doesn't change when items are added/removed).
Changes:
- Remove useMemo from filter computation in Home, UserProfile, and Archived pages
- Calculate filters directly in render since components are already MobX observers
- Fix typo: memoFitler -> memoFilter in Archived.tsx
This ensures filters are recalculated whenever memoFilterStore.filters changes,
making tag clicks and other filter interactions work correctly.
Fixes#5189🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes a regression introduced in v0.25.2 where PostgreSQL IN clause
placeholders were not properly incremented, causing all parameters to
use the same placeholder index (e.g., $1, $1, $1 instead of $1, $2, $3).
This bug affected:
- ListReactions (ContentIDList) - caused "failed to list reactions" errors
- ListAttachments (MemoIDList)
- ListMemos (IDList and UIDList)
The fix combines placeholder generation and argument appending into a
single loop to ensure proper incrementing.
Fixes#5188🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add hover-based visibility for reaction selector in memo cards
- Show reaction selector only on card hover or when popover is open
- Add onOpenChange callback to ReactionSelector for state management
- Reorder action buttons for better visual hierarchy
- Simplify conditional rendering of comment link
- Update GetUser to accept both numeric IDs and username strings (users/{id} or users/{username})
- Implement CEL filter parsing for username-based lookups
- Update proto documentation to reflect dual lookup capability
- Simplify frontend user store to use GetUser instead of ListUsers filter
- Update ListUsers filter documentation to show current capabilities
- Replace fixed width (max-w-56) with responsive viewport-based width
- Use 90vw on mobile and max-w-md on desktop for better adaptability
- Add vertical scrolling with max-h-64 overflow-y-auto
- Increase grid columns to 8 on medium+ screens for better space utilization
- Ensures all reactions are accessible regardless of quantity
Fixes#5117
Co-authored-by: GitHub Copilot <noreply@github.com>
- Updated memo and reaction filtering logic to use a unified engine for compiling filter expressions into SQL statements.
- Removed redundant filter parsing and conversion code from ListMemoRelations, ListReactions, and ListAttachments methods.
- Introduced IDList and UIDList fields in FindMemo and FindReaction structs to support filtering by multiple IDs.
- Removed old filter test files for reactions and attachments, as the filtering logic has been centralized.
- Updated tests for memo filtering to reflect the new SQL statement compilation approach.
- Ensured that unsupported user filters return an error in ListUsers method.
- Bump protoc-gen-go version from v1.36.8 to v1.36.9 in multiple generated files.
- Enhance OpenAPI descriptions for various endpoints to improve clarity and readability.
- Fix typo in the description list handling in the markdown service.
- Update HTMLElement component to handle children and self-closing tags correctly.
- Modify types in the markdown service to include children and self-closing properties for HTML elements.
* fix(editor): Prevent race condition and ensure correct list continuation on Enter
_
* fix: always insert newline after preventing default Enter key behavior
* fix: make the save button disabled after a successful update
* fix: fixing formatting error
---------
Co-authored-by: didi <didi@localhost.localdomain>
* feat: update ItalicNode to use repeated children instead of content
* chore: update gomark dependency to latest version
---------
Co-authored-by: Anubhav Singh <anubhav@mail.techindika.com>
- sort all keys alphabetically for easier side-by-side translation
- remove keys from other locales that don't exist anymore in `en.json`
- remove keys from other locales that have missing {{interpolations}}
- add missing entries to pt-BR
* chore:memo filter support i18n for zh-hans
* feat: restore addFilter method value field in StatisticsView.tsx
* chore: change getFilterDisplayText for i18n
* chore: add string type in utils/i18n
* feat: using switch to avoid change TranslationType
* feat: eslint check
* feat: eslint fix
* feat: test dev push
* feat: add zh Hans text
* feat: restore readme
* feat: update zh hans text
* feat: add tag for properties label
* feat: updarte zh hans translation
* feat: add tab
* fix: migrate sql error
sqlite not support postgres sql
* Apply suggestions from code review
---------
Co-authored-by: Johnny <yourselfhosted@gmail.com>
* fix#4257 fixed ui for mermaid diagrams in dark mode
* fixed linting issues
* added dynamic color theme for mermaid diagrams
* now mermaid block uses theme according to the system as well when 'follow system' is selected
* refactored code for mermaid theme logic
* refactored mermaid code to use const instead of function call
---------
Co-authored-by: root <root@DESKTOP-G3MCU14>
* feat: persist memo filters with url query
- Implemented synchronization of memo filters with URL search parameters.
- Enhanced memo store to manage current request state for fetching memos, allowing for request cancellation, avoiding the race conditions.
* refactor: update import
- upgrade modernc.org/libc to v1.34.2
libc version on go.mod must match upstream's sqlite go.mod file to prevent similar issues.
https://pkg.go.dev/modernc.org/sqlite#hdr-Fragile_modernc_org_libc_dependency
```
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGBUS: bus error code=0x2 addr=0x0 pc=0xdd8568]
```
* [fix] add server name change in mobile header
* Update web/src/components/MobileHeader.tsx
* Update web/src/components/MobileHeader.tsx
* Apply suggestions from code review
---------
Co-authored-by: Николай Витальевич Никоноров <nnv@bitt.moe>
Co-authored-by: Johnny <yourselfhosted@gmail.com>
* fix#4144 now when there is no sso then the Disallow password auth is disabled
* fixed the import issue
* attempt fixing linting issues
---------
Co-authored-by: pransh62390 <63577123+pransh62390@users.noreply.github.com>
This removes the content of the <title> element in the RSS feeds that Memo produces.
Why remove? Every RSS client I can find shows the <title> next to the <description> when viewing an item. This creates a duplicate (but often trimmed, so less useful) version of <description> right above the actual text the user wants to read (often in a much larger font). It similarly makes lists of items in some clients extremely tall, as 128 characters is a lot of hard-to-read text — especially when Memos renders links as their URL in titles.
Why an empty tag? The RSS 1.0 and 2.0 specs require that a <title> element is present.
Examples from elsewhere:
- micro.blog uses an empty <title /> element: https://www.manton.org/feed.xml
- Bluesky omits the <title> element: https://bsky.app/profile/did%3Aplc%3Aqvzn322kmcvd7xtnips5xaun/rss
- Mastodon omits the <title> element: https://mastodon.social/@scalzi.rss
* migration version
* Updated test to match with current version set in version.go file
* updated logic to check if migration should run for fresh install
* Update test to test for version number set in version.go
* fixed test
* Revert "fixed test"
This reverts commit 4004d0e92b.
* Revert "Update test to test for version number set in version.go"
This reverts commit d304add5d6.
* Revert "updated logic to check if migration should run for fresh install"
This reverts commit b44dca2194.
* Revert "Updated test to match with current version set in version.go file"
This reverts commit 3ad0d889b6.
* Revert "migration version"
This reverts commit b13cfc346a.
* Added default reaction if no reation found
* Fixed static check issue
* Use emoji
* fix: automatic indentation follows previous lines in lists (#4048)
* fix: automatic indentation follows previous lines in lists (#4048)
change the position of this logic and recommit it
* feat: support set global Default memo visibility
* feat: Move to WorkspaceMemoRelatedSetting
* fix: goimports check failure
* chore: change item name to default_visibility
* fix: goimports check failure
* Allow single letter user id.
They do work - I use one and login using Authelia, but then I am not able to update the profile to update avatar or add comment because "Invalid username: r" errors
* Add test for the util with uid matcher
* Move the test to separate directory
* Use goimports on the test file
* Rename the test for a mroe matching name, add failing test
* Update the regexp so that two letter work
---------
Co-authored-by: Roman Kamyk <roman@kamyk.me>
* Add tag count to tree view
* Only display tag amounts > 1
* Updated tag amount var name to be more descriptive
* - Moved display logic to html in return
- Updated var names to closer match var passed in by TagSection component
* Set viewport scalable in image preview dialog for mobile zooming and reset it on destroy
* Format with prettier
* move setViewportScalable() into useEffect
* use const instead of function
* Set max thumbnail width to timeline img max width
* Prevent images less than thumbnail size from being scaled up
* Apply suggestions from code review
---------
Co-authored-by: boojack <24653555+boojack@users.noreply.github.com>
- Change base image to distroless (static-debian12)
- Remove unnecessary RUN instructions
- Add build options
- add options to delete debug information
- add options to remobe embedded paths
* Reintroducing thumbnails
* Aligned with linting rules
* making recomended code review change
- changed method names to start with lower case as they are not used outside of their package
- made receiver types for struct funcs to be pointers to not need to create copies
Trying to cover all linting issues
- converted slog warning to use attributes when logging warnings
- seperated imports to have package files in their own section
* Update go.mod
---------
Co-authored-by: boojack <24653555+boojack@users.noreply.github.com>
Fixes#3645, currently the release containers are build from the release branches, which are created in preparation for a release. This causes some users with automated CI/CD to either deploy a pre-release version or be notified of a release version before it has actually been released.
This change switches the workflow to be triggered by tags, which are created on the release version.
* fix: code blocks of unknown languages cause HTML injection
A code block of unknown language (that is, a language not treated as special by Memos and not handled by highlight.js) should fall back on rendering its plaintext content. However, the content is never properly escaped before it is appended to the DOM, and thus any string that happens to contain HTML is unsafely rendered. This commit fixes the issue by ensuring that, when none of the previous cases handle the text, any HTML entities are escaped first.
* Update CodeBlock.tsx to conform to eslint
When using the "export to Markdown" feature in Memos, the files included in the ZIP folder don't feature any kind of reference to their alphanumeric UID from the server's database, which completely breaks the point of links inside the files made with the `[[memos/<uid>]]` format, since it's impossible to know which Markdown file this kind of string inside other files refers to.
This pull request modifies the ExportMemos server function, to add the UID of every memo immediately after the date in the filename. For example, an exported memo would now be called: `YYYY-MM-DDThh:mm:ss+hh:mm-AbcDefGhiJklMnoPqrStu1-PUBLIC.md`.
Before submitting a bug report, please check if the issue is already present in the issues. If it is, please add a reaction to the issue. If it isn't, please fill out the form below.
- type:textarea
Thank you for taking the time to report a bug! Please complete the form below to help us understand and fix the issue.
- type:checkboxes
id:pre-check
attributes:
label:Describe the bug
description:|
Briefly describe the problem you are having in a few paragraphs.
label:Pre-submission Checklist
description:Please confirm you have completed the following steps before submitting
options:
- label:I have searched the existing issues and this bug has not been reported yet
required:true
- label:I have tested this issue on the [demo site](https://demo.usememos.com) or the latest version
required:true
- type:dropdown
id:issue-location
attributes:
label:Where did you encounter this bug?
description:Select where you tested and confirmed this issue
options:
- Latest stable version (self-hosted)
- Latest development version (self-hosted)
- Demo site (demo.usememos.com)
- Older version (please specify below)
default:0
validations:
required:true
- type:textarea
- type:input
id:version
attributes:
label:Steps to reproduce
description:|
Provide the steps to reproduce the issue.
label:Memos Version
description:Provide the exact version (e.g., `v0.25.2`). Find this in Settings → About or via `--version` flag
placeholder:v0.25.2
validations:
required:true
- type:textarea
id:bug-description
attributes:
label:Bug Description
description:A clear and concise description of what the bug is
placeholder:When I try to..., the application...
validations:
required:true
- type:textarea
id:reproduction-steps
attributes:
label:Steps to Reproduce
description:Detailed steps to reproduce the behavior
placeholder:|
1. Go to '...'
2. Click on '....'
3. See error
validations:
required:true
- type:input
attributes:
label:The version of Memos you're using
description:|
Provide the version of Memos you're using.
2. Click on '...'
3. Scroll down to '...'
4. See error
validations:
required:true
- type:textarea
id:expected-behavior
attributes:
label:Screenshots or additional context
description:|
If applicable, add screenshots to help explain your problem. And add any other context about the problem here. Such as the device you're using, etc.
label:Expected Behavior
description:What did you expect to happen?
placeholder:I expected...
- type:textarea
id:additional-context
attributes:
label:Screenshots & Additional Context
description:Add screenshots, browser/OS info, deployment method (Docker/binary), or any other relevant details
Before submitting a feature request, please check if the issue is already present in the issues. If it is, please add a reaction to the issue. If it isn't, please fill out the form below.
- type:textarea
Thank you for suggesting a new feature! Please complete the form below to help us understand your idea.
- type:checkboxes
id:pre-check
attributes:
label:Describe the solution you'd like
description:|
A clear and concise description of what you want to happen.
placeholder:|
It would be great if [...]
validations:
required:true
label:Pre-submission Checklist
description:Please confirm you have completed the following steps before submitting
options:
- label:I have searched the existing issues and this feature has not been requested yet
required:true
- type:dropdown
id:feature-type
attributes:
label:Type of feature
label:Type of Feature
description:What type of feature is this?
options:
- User Interface (UI)
- User Experience (UX)
- API
- API / Backend
- Documentation
- Integrations
- Integrations / Plugins
- Security / Privacy
- Performance
- Other
default:0
validations:
required:true
- type:textarea
id:problem-statement
attributes:
label:Additional context
description:|
What are you trying to do? Why is this important to you?
label:Problem or Use Case
description:What problem does this feature solve? What are you trying to accomplish?
placeholder:|
I often need to... but currently there's no way to...
This would help me/users to...
validations:
required:true
- type:textarea
id:proposed-solution
attributes:
label:Proposed Solution
description:A clear and concise description of what you want to happen
placeholder:|
It would be great if Memos could...
For example, a button/feature that...
validations:
required:true
- type:textarea
id:alternatives
attributes:
label:Alternatives Considered
description:Have you considered any alternative solutions or workarounds?
placeholder:|
I've tried... but it doesn't work well because...
An alternative could be...
- type:textarea
id:additional-context
attributes:
label:Additional Context
description:Add any other context, mockups, screenshots, or examples about the feature request
# not require, default false, . Decide whether to modify the issue title
# if true, the robot account @Issues-translate-bot must have modification permissions, invite @Issues-translate-bot to your project or use your custom bot.
CUSTOM_BOT_NOTE:Issue is not in English. It has been translated automatically.
# not require. Customize the translation robot prefix message.
This document provides comprehensive guidance for AI agents working with the Memos codebase. It covers architecture, workflows, conventions, and key patterns.
## Project Overview
Memos is a self-hosted knowledge management platform built with:
- **Open source and free to use**. Embrace a future where creativity knows no boundaries with our open-source solution. All features are free to use and will never be charged in any form or content.
- **Self-hosting with Docker in just seconds**. Enjoy the flexibility, scalability, and ease of setup that Docker provides, allowing you to have full control over your data and privacy.
- **Pure text with added Markdown support.** Say goodbye to the overwhelming mental burden of rich formatting and embrace a minimalist approach.
- **Customize and share your notes effortlessly**. With our intuitive sharing features, you can easily collaborate and distribute your notes with others.
- **RESTful API for third-party services.** Embrace the power of integration and unleash new possibilities with our RESTful API support.
[**Warp** — The AI-powered terminal built for speed and collaboration](https://go.warp.dev/memos)
## Deploy with Docker in seconds
```bash
docker run -d --name memos -p 5230:5230 -v ~/.memos/:/var/opt/memos neosmemo/memos:stable
```
> [!NOTE]
> This command is only applicable for Unix/Linux systems. For Windows, please refer to the detailed [documentation](https://www.usememos.com/docs/install/self-hosting).
>
> The `~/.memos/` directory will be used as the data directory on your local machine, while `/var/opt/memos` is the directory of the volume in Docker and should not be modified.
Learn more about [other installation methods](https://www.usememos.com/docs/install).
## Contribution
Contributions are what make the open-source community such an amazing place to learn, inspire, and create. We greatly appreciate any contributions you make. Thank you for being a part of our community! 🥰
## Internationalization
Memos supports multiple languages. You can help us translate Memos into your language. We use Weblate to manage translations.
<img src="https://raw.githubusercontent.com/warpdotdev/brand-assets/main/Github/Sponsor/Warp-Github-LG-02.png" alt="Warp - The AI-powered terminal built for speed and collaboration" width="512" />
</a>
## Star history
<p></p>
[**TestMu AI** - The world’s first full-stack Agentic AI Quality Engineering platform](https://www.testmuai.com/?utm_medium=sponsor&utm_source=memos)
Memos is a privacy-first, self-hosted knowledge base for personal notes, team wikis, and knowledge management. Built with Go and React, it runs as a single binary with minimal resource usage.
## Features
- **Privacy-First** — Self-hosted on your infrastructure with zero telemetry, no tracking, and no ads.
- **Markdown Native** — Full markdown support with plain text storage. Your data is always portable.
- **Lightweight** — Single Go binary with a React frontend. Low memory footprint, starts in seconds.
- **Easy to Deploy** — One-line Docker install. Supports SQLite, MySQL, and PostgreSQL.
- **Developer-Friendly** — Full REST and gRPC APIs for integration with existing workflows.
- **Clean Interface** — Minimal design with dark mode and mobile-responsive layout.
## Quick Start
### Docker (Recommended)
```bash
docker run -d \
--name memos \
-p 5230:5230 \
-v ~/.memos:/var/opt/memos \
neosmemo/memos:stable
```
Open `http://localhost:5230` and start writing!
### Try the Live Demo
Don't want to install yet? Try our [live demo](https://demo.usememos.com/) first!
### Other Installation Methods
- **Docker Compose** - Recommended for production deployments
- **Pre-built Binaries** - Available for Linux, macOS, and Windows
- **Kubernetes** - Helm charts and manifests available
- **Build from Source** - For development and customization
See our [installation guide](https://usememos.com/docs/deploy) for detailed instructions.
## Contributing
Contributions are welcome — bug reports, feature suggestions, pull requests, documentation, and translations.
- [Help with translations](https://github.com/usememos/memos/tree/main/web/src/locales)
## Sponsors
Love Memos? [Sponsor us on GitHub](https://github.com/sponsors/usememos) to help keep the project growing!
## Star History
[](https://star-history.com/#usememos/memos&Date)
## Other projects
## License
- [**Slash**](https://github.com/yourselfhosted/slash): An opensource, self-hosted bookmarks and link sharing platform. Save and share your links very easily.
- [**Gomark**](https://github.com/usememos/gomark): A markdown parser written in Go for Memos. And its [WebAssembly version](https://github.com/usememos/gomark-wasm) is also available.
Memos is open-source software licensed under the [MIT License](LICENSE).
## Privacy Policy
Memos is built with privacy as a core principle. As a self-hosted application, all your data stays on your infrastructure. There is no telemetry, no tracking, and no data collection. See our [Privacy Policy](https://usememos.com/privacy) for details.
Report security bugs via GitHub [issues](https://github.com/usememos/memos/issues).
Memos is currently in beta (v0.x). While we take security seriously, we are not yet ready for formal CVE assignments or coordinated disclosure programs.
For more information, please contact [usememos@gmail.com](usememos@gmail.com).
## Reporting Security Issues
### For All Security Concerns:
Please report via **email only**: dev@usememos.com
**DO NOT open public GitHub issues for security vulnerabilities.**
Include in your report:
- Description of the issue
- Steps to reproduce
- Affected versions
- Your assessment of severity
### What to Expect:
- We will acknowledge your report as soon as we can
- Fixes will be included in regular releases without special security advisories
- No CVEs will be assigned during the beta phase
- Credit will be given in release notes if you wish
### For Non-Security Bugs:
Use GitHub issues for functionality bugs, feature requests, and general questions.
## Philosophy
As a beta project, we prioritize:
1.**Rapid iteration** over lengthy disclosure timelines
2.**Quick patches** over formal security processes
3.**Transparency** about our beta status
We plan to implement formal vulnerability disclosure and CVE handling after reaching v1.0 stable.
## Self-Hosting Security
Since Memos is self-hosted software:
- Keep your instance updated to the latest release
- Don't expose your instance directly to the internet without authentication
- Use reverse proxies (nginx, Caddy) with rate limiting
- Review the deployment documentation for security best practices
- [Air](https://github.com/cosmtrek/air#installation) for backend live reload
- [Node.js](https://nodejs.org/)
- [pnpm](https://pnpm.io/installation)
## Steps
(Using PowerShell)
1. pull source code
```powershell
git clone https://github.com/usememos/memos
# or
gh repo clone usememos/memos
```
2. cd into the project root directory
```powershell
cd memos
```
3. start backend using air (with live reload)
```powershell
air -c .\scripts\.air-windows.toml
```
4. start frontend dev server
```powershell
cd web; pnpm i; pnpm dev
```
Memos should now be running at [http://localhost:3001](http://localhost:3001) and changing either frontend or backend code would trigger live reload.
## Building
Frontend must be built before backend. The built frontend must be placed in the backend ./server/frontend/dist directory. Otherwise, you will get a "No frontend embedded" error.
SMTP email sending functionality for self-hosted Memos instances.
## Overview
This plugin provides a simple, reliable email sending interface following industry-standard SMTP protocols. It's designed for self-hosted environments where instance administrators configure their own email service, similar to platforms like GitHub, GitLab, and Discourse.
## Features
- Standard SMTP protocol support
- TLS/STARTTLS and SSL/TLS encryption
- HTML and plain text emails
- Multiple recipients (To, Cc, Bcc)
- Synchronous and asynchronous sending
- Detailed error reporting with context
- Works with all major email providers
- Reply-To header support
- RFC 5322 compliant message formatting
## Quick Start
### 1. Configure SMTP Settings
```go
import"github.com/usememos/memos/plugin/email"
config:=&email.Config{
SMTPHost:"smtp.gmail.com",
SMTPPort:587,
SMTPUsername:"your-email@gmail.com",
SMTPPassword:"your-app-password",
FromEmail:"noreply@yourdomain.com",
FromName:"Memos",
UseTLS:true,
}
```
### 2. Create and Send Email
```go
message:=&email.Message{
To:[]string{"user@example.com"},
Subject:"Welcome to Memos!",
Body:"Thanks for signing up.",
IsHTML:false,
}
// Synchronous send (waits for result)
err:=email.Send(config,message)
iferr!=nil{
log.Printf("Failed to send email: %v",err)
}
// Asynchronous send (returns immediately)
email.SendAsync(config,message)
```
## Provider Configuration
### Gmail
Requires an [App Password](https://support.google.com/accounts/answer/185833) (2FA must be enabled):
```go
config:=&email.Config{
SMTPHost:"smtp.gmail.com",
SMTPPort:587,
SMTPUsername:"your-email@gmail.com",
SMTPPassword:"your-16-char-app-password",
FromEmail:"your-email@gmail.com",
FromName:"Memos",
UseTLS:true,
}
```
**Alternative (SSL):**
```go
config:=&email.Config{
SMTPHost:"smtp.gmail.com",
SMTPPort:465,
SMTPUsername:"your-email@gmail.com",
SMTPPassword:"your-16-char-app-password",
FromEmail:"your-email@gmail.com",
FromName:"Memos",
UseSSL:true,
}
```
### SendGrid
```go
config:=&email.Config{
SMTPHost:"smtp.sendgrid.net",
SMTPPort:587,
SMTPUsername:"apikey",
SMTPPassword:"your-sendgrid-api-key",
FromEmail:"noreply@yourdomain.com",
FromName:"Memos",
UseTLS:true,
}
```
### AWS SES
```go
config:=&email.Config{
SMTPHost:"email-smtp.us-east-1.amazonaws.com",
SMTPPort:587,
SMTPUsername:"your-smtp-username",
SMTPPassword:"your-smtp-password",
FromEmail:"verified@yourdomain.com",
FromName:"Memos",
UseTLS:true,
}
```
**Note:** Replace `us-east-1` with your AWS region. Email must be verified in SES.
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.