Compare commits

...

152 Commits

Author SHA1 Message Date
Johnny 2c2ef53737 chore: fix frontend linter 2025-12-17 09:04:05 +08:00
Johnny ea14280cb3 feat: enhance attachment handling with MIME type validation 2025-12-17 08:58:43 +08:00
Johnny 642271a831 feat: add iframe support for embedded videos in markdown content 2025-12-17 08:52:59 +08:00
Johnny 310590b278 chore: fix golang checks 2025-12-16 22:53:36 +08:00
Johnny 40e129b8af refactor(auth): streamline session authentication and cookie handling 2025-12-16 22:23:59 +08:00
Steven 87b8c2b2d2 refactor(web): rename grpcweb.ts to connect.ts and enable binary format
- 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>
2025-12-16 20:15:16 +08:00
Steven 6926764b91 fix: allow unauthenticated CreateUser for first user registration
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>
2025-12-15 23:18:30 +08:00
Steven b1a52f20ed fix(web): add clipboard fallback for CodeBlock copy button in non-secure contexts
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>
2025-12-15 22:51:34 +08:00
Steven a2ddf05933 chore: fix linter 2025-12-15 19:58:58 +08:00
Steven 1b3318f886 refactor(web): improve ActivityCalendar maintainability and add Calendar page
- 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>
2025-12-15 19:47:46 +08:00
Johnny d14e66daf5
fix: openapi generation (#5349) 2025-12-15 10:55:04 +08:00
Davide Truffa 8659f69da0
chore(locales): update Italian translation (#5346) 2025-12-15 10:05:32 +08:00
Johnny 09afa579e4 chore: implement session sliding expiration and JWT authentication
- 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.
2025-12-15 10:04:11 +08:00
Steven 65a19df4be fix(backend): correct generic type parameter in withHeaderCarrier helper
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>
2025-12-12 08:06:13 +08:00
Steven 06a0f8d618 fix(web): convert visibility enum values to string names in filter expressions
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>
2025-12-12 08:03:25 +08:00
Steven 3d893a7394 fix(backend): implement protocol-agnostic header setting for dual gRPC/Connect-RPC support
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>
2025-12-12 07:46:28 +08:00
Steven 8a7e00886d fix(web): convert enum values to string names in API resource paths
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>
2025-12-11 20:08:11 +08:00
Johnny edd3ced9bf
refactor: migrate to connect-rpc (#5338) 2025-12-11 19:49:07 +08:00
Steven 8af8b9d238 fix(web): use AST parsing for task detection to handle code blocks correctly
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>
2025-12-11 08:26:16 +08:00
Steven 3dc740c752 refactor(web): improve locale/theme preference initialization
- 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.
2025-12-11 07:59:52 +08:00
spaghetti-coder 7479205e21
fix(ui): change focus search bar shortcut overlapping with url shortcut (#5336) 2025-12-11 07:56:20 +08:00
XIN_____ baf33af980
chore: add 'all' translation to zh-Hans and zh-Hant (#5333) 2025-12-11 07:53:20 +08:00
Shivam Kumar 41358ed3ba
fix(theme): improve text contrast in default dark mode (#5323)
Signed-off-by: boojack <stevenlgtm@gmail.com>
Co-authored-by: boojack <stevenlgtm@gmail.com>
2025-12-11 07:53:02 +08:00
xiaolinny 6beb3fcde0
chore: fix some typos in comments (#5332)
Signed-off-by: xiaolinny <xiaolincode@outlook.com>
2025-12-11 07:50:16 +08:00
Steven 48ce4ccc26 fix(web): disable setext header syntax (#5314)
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>
2025-12-09 09:07:54 +08:00
Steven 1cf047707b refactor: migrate binary file serving from gRPC to dedicated HTTP fileserver
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>
2025-12-09 08:53:52 +08:00
spaghetti-coder 9ea27ee61f
fix(ui): fix todo command does nothing (#5329) 2025-12-09 08:20:57 +08:00
spaghetti-coder 618db89f4f
fix(ui): remove unsupported highlight command (#5328) 2025-12-09 07:57:28 +08:00
XIN_____ 2a876436e0
chore: add 'auto-expand' translation in zh-Hans.json (#5326) 2025-12-08 21:03:57 +08:00
Steven 4668c4714b refactor(web): improve MemoContent security and maintainability
Security improvements:
- Add rehype-sanitize for XSS protection in markdown content
- Remove DOMPurify and deprecated __html code block feature
- Extract sanitize schema to constants with comprehensive documentation

Maintainability improvements:
- Extract SANITIZE_SCHEMA to constants.ts for better organization
- Create utils.ts with shared code extraction utilities
- Refactor CodeBlock and MermaidBlock to use shared utilities
- Rename PreProps to CodeBlockProps for clarity
- Reduce code duplication across components

Dependency cleanup:
- Remove explicit katex dependency (now transitive via rehype-katex)
- Remove @matejmazur/react-katex (unused)
- Remove dompurify (replaced by rehype-sanitize)
- Update vite config to remove katex-vendor chunk

Changes: 7 files changed, 84 insertions(+), 100 deletions(-)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 22:45:22 +08:00
Steven d9f8bc80f0 feat(web): add LaTeX math rendering support to MemoContent
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>
2025-12-02 22:08:45 +08:00
Steven 81da20c905 refactor: simplify theme/locale to user preferences and improve initialization
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>
2025-12-02 09:08:46 +08:00
Steven 8154a411a9 fix(web): allow only one active tag filter at a time
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>
2025-12-02 08:40:43 +08:00
Johnny a863154224 docs: update Memos Codebase Guide for clarity and structure 2025-12-02 08:19:20 +08:00
Steven 0610257562 refactor(store): remove deprecated migration_history table and backward compatibility code
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>
2025-12-01 22:54:30 +08:00
Steven fae5eac31b fix(web): fix infinite loop in MemoEditor and improve React/MobX integration
- 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>
2025-12-01 08:54:40 +08:00
Steven d1492007ab fix(store): filter inbox notifications by message type at database level
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>
2025-12-01 08:43:05 +08:00
Steven 17e116b977 chore: fix linter 2025-12-01 08:27:23 +08:00
Johnny 545323d12c refactor(rss): comprehensive RSS service improvements
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>
2025-12-01 00:28:23 +08:00
Johnny 1a9bd32cf1 feat(auth): add PKCE support and enhance OAuth security
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>
2025-12-01 00:04:26 +08:00
Johnny a6a8997f4c chore: tweak comments 2025-11-30 13:16:02 +08:00
Johnny 07072b75a7 chore: reorganize reaction components 2025-11-30 12:48:21 +08:00
Johnny 6dcf7cc74c chore: enhance MemoView component structure 2025-11-30 12:41:24 +08:00
Johnny 7aa8262ef2 chore: streamline MemoEditor components and remove unused code 2025-11-30 12:30:00 +08:00
Huang Youchuan 26cb357685
fix: update user session retrieval to use user.ID instead of userID (#5294)
Co-authored-by: huangyouchuan <huangyouchuan@letu.com>
2025-11-30 11:45:16 +08:00
Johnny ee9d9603ee chore: remove goreleaser 2025-11-30 11:42:46 +08:00
Johnny 2516cdf2b4 refactor: clean up MemoView and MemoEditor component architecture
This commit refactors MemoView and MemoEditor components for better
maintainability, introducing React Context, custom hooks, and improved
folder structure.

MemoView improvements:
- Introduce MemoViewContext to eliminate prop drilling
- Reduce MemoHeader props from 18 to 8
- Reduce MemoBody props from 9 to 4
- Extract custom hooks: useMemoViewDerivedState, useMemoEditor,
  useMemoHandlers for better separation of concerns
- Fix React hooks ordering bug in edit mode

MemoEditor improvements:
- Extract state management into useMemoEditorState hook
- Extract keyboard handling into useMemoEditorKeyboard hook
- Extract event handlers into useMemoEditorHandlers hook
- Extract initialization logic into useMemoEditorInit hook
- Reduce main component from 461 to 317 lines (31% reduction)

Folder structure cleanup:
- Move SortableItem to memo-metadata (correct location)
- Move ErrorBoundary to components folder
- Flatten Toolbar/InsertMenu structure (remove unnecessary nesting)
- Consolidate hooks in main hooks folder
- Consolidate types in main types folder

Benefits:
- Better separation of concerns
- Improved testability
- Easier maintenance
- Cleaner code organization
- No functionality changes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-30 11:15:20 +08:00
Johnny bb7e0cdb79 refactor: remove enable link preview setting
- 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>
2025-11-30 10:34:22 +08:00
Johnny 5fb6f8eccf refactor: remove legacy disable markdown shortcuts setting
- 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>
2025-11-30 10:30:55 +08:00
Johnny 2b7b70ebfe chore: fix linter 2025-11-30 10:15:20 +08:00
Johnny 1ef11f7470 refactor: implement MemoView component with subcomponents and hooks
- 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.
2025-11-29 23:21:35 +08:00
Johnny 50199fe998 feat: add LocationDialog and related hooks for location management in MemoEditor
- 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.
2025-11-28 09:21:53 +08:00
Chriss c1765fc246
feat: add midnight theme (#5288) 2025-11-27 21:21:57 +08:00
Steven 07a030ddfd fix(postgres): update tag filtering SQL to ensure proper type casting for LIKE comparisons 2025-11-26 23:04:07 +08:00
Steven 50f49fc00d chore: update demo data 2025-11-26 21:58:56 +08:00
Steven 05f7c9606b fix: add HTML sanitization and dynamic theme loading 2025-11-26 20:34:52 +08:00
Steven 363bc9f455 fix(web): add missing tag styles and unify primary color across themes
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>
2025-11-26 20:25:28 +08:00
Steven 5a16f8009f fix(markdown): render single newlines as line breaks
Add remark-breaks plugin to render single newlines as <br> tags,
matching GitHub Flavored Markdown behavior.

Fixes https://github.com/usememos/memos/issues/5277

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 20:13:57 +08:00
김강현 ef033d21de
chore(i18n): add missing Korean translations (#5285) 2025-11-26 20:02:57 +08:00
김강현 b7c9f0e6ff
chore(i18n): improve Korean locale translations for copy and delete actions (#5280) 2025-11-26 12:34:54 +08:00
Steven 424f11f227 fix(store): fix PostgreSQL tag filtering type inference error
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>
2025-11-26 07:40:39 +08:00
Steven 68c17469a3 fix(markdown): fix UTF-8 truncation for CJK characters in snippet generation
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>
2025-11-26 07:34:28 +08:00
Steven e17cd163c6 chore: bump version 2025-11-25 22:17:45 +08:00
Steven eb541d04cc fix(web): resolve tag sidebar filtering and reactivity issues
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>
2025-11-25 22:17:01 +08:00
Steven 8ec4c9ab63 fix(web): update time locale when language is changed
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>
2025-11-25 21:37:04 +08:00
Steven ef6456a4f5 refactor(web): restructure MemoEditor with custom hooks and improved error handling
Extract reusable logic into custom hooks (useLocalFileManager, useDragAndDrop, useDebounce, useAbortController), add ErrorBoundary for resilience, and centralize constants. Fix cursor positioning bugs, useEffect dependency issues, and add geocoding request cancellation. Improve performance with debounced localStorage writes and ref-based flags.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 21:29:54 +08:00
Johnny 1832b59190 fix(css): standardize color variable values in default-dark theme 2025-11-25 09:02:57 +08:00
Steven 5e95a77f50 chore: update color variables in default-dark theme for improved contrast 2025-11-24 21:59:24 +08:00
Steven b5de847107 chore(web): add external link icon for memo attachments 2025-11-24 21:56:14 +08:00
Steven 424e599980 refactor(web): optimize memo statistics fetching by using cached data from memo store 2025-11-24 21:37:12 +08:00
Steven 72f93c5379 feat(web): enhance file upload handling with local file support and preview 2025-11-24 21:31:18 +08:00
Steven 1d582e0f39 chore: upgrade dependencies 2025-11-24 20:18:52 +08:00
Richard Szegh 455eef9fa3
feat(web): add ability to delete unused attachments (#5272) 2025-11-24 11:55:53 +08:00
Johnny 60d977c0bf fix: add period to comment for golangci-lint compliance 2025-11-23 23:47:17 +08:00
Johnny b78d4c2568 refactor(markdown): use Unicode categories for tag validation
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.
2025-11-23 23:45:10 +08:00
Steven d69435c97c chore(docs): update security policy to prevent public vulnerability disclosure
- 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
2025-11-21 09:13:40 +08:00
gitkeniwo a533ba02dc
fix: add load more button and pagination to attachments page (#5258) 2025-11-21 09:04:41 +08:00
Steven edfbd6b073 fix(web): refresh sidebar tags when creating/updating memos
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
2025-11-19 22:30:00 +08:00
Steven cabd0d61c6 fix(web): resolve Leaflet DOM cleanup error causing app crashes
- 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
2025-11-19 22:21:01 +08:00
Steven 3989100a27 fix(parser): handle additional Unicode punctuation in tag parsing 2025-11-19 22:19:56 +08:00
Steven 64e9d82d67 fix(parser): support Unicode characters in tags
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
2025-11-19 22:06:11 +08:00
Neo 4de8712cb0
fix: keyboard shortcuts (#5250) 2025-11-17 08:55:57 +08:00
Johnny 357118804e feat(web): add Focus Mode UI entry in Insert Menu
Add discoverable UI entry point for Focus Mode via Insert Menu submenu:

UI Changes:
- Add "View" submenu to Insert Menu (+ button dropdown)
- Nested menu with Focus Mode option (ChatGPT-style pattern)
- Display keyboard shortcut hint (⌘⇧F) next to menu item
- Uses DropdownMenuSub components from Radix UI

User Access Methods:
1. Keyboard: Cmd/Ctrl+Shift+F (primary, power users)
2. Mouse: + menu → View → Focus Mode (discoverable)
3. Mobile: Touch-friendly menu access

Benefits:
- Improves discoverability for new users
- Doesn't clutter main editor UI
- Educates users about keyboard shortcut
- Extensible for future view options (typewriter, reading mode, etc.)
- Follows familiar UI patterns (ChatGPT, Notion)

Files Modified:
- web/src/components/MemoEditor/ActionButton/InsertMenu.tsx
  * Add DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent
  * Add View submenu with Focus Mode entry
  * Add onToggleFocusMode prop
- web/src/components/MemoEditor/index.tsx
  * Pass toggleFocusMode to InsertMenu component
- web/src/locales/en.json
  * Add "editor.view" translation key
2025-11-17 08:55:10 +08:00
Johnny c8162ff3cc feat(web): add Focus Mode for distraction-free writing
Add keyboard-activated Focus Mode to provide an immersive writing experience:

Features:
- Toggle with Cmd/Ctrl+Shift+F (matches GitHub, Google Docs)
- Exit with Escape, toggle shortcut, button click, or backdrop click
- Expands editor to ~80-90% of viewport with centered layout
- Semi-transparent backdrop with blur effect
- Maintains all editor functionality (attachments, shortcuts, etc.)
- Smooth 300ms transitions

Responsive Design:
- Mobile (< 640px): 8px margins, 50vh min-height
- Tablet (640-768px): 16px margins
- Desktop (> 768px): 32px margins, 60vh min-height, 1024px max-width

Implementation:
- Centralized constants for easy maintenance (FOCUS_MODE_STYLES)
- Extracted keyboard shortcuts and heights to named constants
- JSDoc documentation for all new functions and interfaces
- TypeScript type safety with 'as const'
- Explicit positioning (top/left/right/bottom) to avoid width overflow

Files Modified:
- web/src/components/MemoEditor/index.tsx - Main Focus Mode logic
- web/src/components/MemoEditor/Editor/index.tsx - Height adjustments
- web/src/locales/en.json - Translation keys

Design follows industry standards (GitHub Focus Mode, Notion, Obsidian)
and maintains code quality with single source of truth pattern.
2025-11-16 23:15:36 +08:00
Steven 156908c77f chore(web): migrate from ESLint+Prettier to Biome
- Install @biomejs/biome@2.3.5 as unified linter and formatter
- Remove ESLint, Prettier and all related plugins (221 packages removed)
- Migrate linting rules from ESLint to Biome configuration
- Migrate formatting rules from Prettier to Biome configuration
- Exclude auto-generated proto files from linting (src/types/proto/**)
- Exclude CSS files from Biome (Tailwind syntax not yet supported)
- Update package.json scripts:
  - lint: tsc + biome check
  - lint:fix: biome check --write
  - format: biome format --write
- Auto-fix import organization across 60+ files
- Fix duplicate key in Russian locale (ru.json)
- Update CLAUDE.md documentation to reflect Biome usage

Benefits:
- 10-100x faster linting performance
- Simplified toolchain with single configuration file
- 221 fewer npm dependencies
- Unified linting, formatting, and import organization
2025-11-14 23:58:07 +08:00
Johnny 64111369d3
fix(web): refactor task list styles to follow GitHub standard (#5253)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-14 23:37:00 +08:00
Steven d1b2c0308b chore: add demo user access token for testing purposes 2025-11-11 08:31:05 +08:00
Steven dc398cf6a7 chore(web): unify metadata badge styling and fix event handling
- Remove MetadataBadge component and inline styles consistently
- Add pointer/mouse event handlers to prevent drag interference
- Fix LocationDisplay mode handling and popover interaction
- Clean up RelationList empty state logic
2025-11-10 21:53:56 +08:00
boojack 659c63165b
chore: add Vercel OSS Program badge
Added Vercel OSS Program badge to README.

Signed-off-by: boojack <stevenlgtm@gmail.com>
2025-11-10 20:18:41 +08:00
Johnny 9a100d55be
chore(server): remove profiler code and endpoints (#5244)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-10 20:17:21 +08:00
Simon bb3d808e0e
feat: allow closing image preview via backdrop click (#5243) 2025-11-10 19:12:45 +08:00
Johnny 6fc0ef7cc2 chore(web): remove redundant and minimal README files
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>
2025-11-09 14:21:10 +08:00
Johnny fa3e0fc7f9 fix(web): make MermaidBlock reactive to system theme changes and improve code quality
- 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>
2025-11-09 14:19:11 +08:00
Johnny fb736c20d3
feat(web): add Mermaid diagram support in markdown (#5242)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-09 14:13:10 +08:00
Johnny 5925e3cfc1 chore(web): simplify command system and improve editor UX (#5242)
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>
2025-11-09 13:09:50 +08:00
Johnny fc43f86571
chore(web): unify Location/Attachments/Relations components (#5241)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-09 07:23:31 +08:00
Johnny a2ccf6b201
feat(web): improve list auto-completion and tag insertion UX (#5240)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-09 01:26:29 +08:00
Johnny e8b0273473
chore(web): improve CommandSuggestions and TagSuggestions components (#5239)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-09 01:05:24 +08:00
Johnny bd21338fdb
fix(web): markdown list auto-completion creates new line correctly (#5238)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-08 10:47:10 +08:00
Johnny c54fcf7aa7
refactor(web): redesign Settings components (#5237)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-08 10:32:55 +08:00
Steven 805bb4e741 chore(github): streamline issue templates with validation and better structure
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>
2025-11-08 10:28:51 +08:00
boojack 906412013f
refactor(api): remove test_auth.go and inline test helpers (#5235)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-08 09:53:29 +08:00
boojack 13fea64d15
fix(api): implement custom memo ID support in CreateMemo (#5234)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-08 09:43:10 +08:00
Steven ef9eee19d6 fix: implement tag suggestions functionality
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>
2025-11-08 09:15:38 +08:00
boojack 7d4d1e8517
feat(web): standardize theme system with auto sync option (#5231)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-08 00:41:21 +08:00
Steven 8f29db2f49 fix(server): prevent memory exhaustion in thumbnail generation
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>
2025-11-07 09:04:21 +08:00
Steven b7215f46a6 refactor(web): use Radix Checkbox and remove memoTypeStats
- 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>
2025-11-07 08:36:15 +08:00
Steven 8f136ffa75 fix(api): correct user registration logic and first-user detection
- 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>
2025-11-06 23:41:47 +08:00
boojack 21d31e3609
fix(security): implement security review recommendations (#5228)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-06 23:32:27 +08:00
Elliott bb8fa90496
feat(i18n): add missing translations for zh-Hans (closes #5209) (#5229) 2025-11-06 22:32:03 +08:00
Steven 32d47abef2 fix(api): use correct instance setting method in user registration
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>
2025-11-06 20:00:35 +08:00
Florian Dewald 769dcd0cf9
fix(security): add missing authorization checks to various services (#5217) 2025-11-06 19:42:44 +08:00
boojack df93120f60
chore: add Claude Code GitHub workflow (#5227) 2025-11-06 19:40:01 +08:00
boojack 6cff7972d5
fix(web): fix tag syntax rendering on first line (#5226)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-06 19:29:16 +08:00
Steven 8f0658e90d feat(web): enhance inbox notifications and user profile layouts
- 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>
2025-11-06 08:41:30 +08:00
Steven 71d0dbaf41 chore: fix linter 2025-11-05 23:59:24 +08:00
Steven 89b0b81bdc fix(web): add required headers for Nominatim reverse geocoding API
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>
2025-11-05 23:47:44 +08:00
Steven 4c1d1c70d1 refactor: rename workspace to instance throughout codebase
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>
2025-11-05 23:35:35 +08:00
boojack d98ee36178
chore: standardize and improve API structure (#5224)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-05 22:36:51 +08:00
Florian Dewald 5f57f48673
fix(security): validate attachment filenames (#5218) 2025-11-05 08:48:55 +08:00
Steven 1d7efb1580 refactor(web): unify memo stats/filters with context-aware MainLayout
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>
2025-11-05 08:46:52 +08:00
Steven d30ff2898f fix(web): correct task checkbox toggling in multi-section memos
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>
2025-11-04 09:25:15 +08:00
Claude 9b72963e08 feat: replace HomeSidebar with MemoExplorer and MemoExplorerDrawer components 2025-11-02 23:31:29 +08:00
Claude dc9470f71c feat: implement OAuth state management with CSRF protection and cleanup functionality 2025-11-02 23:23:44 +08:00
Claude fb01b49ecf feat: add VisuallyHidden component and enhance LocationDialog accessibility 2025-11-02 23:02:08 +08:00
Claude 638b22a20d chore: implement InsertMenu with file upload and memo linking functionality 2025-11-02 22:53:07 +08:00
Steven 93964827ad refactor: redesign memo editor action bar UI
- 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
2025-10-31 21:29:58 +08:00
Steven 1ced0bcdbd chore: fix linter 2025-10-31 08:36:12 +08:00
Steven bc1550e926 refactor(api): migrate inbox functionality to user notifications
- 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>
2025-10-31 08:33:09 +08:00
Steven e915e3a46b refactor(web): refactor MemoFilters component and add comprehensive filter support
- 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
2025-10-30 22:52:58 +08:00
Steven 1e954070b9 refactor(web): improve memo component skeleton and loading states 2025-10-30 22:25:19 +08:00
Steven 5e47f25bf5 feat(store): add hierarchical tag filtering support
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>
2025-10-30 00:21:53 +08:00
boojack a2bfba6928
chore: update readme
Added LambdaTest to sponsorship section

Signed-off-by: boojack <stevenlgtm@gmail.com>
2025-10-30 00:16:40 +08:00
Steven f65633e8a9 chore: fix linter issues in backend and frontend
- 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>
2025-10-29 23:41:20 +08:00
Steven 243ecf14b0 refactor(api): remove DeleteMemoTag and RenameMemoTag endpoints
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>
2025-10-29 23:32:47 +08:00
Steven d794c0bf8b feat(web): improve loading performance with skeleton screens and parallel fetches
- Add MemoSkeleton component for smooth initial page load experience
- Integrate skeleton loader into PagedMemoList during initial fetch
- Parallelize user settings and shortcuts API calls (~50% faster session init)
- Batch-fetch memo creators in parallel to eliminate individual loading spinners
- Pass showCreator prop to Explore page for proper skeleton rendering

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 23:53:35 +08:00
Steven 2371bbb1b7 feat(web): add quick language and theme switchers to user menu
- 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>
2025-10-28 22:32:25 +08:00
Steven d693142dd4 feat(web): enhance code blocks with copy button and fix link navigation
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>
2025-10-28 22:06:07 +08:00
Steven b00df8a9d1 fix(web): remove error notifications for location geocoding failures
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>
2025-10-28 21:28:29 +08:00
Steven 5f7c758f38 fix(web): make layout and direction settings reactive in UI
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>
2025-10-28 07:58:15 +08:00
Steven 91a7e927a5 fix(server): reduce static asset cache to prevent stale files after redeploy
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>
2025-10-28 07:55:02 +08:00
Steven 2197178dd7 chore: update README for clarity and consistency 2025-10-28 07:46:45 +08:00
boojack d516f9aab2
chore: tweak readme
Signed-off-by: boojack <stevenlgtm@gmail.com>
2025-10-27 23:41:24 +08:00
Steven f635d85bcf chore: fix reactions seed data
- 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>
2025-10-27 20:27:27 +08:00
Claude f241b590a2 chore: update demo data 2025-10-27 09:20:26 +08:00
Claude 596b894ca0 chore: remove unused syntax
- 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.
2025-10-27 08:31:57 +08:00
Claude 7eec424274 chore: remove references handling from markdown extraction 2025-10-27 00:12:24 +08:00
Claude 6cb96ef65e chore: add missing punctuation in comments 2025-10-26 11:58:34 +08:00
Claude 739fd2cde6 refactor: update markdown parser
- 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`.
2025-10-26 11:28:40 +08:00
Steven bfad0708e2 fix(web): make memoFilterStore reactive by marking fields as observable
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>
2025-10-25 07:01:27 +08:00
Steven e0b1153269 fix(web): resolve MobX observable reactivity issue in filter computation
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>
2025-10-25 06:59:13 +08:00
Steven 46ce0bc62e fix(store): correct PostgreSQL placeholder generation in IN clauses
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>
2025-10-25 06:47:06 +08:00
521 changed files with 33318 additions and 54430 deletions

View File

@ -5,35 +5,78 @@ body:
- type: markdown - type: markdown
attributes: attributes:
value: | value: |
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. Thank you for taking the time to report a bug! Please complete the form below to help us understand and fix the issue.
- type: textarea
- type: checkboxes
id: pre-check
attributes: attributes:
label: Describe the bug label: Pre-submission Checklist
description: | description: Please confirm you have completed the following steps before submitting
Briefly describe the problem you are having in a few paragraphs. 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: validations:
required: true required: true
- type: textarea
- type: input
id: version
attributes: attributes:
label: Steps to reproduce label: Memos Version
description: | description: Provide the exact version (e.g., `v0.25.2`). Find this in Settings → About or via `--version` flag
Provide the steps to reproduce the issue. 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: | placeholder: |
1. Go to '...' 1. Go to '...'
2. Click on '....' 2. Click on '...'
3. See error 3. Scroll down to '...'
validations: 4. See error
required: true
- type: input
attributes:
label: |
The version of Memos you're using
description: |
Provide the version of Memos you're using. Please use the following format: `v0.22.0` instead of `stable` or `latest`.
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: expected-behavior
attributes: attributes:
label: Screenshots or additional context label: Expected Behavior
description: | description: What did you expect to happen?
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. 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
placeholder: |
- Browser: Chrome 120
- OS: macOS 14
- Deployment: Docker
- Database: SQLite

View File

@ -5,32 +5,72 @@ body:
- type: markdown - type: markdown
attributes: attributes:
value: | value: |
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. Thank you for suggesting a new feature! Please complete the form below to help us understand your idea.
- type: textarea
- type: checkboxes
id: pre-check
attributes: attributes:
label: Describe the solution you'd like label: Pre-submission Checklist
description: | description: Please confirm you have completed the following steps before submitting
A clear and concise description of what you want to happen. options:
placeholder: | - label: I have searched the existing issues and this feature has not been requested yet
It would be great if [...] required: true
validations:
required: true
- type: dropdown - type: dropdown
id: feature-type
attributes: attributes:
label: Type of feature label: Type of Feature
description: What type of feature is this? description: What type of feature is this?
options: options:
- User Interface (UI) - User Interface (UI)
- User Experience (UX) - User Experience (UX)
- API - API / Backend
- Documentation - Documentation
- Integrations - Integrations / Plugins
- Security / Privacy
- Performance
- Other - Other
default: 0 default: 0
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: problem-statement
attributes: attributes:
label: Additional context label: Problem or Use Case
description: | description: What problem does this feature solve? What are you trying to accomplish?
What are you trying to do? Why is this important to you? 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
placeholder: |
- Similar feature in other apps: ...
- Mockups or screenshots: ...
- Related discussions: ...

View File

@ -1,45 +0,0 @@
name: Build Artifacts
on:
push:
tags:
- "*"
permissions:
contents: write
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: actions/setup-go@v6
with:
go-version: 1.25
check-latest: true
cache: true
- uses: pnpm/action-setup@v4.1.0
with:
version: 9
- uses: actions/setup-node@v5
with:
node-version: "20"
cache: pnpm
cache-dependency-path: "web/pnpm-lock.yaml"
- run: pnpm install
working-directory: web
- run: pnpm release
working-directory: web
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
with:
# either 'goreleaser' (default) or 'goreleaser-pro'
distribution: goreleaser
# 'latest', 'nightly', or a semver
version: latest
args: release --clean --skip=validate
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,36 +0,0 @@
version: 1
before:
hooks:
# You may remove this if you don't use go modules.
- go mod tidy
builds:
- main: ./cmd/memos
binary: memos
goos:
- linux
- darwin
archives:
- format: tar.gz
# this name template makes the OS and Arch compatible with the results of `uname`.
name_template: >-
{{ .ProjectName }}_{{ .Tag }}_{{ .Os }}_{{ .Arch }}
changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
checksum:
disable: true
release:
draft: true
replace_existing_draft: true
make_latest: true
mode: replace
skip_upload: false

376
CLAUDE.md
View File

@ -1,343 +1,83 @@
# CLAUDE.md # Memos Codebase Guide
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview ## Project Overview
Memos is a lightweight, self-hosted knowledge management and note-taking platform. The architecture pairs a Go backend with a React+Vite frontend, using gRPC for internal communication and providing REST API access via gRPC-Gateway. Memos is a self-hosted knowledge management platform with a Go backend and React frontend. The architecture uses gRPC for internal communication with REST access via gRPC-Gateway.
## Development Commands ## Architecture Decision Context
### Backend (Go) **Why gRPC + gRPC-Gateway?**
- Native gRPC for performance, REST API for compatibility
- Both protocols served on same port via `cmux` connection multiplexer
- Frontend uses gRPC-Web (`nice-grpc-web`) for type-safe API calls
**Start Development Server:** **Why multi-database support?**
- Store interface (`store/driver.go`) abstracts persistence
- Three implementations: SQLite (default), MySQL, PostgreSQL
- Each driver has its own migration files in `store/db/{driver}/migration/`
- Schema version tracked in `instance_setting` table (key: `bb.general.version`)
**Why MobX for frontend state?**
- Simpler than Redux for this application's needs
- Stores in `web/src/store/` handle global state (user, memos, editor, dialogs)
## Critical Development Commands
**Backend:**
```bash ```bash
go run ./cmd/memos --mode dev --port 8081 go run ./cmd/memos --mode dev --port 8081 # Start dev server
go test ./... # Run tests
golangci-lint run # Lint
``` ```
**Build Binary:**
```bash
go build ./cmd/memos
```
**Run Tests:**
```bash
go test ./... # All tests
go test ./store/... # Store layer tests only
go test ./server/router/api/v1/test/... # API tests
```
**Lint:**
```bash
golangci-lint run # Full lint check (uses .golangci.yaml)
```
**Generate Protocol Buffers:**
```bash
cd proto
buf generate # Generate Go/TypeScript from .proto files
buf format -w # Format proto files
```
### Frontend (React + Vite)
**Install Dependencies:**
```bash
cd web
pnpm install
```
**Development Server:**
```bash
cd web
pnpm dev # Hot-reload dev server (typically :5173)
```
**Build:**
```bash
cd web
pnpm build # Build to web/dist/
pnpm release # Build and copy to server/router/frontend/dist/
```
**Lint:**
```bash
cd web
pnpm lint # TypeScript check + ESLint
```
### CLI Flags and Environment Variables
The backend accepts the following configuration via flags or `MEMOS_*` environment variables:
- `--mode` / `MEMOS_MODE` - Runtime mode: `dev`, `prod`, `demo` (default: `prod`)
- `--addr` / `MEMOS_ADDR` - Bind address (default: `0.0.0.0`)
- `--port` / `MEMOS_PORT` - HTTP/gRPC port (default: `5230`)
- `--unix-sock` / `MEMOS_UNIX_SOCK` - Unix socket path (optional)
- `--data` / `MEMOS_DATA` - Data directory for SQLite (default: `~/.memos`)
- `--driver` / `MEMOS_DRIVER` - Database driver: `sqlite`, `mysql`, `postgres` (default: `sqlite`)
- `--dsn` / `MEMOS_DSN` - Database connection string (for MySQL/PostgreSQL)
- `--instance-url` / `MEMOS_INSTANCE_URL` - Public instance URL for webhooks/OAuth
## Architecture
### High-Level Structure
```
cmd/memos/ # CLI entry point, starts HTTP+gRPC server
server/ # HTTP server and routing
├── router/api/v1/ # gRPC services + gRPC-Gateway REST handlers
├── router/frontend/ # Embedded SPA static file serving
└── router/rss/ # RSS feed generation
internal/ # Shared domain logic and utilities
├── base/ # Base types and constants
├── profile/ # Configuration profile handling
├── util/ # Utility functions
└── version/ # Version information
store/ # Data persistence layer (repository pattern)
├── db/ # Database driver implementations
│ ├── sqlite/ # SQLite driver + migrations
│ ├── mysql/ # MySQL driver + migrations
│ └── postgres/ # PostgreSQL driver + migrations
└── cache/ # In-memory caching
proto/ # Protocol Buffer definitions
└── api/v1/ # API service contracts (.proto files)
web/ # Frontend React application
├── src/
│ ├── components/ # React components
│ ├── pages/ # Page-level components
│ ├── store/ # MobX state management
│ ├── router/ # React Router configuration
│ ├── locales/ # i18n translation files
│ ├── hooks/ # Custom React hooks
│ └── utils/ # Utility functions
└── public/ # Static assets
```
### API Architecture
**Dual Protocol Serving:**
The server uses `cmux` (connection multiplexer) to serve both gRPC and HTTP on the same port:
- **HTTP/2 + `application/grpc`** → Native gRPC server
- **HTTP/1.1** → Echo server (REST API via gRPC-Gateway, static files, RSS)
**API Services** (defined in `proto/api/v1/*.proto`):
- `WorkspaceService` - Workspace settings and profiles
- `AuthService` - Authentication and session management
- `UserService` - User management
- `MemoService` - Core memo CRUD operations
- `AttachmentService` - File uploads and storage
- `InboxService` - Inbox items
- `ActivityService` - Activity logging
- `MarkdownService` - Markdown utilities (link metadata, etc.)
- `IdentityProviderService` - OAuth/SSO providers
- `ShortcutService` - User shortcuts
**API Access Methods:**
1. **Native gRPC** - Direct gRPC calls to `memos.api.v1.*` services
2. **REST API** - HTTP REST at `/api/v1/*` (via gRPC-Gateway)
3. **gRPC-Web** - Browser gRPC calls to `/memos.api.v1.*` (via grpc-web proxy)
### Database Layer
**Store Interface:**
The `store.Driver` interface (`store/driver.go`) defines all data access methods. Three implementations exist:
- `store/db/sqlite/` - SQLite driver (default, embedded database)
- `store/db/mysql/` - MySQL driver
- `store/db/postgres/` - PostgreSQL driver
**Migrations:**
Each driver contains its own migration files in subdirectories. Schema version tracking is stored in `workspace_setting` (key: `bb.general.version`). The `store/migrator.go` orchestrates migrations across all drivers.
**Key Models:**
- `Memo` - Core note/memo entity
- `User` - User accounts
- `Attachment` - Uploaded files and images
- `MemoRelation` - Graph relationships between memos
- `Activity` - Activity log entries
- `Inbox` - Inbox items
- `Reaction` - Emoji reactions
- `WorkspaceSetting` - Workspace-level configuration
- `UserSetting` - User preferences
- `IdentityProvider` - OAuth/SSO provider configs
### Frontend Architecture
**Tech Stack:**
- **Framework:** React 18 with TypeScript
- **Build Tool:** Vite 7
- **Routing:** React Router 7
- **State Management:** MobX
- **Styling:** Tailwind CSS 4 + Emotion
- **UI Components:** Radix UI primitives
- **i18n:** react-i18next with language files in `web/src/locales/`
**State Management:**
MobX stores in `web/src/store/` handle global state:
- `userStore` - Current user session
- `memoStore` - Memo collection and filters
- `editorStore` - Memo editor state
- `dialogStore` - Modal dialogs
- etc.
**gRPC-Web Client:**
The frontend uses `nice-grpc-web` to call backend services. Client setup is in `web/src/grpcweb.ts`.
### Authentication
**Dual Auth Support:**
1. **Session-based (Cookie):** `user_session` cookie with format `{userID}-{sessionID}`
2. **Token-based (JWT):** `Authorization: Bearer <token>` header
**Flow:**
- Authentication interceptor (`server/router/api/v1/acl.go`) runs on all gRPC methods
- Public endpoints bypass auth (see `acl_config.go` for allowlist)
- Context values set: `userIDContextKey`, `sessionIDContextKey`, `accessTokenContextKey`
- Sessions have sliding expiration (14 days from last access)
## Code Style and Conventions
### Go
- **Formatting:** All code must be `gofmt`-compliant (tabs for indentation)
- **Imports:** Group stdlib, external, and local imports (enforced by `goimports`)
- **Error Handling:** Wrap errors with `%w` when propagating: `errors.Wrap(err, "context")`
- **Naming:** Package names lowercase, no underscores
- **Linting:** Enforced via `.golangci.yaml` (revive, staticcheck, gocritic, etc.)
### TypeScript/React
- **Components:** PascalCase filenames (e.g., `MemoEditor.tsx`)
- **Hooks:** camelCase filenames (e.g., `useMemoList.ts`)
- **Formatting:** Prettier enforced (see `web/.prettierrc.js`)
- **Import Ordering:** Managed by `@trivago/prettier-plugin-sort-imports`
- **Styling:** Tailwind utility classes preferred over custom CSS
### Commit Messages
Follow Conventional Commits format:
- `feat(scope): description` - New features
- `fix(scope): description` - Bug fixes
- `chore(scope): description` - Maintenance tasks
- `refactor(scope): description` - Code restructuring
- `test(scope): description` - Test additions/fixes
- `docs(scope): description` - Documentation updates
Scopes: `server`, `api`, `store`, `web`, `proto`, etc.
## Testing
**Go Tests:**
- Test files: `*_test.go` alongside source files
- Run specific package: `go test ./store/cache/...`
- API integration tests: `server/router/api/v1/test/*_test.go`
- Prefer table-driven tests for multiple test cases
**Frontend:** **Frontend:**
Currently relies on linting and manual testing. For UI changes, validate with local dev server. ```bash
cd web && pnpm dev # Start dev server
cd web && pnpm lint:fix # Lint and fix
cd web && pnpm release # Build and copy to backend
```
## Protocol Buffer Workflow **Protocol Buffers:**
```bash
cd proto && buf generate # Regenerate Go + TypeScript from .proto
```
**Prerequisites:** Install [buf](https://docs.buf.build/installation) ## Key Workflows
**Modifying APIs:** **Modifying APIs:**
1. Edit `.proto` files in `proto/api/v1/` 1. Edit `.proto` files in `proto/api/v1/`
2. Run `cd proto && buf generate` to regenerate Go and TypeScript code
3. Update service implementations in `server/router/api/v1/`
4. Update frontend gRPC-Web clients in `web/src/`
**Generated Code Locations:**
- Go: `proto/gen/api/v1/`
- TypeScript: `web/src/types/proto/api/v1/`
## Database Migrations
When adding database schema changes:
1. Create migration file in driver-specific directory:
- SQLite: `store/db/sqlite/migration/`
- MySQL: `store/db/mysql/migration/`
- PostgreSQL: `store/db/postgres/migration/`
2. Follow naming: `prod_YYYYMMDD_description.sql`
3. Update schema version constant in `store/migrator.go`
4. Test migration locally with all three database drivers
## Common Patterns
### Adding a New API Endpoint
1. Define service method in `proto/api/v1/{service}_service.proto`
2. Run `buf generate` to regenerate code 2. Run `buf generate` to regenerate code
3. Implement method in `server/router/api/v1/{service}_service.go` 3. Implement in `server/router/api/v1/`
4. Add to public allowlist in `acl_config.go` if unauthenticated 4. Frontend types auto-update in `web/src/types/proto/`
5. Update frontend client in `web/src/`
### Adding a New Data Model **Database Schema Changes:**
1. Create migration files: `store/migration/{sqlite,mysql,postgres}/{version}/NN__description.sql`
2. Update `LATEST.sql` in each driver directory
3. Schema version auto-determined from migration files
4. If adding new tables/models, also update `store/driver.go` interface and implementations
1. Define struct in `store/{model}.go` **Authentication Flow:**
2. Add CRUD methods to `store.Driver` interface in `store/driver.go` - Interceptor runs on all gRPC methods (`server/router/api/v1/acl.go`)
3. Implement methods in each driver: - Public endpoints listed in `acl_config.go`
- `store/db/sqlite/{model}.go` - Supports both session cookies and JWT bearer tokens
- `store/db/mysql/{model}.go`
- `store/db/postgres/{model}.go`
4. Create migration files for schema changes
5. Add tests in `store/test/{model}_test.go`
### Frontend Data Fetching ## Critical Path Components
Use gRPC-Web client from `web/src/grpcweb.ts`: **Entry point:** `cmd/memos/` starts the server
**API layer:** `server/router/api/v1/` implements gRPC services
**Data layer:** `store/` handles all persistence
**Frontend:** `web/src/` React app with MobX state management
```typescript ## Testing Expectations
import { memoServiceClient } from "@/grpcweb";
const response = await memoServiceClient.listMemos({ Go tests are required for store and API changes. Frontend relies on TypeScript checking and manual validation.
filter: "creator == 'users/1'",
});
```
State is typically managed in MobX stores (`web/src/store/`). Run `go test ./store/...` and `go test ./server/router/api/v1/test/...` before committing backend changes.
## Production Deployment ## Configuration
**Docker (Recommended):** Backend accepts flags or `MEMOS_*` environment variables:
```bash - `--mode` / `MEMOS_MODE`: `dev`, `prod`, `demo`
docker run -d \ - `--port` / `MEMOS_PORT`: HTTP/gRPC port (default: 5230)
--name memos \ - `--data` / `MEMOS_DATA`: Data directory (default: ~/.memos)
-p 5230:5230 \ - `--driver` / `MEMOS_DRIVER`: `sqlite`, `mysql`, `postgres`
-v ~/.memos:/var/opt/memos \
neosmemo/memos:stable
```
**From Source:**
1. Build frontend: `cd web && pnpm install && pnpm release`
2. Build backend: `go build -o memos ./cmd/memos`
3. Run: `./memos --mode prod --port 5230`
**Data Directory:**
For SQLite (default), all data is stored in the directory specified by `--data` flag. This includes:
- `memos_prod.db` - SQLite database
- `assets/` - Uploaded files (unless using S3-compatible storage)
## Key Dependencies
**Backend:**
- `github.com/spf13/cobra` - CLI framework
- `github.com/spf13/viper` - Configuration management
- `google.golang.org/grpc` - gRPC server
- `github.com/grpc-ecosystem/grpc-gateway/v2` - REST gateway
- `github.com/labstack/echo/v4` - HTTP server
- `github.com/soheilhy/cmux` - Connection multiplexing
- `modernc.org/sqlite` - Pure Go SQLite driver
- `github.com/golang-jwt/jwt/v5` - JWT authentication
**Frontend:**
- `react` / `react-dom` - UI framework
- `react-router-dom` - Routing
- `mobx` / `mobx-react-lite` - State management
- `tailwindcss` - Styling
- `nice-grpc-web` - gRPC-Web client
- `@radix-ui/*` - Headless UI components
- `react-i18next` - Internationalization

162
README.md
View File

@ -1,12 +1,8 @@
<div align="center">
<img height="96px" src="https://raw.githubusercontent.com/usememos/.github/refs/heads/main/assets/logo-rounded.png" alt="Memos" />
# Memos # Memos
**A privacy-first, lightweight note-taking service** <img align="right" height="96px" src="https://raw.githubusercontent.com/usememos/.github/refs/heads/main/assets/logo-rounded.png" alt="Memos" />
Write, organize, and own your knowledge An open-source, self-hosted note-taking service. Your thoughts, your data, your control — no tracking, no ads, no subscription fees.
[![Home](https://img.shields.io/badge/🏠-usememos.com-blue?style=flat-square)](https://www.usememos.com) [![Home](https://img.shields.io/badge/🏠-usememos.com-blue?style=flat-square)](https://www.usememos.com)
[![Live Demo](https://img.shields.io/badge/✨-Try%20Demo-orange?style=flat-square)](https://demo.usememos.com/) [![Live Demo](https://img.shields.io/badge/✨-Try%20Demo-orange?style=flat-square)](https://demo.usememos.com/)
@ -14,34 +10,74 @@ Write, organize, and own your knowledge
[![Discord](https://img.shields.io/badge/💬-Discord-5865f2?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/tfPJa4UmAv) [![Discord](https://img.shields.io/badge/💬-Discord-5865f2?style=flat-square&logo=discord&logoColor=white)](https://discord.gg/tfPJa4UmAv)
[![Docker Pulls](https://img.shields.io/docker/pulls/neosmemo/memos?style=flat-square&logo=docker)](https://hub.docker.com/r/neosmemo/memos) [![Docker Pulls](https://img.shields.io/docker/pulls/neosmemo/memos?style=flat-square&logo=docker)](https://hub.docker.com/r/neosmemo/memos)
### 💎 Sponsored By <img src="https://raw.githubusercontent.com/usememos/.github/refs/heads/main/assets/demo.png" alt="Memos Demo Screenshot" height="512" />
<a href="https://go.warp.dev/memos" target="_blank" rel="noopener"> ### 💎 Featured Sponsors
<img src="https://raw.githubusercontent.com/warpdotdev/brand-assets/main/Github/Sponsor/Warp-Github-LG-02.png" alt="Warp - The terminal for the 21st century" height="200" />
</a>
[**Warp** — The AI-powered terminal built for speed and collaboration](https://go.warp.dev/memos) [**Warp** — The AI-powered terminal built for speed and collaboration](https://go.warp.dev/memos)
![screenshot](https://raw.githubusercontent.com/usememos/.github/refs/heads/main/assets/demo.png) <a href="https://go.warp.dev/memos" target="_blank" rel="noopener">
<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>
</div> ---
## 🎯 Why Memos? [**LambdaTest** - Cross-browser testing cloud](https://www.lambdatest.com/?utm_source=memos&utm_medium=sponsor)
<a href="https://www.lambdatest.com/?utm_source=memos&utm_medium=sponsor" target="_blank" rel="noopener">
<img src="https://www.lambdatest.com/blue-logo.png" alt="LambdaTest - Cross-browser testing cloud" height="50" />
</a>
**Your thoughts. Your data. Your control.** ## Overview
Memos is an open-source, self-hosted alternative to cloud note-taking services. No tracking, no ads, no subscription fees — just a clean, fast way to capture and organize your ideas. Memos is a privacy-first, self-hosted knowledge base that works seamlessly for personal notes, team wikis, and knowledge management. Built with Go and React, it offers lightning-fast performance without compromising on features or usability.
- 🔒 **Privacy by design** — All data stays on your server **Why choose Memos over cloud services?**
- ⚡ **Lightning fast** — Built with Go and React for speed
- 📝 **Markdown native** — Write naturally with full markdown support
- 🐳 **Deploy in seconds** — One Docker command to get started
- 🎨 **Beautiful & minimal** — Focus on your thoughts, not the UI
- 🔗 **API-first** — Integrate with your workflow seamlessly
## 🚀 Quick Start | Feature | Memos | Cloud Services |
| ----------------- | ------------------------------ | ----------------------------- |
| **Privacy** | ✅ Self-hosted, zero telemetry | ❌ Your data on their servers |
| **Cost** | ✅ Free forever, MIT license | ❌ Subscription fees |
| **Performance** | ✅ Instant load, no latency | ⚠️ Depends on internet |
| **Ownership** | ✅ Full control & export | ❌ Vendor lock-in |
| **API Access** | ✅ Full REST + gRPC APIs | ⚠️ Limited or paid |
| **Customization** | ✅ Open source, forkable | ❌ Closed ecosystem |
Get up and running in **under 30 seconds**: ## Features
- **🔒 Privacy-First Architecture**
- Self-hosted on your infrastructure with zero telemetry
- Complete data ownership and export capabilities
- No tracking, no ads, no vendor lock-in
- **📝 Markdown Native**
- Full markdown support
- Plain text storage — take your data anywhere
- **⚡ Blazing Fast**
- Built with Go backend and React frontend
- Optimized for performance at any scale
- **🐳 Simple Deployment**
- One-line Docker installation
- Supports SQLite, MySQL, and PostgreSQL
- **🔗 Developer-Friendly**
- Full REST and gRPC APIs
- Easy integration with existing workflows
- **🎨 Beautiful Interface**
- Clean, minimal design and dark mode support
- Mobile-responsive layout
## Quick Start
### Docker (Recommended)
```bash ```bash
docker run -d \ docker run -d \
@ -51,56 +87,26 @@ docker run -d \
neosmemo/memos:stable neosmemo/memos:stable
``` ```
Open `http://localhost:5230` and start writing! 🎉 Open `http://localhost:5230` and start writing!
**Need more options?** Check out our [installation guide](https://www.usememos.com/docs/installation) for Docker Compose, binaries, and building from source. ### Try the Live Demo
## ✨ Features Don't want to install yet? Try our [live demo](https://demo.usememos.com/) first!
- **🔒 Privacy-First Architecture** — Self-hosted on your infrastructure with no telemetry or tracking. Your data stays yours with full ownership and export capabilities. ### Other Installation Methods
- **⚡ Lightning-Fast Performance** — Built with Go and React for exceptional speed. Real-time updates ensure your notes are always in sync across devices. - **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
- **📝 Markdown Native** — Write naturally with full markdown support, code highlighting, and rich text rendering. No lock-in, just plain text you can take anywhere. See our [installation guide](https://www.usememos.com/docs/installation) for detailed instructions.
- **🐳 Deploy in Seconds** — One Docker command to get started. Simple deployment with Docker Compose, binaries, or Kubernetes. ## Contributing
- **🎨 Beautiful & Minimal UI** — Clean, intuitive interface that stays out of your way. Mobile-responsive design with dark mode support. We welcome contributions of all kinds! Whether you're fixing bugs, adding features, improving documentation, or helping with translations — every contribution matters.
- **🔗 Powerful API** — RESTful and gRPC APIs for seamless integration with your existing workflow. Build custom tools and automations easily. **Ways to contribute:**
- **🌍 Open Source & Community-Driven** — MIT licensed with an active community. Regular updates and transparent development on GitHub.
## 💡 Perfect For
- 📓 **Personal journaling** — Daily thoughts and reflections
- 🧠 **Knowledge management** — Build your second brain
- 📋 **Quick notes** — Capture ideas on the go
- 🔗 **Link collections** — Save and organize useful resources
- 👥 **Team wikis** — Collaborative knowledge bases
- 🎓 **Learning logs** — Document your learning journey
## 💖 Support Memos
Love Memos? Help us keep it growing!
<a href="https://github.com/sponsors/usememos" target="_blank">
<img src="https://img.shields.io/badge/❤_Sponsor_on_GitHub-ea4aaa?style=for-the-badge&logo=github-sponsors&logoColor=white" alt="Sponsor on GitHub">
</a>
**Community Sponsors:**
<a href="https://github.com/yourselfhosted" target="_blank"><img src="https://avatars.githubusercontent.com/u/140182318?v=4" alt="yourselfhosted" height="50" style="border-radius: 50%; margin: 5px;" /></a>
<a href="https://github.com/fixermark" target="_blank"><img src="https://avatars.githubusercontent.com/u/169982?v=4" alt="fixermark" height="50" style="border-radius: 50%; margin: 5px;" /></a>
<a href="https://github.com/alik-agaev" target="_blank"><img src="https://avatars.githubusercontent.com/u/2662697?v=4" alt="alik-agaev" height="50" style="border-radius: 50%; margin: 5px;" /></a>
_Every contribution, big or small, makes a difference!_
## 🤝 Contributing
We love contributions! Whether you're fixing bugs, adding features, improving docs, or spreading the word — every contribution matters.
**Get involved:**
- 🐛 [Report bugs](https://github.com/usememos/memos/issues/new?template=bug_report.md) - 🐛 [Report bugs](https://github.com/usememos/memos/issues/new?template=bug_report.md)
- 💡 [Suggest features](https://github.com/usememos/memos/issues/new?template=feature_request.md) - 💡 [Suggest features](https://github.com/usememos/memos/issues/new?template=feature_request.md)
@ -108,22 +114,22 @@ We love contributions! Whether you're fixing bugs, adding features, improving do
- 📖 [Improve documentation](https://github.com/usememos/memos/tree/main/docs) - 📖 [Improve documentation](https://github.com/usememos/memos/tree/main/docs)
- 🌍 [Help with translations](https://github.com/usememos/memos/tree/main/web/src/locales) - 🌍 [Help with translations](https://github.com/usememos/memos/tree/main/web/src/locales)
## ⭐ Star History ## Sponsors
Love Memos? [Sponsor us on GitHub](https://github.com/sponsors/usememos) to help keep the project growing!
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=usememos/memos&type=Date)](https://star-history.com/#usememos/memos&Date) [![Star History Chart](https://api.star-history.com/svg?repos=usememos/memos&type=Date)](https://star-history.com/#usememos/memos&Date)
## License
Memos is open-source software licensed under the [MIT License](LICENSE).
--- ---
<div align="center"> **[Website](https://www.usememos.com)** • **[Documentation](https://www.usememos.com/docs)** • **[Demo](https://demo.usememos.com/)** • **[Discord](https://discord.gg/tfPJa4UmAv)** • **[X/Twitter](https://x.com/usememos)**
**[Website](https://www.usememos.com)** • <a href="https://vercel.com/oss">
**[Docs](https://www.usememos.com/docs)** • <img alt="Vercel OSS Program" src="https://vercel.com/oss/program-badge.svg" />
**[Demo](https://demo.usememos.com/)** • </a>
**[X](https://x.com/usememos)** •
**[Discord](https://discord.gg/tfPJa4UmAv)**
Made with ❤️ by the Memos community
**If you like Memos, give us a ⭐ on GitHub!**
</div>

View File

@ -1,7 +1,46 @@
# Security Policy # Security Policy
## Reporting a bug ## Project Status
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**: usememos@gmail.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
Thank you for helping improve Memos!

11
go.mod
View File

@ -3,6 +3,7 @@ module github.com/usememos/memos
go 1.25 go 1.25
require ( require (
connectrpc.com/connect v1.19.1
github.com/aws/aws-sdk-go-v2 v1.39.2 github.com/aws/aws-sdk-go-v2 v1.39.2
github.com/aws/aws-sdk-go-v2/config v1.31.12 github.com/aws/aws-sdk-go-v2/config v1.31.12
github.com/aws/aws-sdk-go-v2/credentials v1.18.16 github.com/aws/aws-sdk-go-v2/credentials v1.18.16
@ -12,9 +13,7 @@ require (
github.com/google/cel-go v0.26.1 github.com/google/cel-go v0.26.1
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/gorilla/feeds v1.2.0 github.com/gorilla/feeds v1.2.0
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2
github.com/improbable-eng/grpc-web v0.15.0
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
github.com/labstack/echo/v4 v4.13.4 github.com/labstack/echo/v4 v4.13.4
github.com/lib/pq v1.10.9 github.com/lib/pq v1.10.9
@ -23,11 +22,12 @@ require (
github.com/spf13/cobra v1.10.1 github.com/spf13/cobra v1.10.1
github.com/spf13/viper v1.20.1 github.com/spf13/viper v1.20.1
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/usememos/gomark v0.0.0-20251021153759-00d1ea6c86f0 github.com/yuin/goldmark v1.7.13
golang.org/x/crypto v0.42.0 golang.org/x/crypto v0.42.0
golang.org/x/mod v0.28.0 golang.org/x/mod v0.28.0
golang.org/x/net v0.43.0 golang.org/x/net v0.43.0
golang.org/x/oauth2 v0.30.0 golang.org/x/oauth2 v0.30.0
golang.org/x/sync v0.17.0
google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1 google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1
google.golang.org/grpc v1.75.1 google.golang.org/grpc v1.75.1
modernc.org/sqlite v1.38.2 modernc.org/sqlite v1.38.2
@ -37,8 +37,6 @@ require (
cel.dev/expr v0.24.0 // indirect cel.dev/expr v0.24.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/desertbit/timer v1.0.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
@ -47,7 +45,6 @@ require (
github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/rs/cors v1.11.1 // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.12.0 // indirect github.com/spf13/afero v1.12.0 // indirect
@ -63,7 +60,6 @@ require (
modernc.org/libc v1.66.8 // indirect modernc.org/libc v1.66.8 // indirect
modernc.org/mathutil v1.7.1 // indirect modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect modernc.org/memory v1.11.0 // indirect
nhooyr.io/websocket v1.8.17 // indirect
) )
require ( require (
@ -88,7 +84,6 @@ require (
github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/soheilhy/cmux v0.1.5
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect
golang.org/x/sys v0.36.0 // indirect golang.org/x/sys v0.36.0 // indirect

492
go.sum
View File

@ -1,33 +1,11 @@
cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/aws/aws-sdk-go-v2 v1.39.2 h1:EJLg8IdbzgeD7xgvZ+I8M1e0fL0ptn/M47lianzth0I= github.com/aws/aws-sdk-go-v2 v1.39.2 h1:EJLg8IdbzgeD7xgvZ+I8M1e0fL0ptn/M47lianzth0I=
github.com/aws/aws-sdk-go-v2 v1.39.2/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY= github.com/aws/aws-sdk-go-v2 v1.39.2/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 h1:i8p8P4diljCr60PpJp6qZXNlgX4m2yQFpYk+9ZT+J4E= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 h1:i8p8P4diljCr60PpJp6qZXNlgX4m2yQFpYk+9ZT+J4E=
@ -66,361 +44,97 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 h1:p3jIvqYwUZgu/XYeI48bJxOhvm47
github.com/aws/aws-sdk-go-v2/service/sts v1.38.6/go.mod h1:WtKK+ppze5yKPkZ0XwqIVWD4beCwv056ZbPQNoeHqM8= github.com/aws/aws-sdk-go-v2/service/sts v1.38.6/go.mod h1:WtKK+ppze5yKPkZ0XwqIVWD4beCwv056ZbPQNoeHqM8=
github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE= github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE=
github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE=
github.com/desertbit/timer v1.0.1 h1:yRpYNn5Vaaj6QXecdLMPMJsW81JLiI1eokUft5nBmeo=
github.com/desertbit/timer v1.0.1/go.mod h1:htRrYeY5V/t4iu1xCJ5XsQvp4xve8QulXXctAzxqcwE=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/cel-go v0.26.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ= github.com/google/cel-go v0.26.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ=
github.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= github.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/feeds v1.2.0 h1:O6pBiXJ5JHhPvqy53NsjKOThq+dNFm8+DFrxBEdzSCc= github.com/gorilla/feeds v1.2.0 h1:O6pBiXJ5JHhPvqy53NsjKOThq+dNFm8+DFrxBEdzSCc=
github.com/gorilla/feeds v1.2.0/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y= github.com/gorilla/feeds v1.2.0/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ=
github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcXEA= github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcXEA=
github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ= github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lithammer/shortuuid/v4 v4.2.0 h1:LMFOzVB3996a7b8aBuEXxqOBflbfPQAiVzkIcHO0h8c= github.com/lithammer/shortuuid/v4 v4.2.0 h1:LMFOzVB3996a7b8aBuEXxqOBflbfPQAiVzkIcHO0h8c=
github.com/lithammer/shortuuid/v4 v4.2.0/go.mod h1:D5noHZ2oFw/YaKCfGy0YxyE7M0wMbezmMjPdhyEFe6Y= github.com/lithammer/shortuuid/v4 v4.2.0/go.mod h1:D5noHZ2oFw/YaKCfGy0YxyE7M0wMbezmMjPdhyEFe6Y=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs= github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs=
github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
@ -428,25 +142,12 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/usememos/gomark v0.0.0-20251021153759-00d1ea6c86f0 h1:hN+LjlPdqd/6OLYWs5mYYwJ6WUQBKBUreCt1Kg8u5jk=
github.com/usememos/gomark v0.0.0-20251021153759-00d1ea6c86f0/go.mod h1:7CZRoYFQyyljzplOTeyODFR26O+wr0BbnpTWVLGfKJA=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
@ -459,231 +160,51 @@ go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFh
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps= go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0= golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0=
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4= golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.30.0 h1:jD5RhkmVAnjqaCUXfbGBrn3lpxbknfN9w2UhHHU+5B4= golang.org/x/image v0.30.0 h1:jD5RhkmVAnjqaCUXfbGBrn3lpxbknfN9w2UhHHU+5B4=
golang.org/x/image v0.30.0/go.mod h1:SAEUTxCCMWSrJcCy/4HwavEsfZZJlYxeHLc6tTiAe/c= golang.org/x/image v0.30.0/go.mod h1:SAEUTxCCMWSrJcCy/4HwavEsfZZJlYxeHLc6tTiAe/c=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1 h1:APHvLLYBhtZvsbnpkfknDZ7NyH4z5+ub/I0u8L3Oz6g= google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1 h1:APHvLLYBhtZvsbnpkfknDZ7NyH4z5+ub/I0u8L3Oz6g=
google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1/go.mod h1:xUjFWUnWDpZ/C0Gu0qloASKFb6f8/QXiiXhSPFsD668= google.golang.org/genproto/googleapis/api v0.0.0-20250826171959-ef028d996bc1/go.mod h1:xUjFWUnWDpZ/C0Gu0qloASKFb6f8/QXiiXhSPFsD668=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 h1:pmJpJEvT846VzausCQ5d7KreSROcDqmO388w5YbnltA= google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 h1:pmJpJEvT846VzausCQ5d7KreSROcDqmO388w5YbnltA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI= google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
modernc.org/cc/v4 v4.26.4 h1:jPhG8oNjtTYuP2FA4YefTJ/wioNUGALmGuEWt7SUR6s= modernc.org/cc/v4 v4.26.4 h1:jPhG8oNjtTYuP2FA4YefTJ/wioNUGALmGuEWt7SUR6s=
modernc.org/cc/v4 v4.26.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= modernc.org/cc/v4 v4.26.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.28.1 h1:wPKYn5EC/mYTqBO373jKjvX2n+3+aK7+sICCv4Fjy1A= modernc.org/ccgo/v4 v4.28.1 h1:wPKYn5EC/mYTqBO373jKjvX2n+3+aK7+sICCv4Fjy1A=
@ -710,8 +231,3 @@ modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
nhooyr.io/websocket v1.8.17 h1:KEVeLJkUywCKVsnLIDlD/5gtayKp8VoCkksHCGGfT9Y=
nhooyr.io/websocket v1.8.17/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=

View File

@ -9,10 +9,10 @@ import (
// Version is the service current released version. // Version is the service current released version.
// Semantic versioning: https://semver.org/ // Semantic versioning: https://semver.org/
var Version = "0.25.2" var Version = "0.25.3"
// DevVersion is the service current development version. // DevVersion is the service current development version.
var DevVersion = "0.25.2" var DevVersion = "0.25.3"
func GetCurrentVersion(mode string) string { func GetCurrentVersion(mode string) string {
if mode == "dev" || mode == "demo" { if mode == "dev" || mode == "demo" {

View File

@ -187,7 +187,7 @@ func TestChainSkipIfStillRunning(t *testing.T) {
}() }()
// After 5ms, the first job is still in progress, and the second job was // After 5ms, the first job is still in progress, and the second job was
// aleady skipped. // already skipped.
time.Sleep(5 * time.Millisecond) time.Sleep(5 * time.Millisecond)
started, done := j.Started(), j.Done() started, done := j.Started(), j.Done()
if started != 1 || done != 0 { if started != 1 || done != 0 {

View File

@ -338,13 +338,22 @@ func (r *renderer) renderTagInList(values []ValueExpr) (renderResult, error) {
switch r.dialect { switch r.dialect {
case DialectSQLite: case DialectSQLite:
expr := fmt.Sprintf("%s LIKE %s", jsonArrayExpr(r.dialect, field), r.addArg(fmt.Sprintf(`%%"%s"%%`, str))) // Support hierarchical tags: match exact tag OR tags with this prefix (e.g., "book" matches "book" and "book/something")
exactMatch := fmt.Sprintf("%s LIKE %s", jsonArrayExpr(r.dialect, field), r.addArg(fmt.Sprintf(`%%"%s"%%`, str)))
prefixMatch := fmt.Sprintf("%s LIKE %s", jsonArrayExpr(r.dialect, field), r.addArg(fmt.Sprintf(`%%"%s/%%`, str)))
expr := fmt.Sprintf("(%s OR %s)", exactMatch, prefixMatch)
conditions = append(conditions, expr) conditions = append(conditions, expr)
case DialectMySQL: case DialectMySQL:
expr := fmt.Sprintf("JSON_CONTAINS(%s, %s)", jsonArrayExpr(r.dialect, field), r.addArg(fmt.Sprintf(`"%s"`, str))) // Support hierarchical tags: match exact tag OR tags with this prefix
exactMatch := fmt.Sprintf("JSON_CONTAINS(%s, %s)", jsonArrayExpr(r.dialect, field), r.addArg(fmt.Sprintf(`"%s"`, str)))
prefixMatch := fmt.Sprintf("%s LIKE %s", jsonArrayExpr(r.dialect, field), r.addArg(fmt.Sprintf(`%%"%s/%%`, str)))
expr := fmt.Sprintf("(%s OR %s)", exactMatch, prefixMatch)
conditions = append(conditions, expr) conditions = append(conditions, expr)
case DialectPostgres: case DialectPostgres:
expr := fmt.Sprintf("%s @> jsonb_build_array(%s::json)", jsonArrayExpr(r.dialect, field), r.addArg(fmt.Sprintf(`"%s"`, str))) // Support hierarchical tags: match exact tag OR tags with this prefix
exactMatch := fmt.Sprintf("%s @> jsonb_build_array(%s::json)", jsonArrayExpr(r.dialect, field), r.addArg(fmt.Sprintf(`"%s"`, str)))
prefixMatch := fmt.Sprintf("(%s)::text LIKE %s", jsonArrayExpr(r.dialect, field), r.addArg(fmt.Sprintf(`%%"%s/%%`, str)))
expr := fmt.Sprintf("(%s OR %s)", exactMatch, prefixMatch)
conditions = append(conditions, expr) conditions = append(conditions, expr)
default: default:
return renderResult{}, errors.Errorf("unsupported dialect %s", r.dialect) return renderResult{}, errors.Errorf("unsupported dialect %s", r.dialect)
@ -382,10 +391,10 @@ func (r *renderer) renderElementInCondition(cond *ElementInCondition) (renderRes
sql := fmt.Sprintf("%s LIKE %s", jsonArrayExpr(r.dialect, field), r.addArg(fmt.Sprintf(`%%"%s"%%`, str))) sql := fmt.Sprintf("%s LIKE %s", jsonArrayExpr(r.dialect, field), r.addArg(fmt.Sprintf(`%%"%s"%%`, str)))
return renderResult{sql: sql}, nil return renderResult{sql: sql}, nil
case DialectMySQL: case DialectMySQL:
sql := fmt.Sprintf("JSON_CONTAINS(%s, %s)", jsonArrayExpr(r.dialect, field), r.addArg(str)) sql := fmt.Sprintf("JSON_CONTAINS(%s, %s)", jsonArrayExpr(r.dialect, field), r.addArg(fmt.Sprintf(`"%s"`, str)))
return renderResult{sql: sql}, nil return renderResult{sql: sql}, nil
case DialectPostgres: case DialectPostgres:
sql := fmt.Sprintf("%s @> jsonb_build_array(%s::json)", jsonArrayExpr(r.dialect, field), r.addArg(str)) sql := fmt.Sprintf("%s @> jsonb_build_array(%s::json)", jsonArrayExpr(r.dialect, field), r.addArg(fmt.Sprintf(`"%s"`, str)))
return renderResult{sql: sql}, nil return renderResult{sql: sql}, nil
default: default:
return renderResult{}, errors.Errorf("unsupported dialect %s", r.dialect) return renderResult{}, errors.Errorf("unsupported dialect %s", r.dialect)

View File

@ -41,7 +41,8 @@ func NewIdentityProvider(config *storepb.OAuth2Config) (*IdentityProvider, error
} }
// ExchangeToken returns the exchanged OAuth2 token using the given authorization code. // ExchangeToken returns the exchanged OAuth2 token using the given authorization code.
func (p *IdentityProvider) ExchangeToken(ctx context.Context, redirectURL, code string) (string, error) { // If codeVerifier is provided, it will be used for PKCE (Proof Key for Code Exchange) validation.
func (p *IdentityProvider) ExchangeToken(ctx context.Context, redirectURL, code, codeVerifier string) (string, error) {
conf := &oauth2.Config{ conf := &oauth2.Config{
ClientID: p.config.ClientId, ClientID: p.config.ClientId,
ClientSecret: p.config.ClientSecret, ClientSecret: p.config.ClientSecret,
@ -54,17 +55,26 @@ func (p *IdentityProvider) ExchangeToken(ctx context.Context, redirectURL, code
}, },
} }
token, err := conf.Exchange(ctx, code) // Prepare token exchange options
opts := []oauth2.AuthCodeOption{}
// Add PKCE code_verifier if provided
if codeVerifier != "" {
opts = append(opts, oauth2.SetAuthURLParam("code_verifier", codeVerifier))
}
token, err := conf.Exchange(ctx, code, opts...)
if err != nil { if err != nil {
return "", errors.Wrap(err, "failed to exchange access token") return "", errors.Wrap(err, "failed to exchange access token")
} }
accessToken, ok := token.Extra("access_token").(string) // Use the standard AccessToken field instead of Extra()
if !ok { // This is more reliable across different OAuth providers
return "", errors.New(`missing "access_token" from authorization response`) if token.AccessToken == "" {
return "", errors.New("missing access token from authorization response")
} }
return accessToken, nil return token.AccessToken, nil
} }
// UserInfo returns the parsed user information using the given OAuth2 token. // UserInfo returns the parsed user information using the given OAuth2 token.

View File

@ -147,7 +147,8 @@ func TestIdentityProvider(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
redirectURL := "https://example.com/oauth/callback" redirectURL := "https://example.com/oauth/callback"
oauthToken, err := oauth2.ExchangeToken(ctx, redirectURL, testCode) // Test without PKCE (backward compatibility)
oauthToken, err := oauth2.ExchangeToken(ctx, redirectURL, testCode, "")
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, testAccessToken, oauthToken) require.Equal(t, testAccessToken, oauthToken)

View File

@ -0,0 +1,28 @@
package ast
import (
gast "github.com/yuin/goldmark/ast"
)
// TagNode represents a #tag in the markdown AST.
type TagNode struct {
gast.BaseInline
// Tag name without the # prefix
Tag []byte
}
// KindTag is the NodeKind for TagNode.
var KindTag = gast.NewNodeKind("Tag")
// Kind returns KindTag.
func (*TagNode) Kind() gast.NodeKind {
return KindTag
}
// Dump implements Node.Dump for debugging.
func (n *TagNode) Dump(source []byte, level int) {
gast.DumpHelper(n, source, level, map[string]string{
"Tag": string(n.Tag),
}, nil)
}

View File

@ -0,0 +1,24 @@
package extensions
import (
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/util"
mparser "github.com/usememos/memos/plugin/markdown/parser"
)
type tagExtension struct{}
// TagExtension is a goldmark extension for #tag syntax.
var TagExtension = &tagExtension{}
// Extend extends the goldmark parser with tag support.
func (*tagExtension) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(
parser.WithInlineParsers(
// Priority 200 - run before standard link parser (500)
util.Prioritized(mparser.NewTagParser(), 200),
),
)
}

410
plugin/markdown/markdown.go Normal file
View File

@ -0,0 +1,410 @@
package markdown
import (
"bytes"
"strings"
"github.com/yuin/goldmark"
gast "github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/extension"
east "github.com/yuin/goldmark/extension/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/text"
mast "github.com/usememos/memos/plugin/markdown/ast"
"github.com/usememos/memos/plugin/markdown/extensions"
"github.com/usememos/memos/plugin/markdown/renderer"
storepb "github.com/usememos/memos/proto/gen/store"
)
// ExtractedData contains all metadata extracted from markdown in a single pass.
type ExtractedData struct {
Tags []string
Property *storepb.MemoPayload_Property
}
// Service handles markdown metadata extraction.
// It uses goldmark to parse markdown and extract tags, properties, and snippets.
// HTML rendering is primarily done on frontend using markdown-it, but backend provides
// RenderHTML for RSS feeds and other server-side rendering needs.
type Service interface {
// ExtractAll extracts tags, properties, and references in a single parse (most efficient)
ExtractAll(content []byte) (*ExtractedData, error)
// ExtractTags returns all #tags found in content
ExtractTags(content []byte) ([]string, error)
// ExtractProperties computes boolean properties
ExtractProperties(content []byte) (*storepb.MemoPayload_Property, error)
// RenderMarkdown renders goldmark AST back to markdown text
RenderMarkdown(content []byte) (string, error)
// RenderHTML renders markdown content to HTML
RenderHTML(content []byte) (string, error)
// GenerateSnippet creates plain text summary
GenerateSnippet(content []byte, maxLength int) (string, error)
// ValidateContent checks for syntax errors
ValidateContent(content []byte) error
// RenameTag renames all occurrences of oldTag to newTag in content
RenameTag(content []byte, oldTag, newTag string) (string, error)
}
// service implements the Service interface.
type service struct {
md goldmark.Markdown
}
// Option configures the markdown service.
type Option func(*config)
type config struct {
enableTags bool
}
// WithTagExtension enables #tag parsing.
func WithTagExtension() Option {
return func(c *config) {
c.enableTags = true
}
}
// NewService creates a new markdown service with the given options.
func NewService(opts ...Option) Service {
cfg := &config{}
for _, opt := range opts {
opt(cfg)
}
exts := []goldmark.Extender{
extension.GFM, // GitHub Flavored Markdown (tables, strikethrough, task lists, autolinks)
}
// Add custom extensions based on config
if cfg.enableTags {
exts = append(exts, extensions.TagExtension)
}
md := goldmark.New(
goldmark.WithExtensions(exts...),
goldmark.WithParserOptions(
parser.WithAutoHeadingID(), // Generate heading IDs
),
)
return &service{
md: md,
}
}
// parse is an internal helper to parse content into AST.
func (s *service) parse(content []byte) (gast.Node, error) {
reader := text.NewReader(content)
doc := s.md.Parser().Parse(reader)
return doc, nil
}
// ExtractTags returns all #tags found in content.
func (s *service) ExtractTags(content []byte) ([]string, error) {
root, err := s.parse(content)
if err != nil {
return nil, err
}
var tags []string
// Walk the AST to find tag nodes
err = gast.Walk(root, func(n gast.Node, entering bool) (gast.WalkStatus, error) {
if !entering {
return gast.WalkContinue, nil
}
// Check for custom TagNode
if tagNode, ok := n.(*mast.TagNode); ok {
tags = append(tags, string(tagNode.Tag))
}
return gast.WalkContinue, nil
})
if err != nil {
return nil, err
}
// Deduplicate and normalize tags
return uniqueLowercase(tags), nil
}
// ExtractProperties computes boolean properties about the content.
func (s *service) ExtractProperties(content []byte) (*storepb.MemoPayload_Property, error) {
root, err := s.parse(content)
if err != nil {
return nil, err
}
prop := &storepb.MemoPayload_Property{}
err = gast.Walk(root, func(n gast.Node, entering bool) (gast.WalkStatus, error) {
if !entering {
return gast.WalkContinue, nil
}
switch n.Kind() {
case gast.KindLink:
prop.HasLink = true
case gast.KindCodeBlock, gast.KindFencedCodeBlock, gast.KindCodeSpan:
prop.HasCode = true
case east.KindTaskCheckBox:
prop.HasTaskList = true
if checkBox, ok := n.(*east.TaskCheckBox); ok {
if !checkBox.IsChecked {
prop.HasIncompleteTasks = true
}
}
default:
// No special handling for other node types
}
return gast.WalkContinue, nil
})
if err != nil {
return nil, err
}
return prop, nil
}
// RenderMarkdown renders goldmark AST back to markdown text.
func (s *service) RenderMarkdown(content []byte) (string, error) {
root, err := s.parse(content)
if err != nil {
return "", err
}
mdRenderer := renderer.NewMarkdownRenderer()
return mdRenderer.Render(root, content), nil
}
// RenderHTML renders markdown content to HTML using goldmark's built-in HTML renderer.
func (s *service) RenderHTML(content []byte) (string, error) {
var buf bytes.Buffer
if err := s.md.Convert(content, &buf); err != nil {
return "", err
}
return buf.String(), nil
}
// GenerateSnippet creates a plain text summary from markdown content.
func (s *service) GenerateSnippet(content []byte, maxLength int) (string, error) {
root, err := s.parse(content)
if err != nil {
return "", err
}
var buf strings.Builder
var lastNodeWasBlock bool
err = gast.Walk(root, func(n gast.Node, entering bool) (gast.WalkStatus, error) {
if entering {
// Skip code blocks and code spans entirely
switch n.Kind() {
case gast.KindCodeBlock, gast.KindFencedCodeBlock, gast.KindCodeSpan:
return gast.WalkSkipChildren, nil
default:
// Continue walking for other node types
}
// Add space before block elements (except first)
switch n.Kind() {
case gast.KindParagraph, gast.KindHeading, gast.KindListItem:
if buf.Len() > 0 && lastNodeWasBlock {
buf.WriteByte(' ')
}
default:
// No space needed for other node types
}
}
if !entering {
// Mark that we just exited a block element
switch n.Kind() {
case gast.KindParagraph, gast.KindHeading, gast.KindListItem:
lastNodeWasBlock = true
default:
// Not a block element
}
return gast.WalkContinue, nil
}
lastNodeWasBlock = false
// Only extract plain text nodes
if textNode, ok := n.(*gast.Text); ok {
segment := textNode.Segment
buf.Write(segment.Value(content))
// Add space if this is a soft line break
if textNode.SoftLineBreak() {
buf.WriteByte(' ')
}
}
// Stop walking if we've exceeded double the max length
// (we'll truncate precisely later)
if buf.Len() > maxLength*2 {
return gast.WalkStop, nil
}
return gast.WalkContinue, nil
})
if err != nil {
return "", err
}
snippet := buf.String()
// Truncate at word boundary if needed
if len(snippet) > maxLength {
snippet = truncateAtWord(snippet, maxLength)
}
return strings.TrimSpace(snippet), nil
}
// ValidateContent checks if the markdown content is valid.
func (s *service) ValidateContent(content []byte) error {
// Try to parse the content
_, err := s.parse(content)
return err
}
// ExtractAll extracts tags, properties, and references in a single parse for efficiency.
func (s *service) ExtractAll(content []byte) (*ExtractedData, error) {
root, err := s.parse(content)
if err != nil {
return nil, err
}
data := &ExtractedData{
Tags: []string{},
Property: &storepb.MemoPayload_Property{},
}
// Single walk to collect all data
err = gast.Walk(root, func(n gast.Node, entering bool) (gast.WalkStatus, error) {
if !entering {
return gast.WalkContinue, nil
}
// Extract tags
if tagNode, ok := n.(*mast.TagNode); ok {
data.Tags = append(data.Tags, string(tagNode.Tag))
}
// Extract properties based on node kind
switch n.Kind() {
case gast.KindLink:
data.Property.HasLink = true
case gast.KindCodeBlock, gast.KindFencedCodeBlock, gast.KindCodeSpan:
data.Property.HasCode = true
case east.KindTaskCheckBox:
data.Property.HasTaskList = true
if checkBox, ok := n.(*east.TaskCheckBox); ok {
if !checkBox.IsChecked {
data.Property.HasIncompleteTasks = true
}
}
default:
// No special handling for other node types
}
return gast.WalkContinue, nil
})
if err != nil {
return nil, err
}
// Deduplicate and normalize tags
data.Tags = uniqueLowercase(data.Tags)
return data, nil
}
// RenameTag renames all occurrences of oldTag to newTag in content.
func (s *service) RenameTag(content []byte, oldTag, newTag string) (string, error) {
root, err := s.parse(content)
if err != nil {
return "", err
}
// Walk the AST to find and rename tag nodes
err = gast.Walk(root, func(n gast.Node, entering bool) (gast.WalkStatus, error) {
if !entering {
return gast.WalkContinue, nil
}
// Check for custom TagNode and rename if it matches
if tagNode, ok := n.(*mast.TagNode); ok {
if string(tagNode.Tag) == oldTag {
tagNode.Tag = []byte(newTag)
}
}
return gast.WalkContinue, nil
})
if err != nil {
return "", err
}
// Render back to markdown using the already-parsed AST
mdRenderer := renderer.NewMarkdownRenderer()
return mdRenderer.Render(root, content), nil
}
// uniqueLowercase returns unique lowercase strings from input.
func uniqueLowercase(strs []string) []string {
seen := make(map[string]bool)
var result []string
for _, s := range strs {
lower := strings.ToLower(s)
if !seen[lower] {
seen[lower] = true
result = append(result, lower)
}
}
return result
}
// truncateAtWord truncates a string at the last word boundary before maxLength.
// maxLength is treated as a rune (character) count to properly handle UTF-8 multi-byte characters.
func truncateAtWord(s string, maxLength int) string {
// Convert to runes to properly handle multi-byte UTF-8 characters
runes := []rune(s)
if len(runes) <= maxLength {
return s
}
// Truncate to max length (by character count, not byte count)
truncated := string(runes[:maxLength])
// Find last space to avoid cutting in the middle of a word
lastSpace := strings.LastIndexAny(truncated, " \t\n\r")
if lastSpace > 0 {
truncated = truncated[:lastSpace]
}
return truncated + " ..."
}

View File

@ -0,0 +1,448 @@
package markdown
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewService(t *testing.T) {
svc := NewService()
assert.NotNil(t, svc)
}
func TestValidateContent(t *testing.T) {
svc := NewService()
tests := []struct {
name string
content string
wantErr bool
}{
{
name: "valid markdown",
content: "# Hello\n\nThis is **bold** text.",
wantErr: false,
},
{
name: "empty content",
content: "",
wantErr: false,
},
{
name: "complex markdown",
content: "# Title\n\n- List item 1\n- List item 2\n\n```go\ncode block\n```",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := svc.ValidateContent([]byte(tt.content))
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestGenerateSnippet(t *testing.T) {
svc := NewService()
tests := []struct {
name string
content string
maxLength int
expected string
}{
{
name: "simple text",
content: "Hello world",
maxLength: 100,
expected: "Hello world",
},
{
name: "text with formatting",
content: "This is **bold** and *italic* text.",
maxLength: 100,
expected: "This is bold and italic text.",
},
{
name: "truncate long text",
content: "This is a very long piece of text that should be truncated at a word boundary.",
maxLength: 30,
expected: "This is a very long piece of ...",
},
{
name: "heading and paragraph",
content: "# My Title\n\nThis is the first paragraph.",
maxLength: 100,
expected: "My Title This is the first paragraph.",
},
{
name: "code block removed",
content: "Text before\n\n```go\ncode\n```\n\nText after",
maxLength: 100,
expected: "Text before Text after",
},
{
name: "list items",
content: "- Item 1\n- Item 2\n- Item 3",
maxLength: 100,
expected: "Item 1 Item 2 Item 3",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
snippet, err := svc.GenerateSnippet([]byte(tt.content), tt.maxLength)
require.NoError(t, err)
assert.Equal(t, tt.expected, snippet)
})
}
}
func TestExtractProperties(t *testing.T) {
tests := []struct {
name string
content string
hasLink bool
hasCode bool
hasTasks bool
hasInc bool
}{
{
name: "plain text",
content: "Just plain text",
hasLink: false,
hasCode: false,
hasTasks: false,
hasInc: false,
},
{
name: "with link",
content: "Check out [this link](https://example.com)",
hasLink: true,
hasCode: false,
hasTasks: false,
hasInc: false,
},
{
name: "with inline code",
content: "Use `console.log()` to debug",
hasLink: false,
hasCode: true,
hasTasks: false,
hasInc: false,
},
{
name: "with code block",
content: "```go\nfunc main() {}\n```",
hasLink: false,
hasCode: true,
hasTasks: false,
hasInc: false,
},
{
name: "with completed task",
content: "- [x] Completed task",
hasLink: false,
hasCode: false,
hasTasks: true,
hasInc: false,
},
{
name: "with incomplete task",
content: "- [ ] Todo item",
hasLink: false,
hasCode: false,
hasTasks: true,
hasInc: true,
},
{
name: "mixed tasks",
content: "- [x] Done\n- [ ] Not done",
hasLink: false,
hasCode: false,
hasTasks: true,
hasInc: true,
},
{
name: "everything",
content: "# Title\n\n[Link](url)\n\n`code`\n\n- [ ] Task",
hasLink: true,
hasCode: true,
hasTasks: true,
hasInc: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
svc := NewService()
props, err := svc.ExtractProperties([]byte(tt.content))
require.NoError(t, err)
assert.Equal(t, tt.hasLink, props.HasLink, "HasLink")
assert.Equal(t, tt.hasCode, props.HasCode, "HasCode")
assert.Equal(t, tt.hasTasks, props.HasTaskList, "HasTaskList")
assert.Equal(t, tt.hasInc, props.HasIncompleteTasks, "HasIncompleteTasks")
})
}
}
func TestExtractTags(t *testing.T) {
tests := []struct {
name string
content string
withExt bool
expected []string
}{
{
name: "no tags",
content: "Just plain text",
withExt: false,
expected: []string{},
},
{
name: "single tag",
content: "Text with #tag",
withExt: true,
expected: []string{"tag"},
},
{
name: "multiple tags",
content: "Text with #tag1 and #tag2",
withExt: true,
expected: []string{"tag1", "tag2"},
},
{
name: "duplicate tags",
content: "#work is important. #Work #WORK",
withExt: true,
expected: []string{"work"}, // Deduplicated and lowercased
},
{
name: "tags with hyphens and underscores",
content: "Tags: #work-notes #2024_plans",
withExt: true,
expected: []string{"work-notes", "2024_plans"},
},
{
name: "tags at end of sentence",
content: "This is important #urgent.",
withExt: true,
expected: []string{"urgent"},
},
{
name: "headings not tags",
content: "## Heading\n\n# Title\n\nText with #realtag",
withExt: true,
expected: []string{"realtag"},
},
{
name: "numeric tag",
content: "Issue #123",
withExt: true,
expected: []string{"123"},
},
{
name: "tag in list",
content: "- Item 1 #todo\n- Item 2 #done",
withExt: true,
expected: []string{"todo", "done"},
},
{
name: "no extension enabled",
content: "Text with #tag",
withExt: false,
expected: []string{},
},
{
name: "Chinese tag",
content: "Text with #测试",
withExt: true,
expected: []string{"测试"},
},
{
name: "Chinese tag followed by punctuation",
content: "Text #测试。 More text",
withExt: true,
expected: []string{"测试"},
},
{
name: "mixed Chinese and ASCII tag",
content: "#测试test123 content",
withExt: true,
expected: []string{"测试test123"},
},
{
name: "Japanese tag",
content: "#日本語 content",
withExt: true,
expected: []string{"日本語"},
},
{
name: "Korean tag",
content: "#한국어 content",
withExt: true,
expected: []string{"한국어"},
},
{
name: "hierarchical tag with Chinese",
content: "#work/测试/项目",
withExt: true,
expected: []string{"work/测试/项目"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var svc Service
if tt.withExt {
svc = NewService(WithTagExtension())
} else {
svc = NewService()
}
tags, err := svc.ExtractTags([]byte(tt.content))
require.NoError(t, err)
assert.ElementsMatch(t, tt.expected, tags)
})
}
}
func TestUniqueLowercase(t *testing.T) {
tests := []struct {
name string
input []string
expected []string
}{
{
name: "empty",
input: []string{},
expected: []string{},
},
{
name: "unique items",
input: []string{"tag1", "tag2", "tag3"},
expected: []string{"tag1", "tag2", "tag3"},
},
{
name: "duplicates",
input: []string{"tag", "TAG", "Tag"},
expected: []string{"tag"},
},
{
name: "mixed",
input: []string{"Work", "work", "Important", "work"},
expected: []string{"work", "important"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := uniqueLowercase(tt.input)
assert.ElementsMatch(t, tt.expected, result)
})
}
}
func TestTruncateAtWord(t *testing.T) {
tests := []struct {
name string
input string
maxLength int
expected string
}{
{
name: "no truncation needed",
input: "short",
maxLength: 10,
expected: "short",
},
{
name: "exact length",
input: "exactly ten",
maxLength: 11,
expected: "exactly ten",
},
{
name: "truncate at word",
input: "this is a long sentence",
maxLength: 10,
expected: "this is a ...",
},
{
name: "truncate very long word",
input: "supercalifragilisticexpialidocious",
maxLength: 10,
expected: "supercalif ...",
},
{
name: "CJK characters without spaces",
input: "这是一个很长的中文句子没有空格的情况下也要正确处理",
maxLength: 15,
expected: "这是一个很长的中文句子没有空格 ...",
},
{
name: "mixed CJK and Latin",
input: "这是中文mixed with English文字",
maxLength: 10,
expected: "这是中文mixed ...",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := truncateAtWord(tt.input, tt.maxLength)
assert.Equal(t, tt.expected, result)
})
}
}
// Benchmark tests.
func BenchmarkGenerateSnippet(b *testing.B) {
svc := NewService()
content := []byte(`# Large Document
This is a large document with multiple paragraphs and formatting.
## Section 1
Here is some **bold** text and *italic* text with [links](https://example.com).
- List item 1
- List item 2
- List item 3
## Section 2
More content here with ` + "`inline code`" + ` and other elements.
` + "```go\nfunc example() {\n return true\n}\n```")
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := svc.GenerateSnippet(content, 200)
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkExtractProperties(b *testing.B) {
svc := NewService()
content := []byte("# Title\n\n[Link](url)\n\n`code`\n\n- [ ] Task\n- [x] Done")
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := svc.ExtractProperties(content)
if err != nil {
b.Fatal(err)
}
}
}

View File

@ -0,0 +1,138 @@
package parser
import (
"unicode"
"unicode/utf8"
gast "github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/text"
mast "github.com/usememos/memos/plugin/markdown/ast"
)
const (
// MaxTagLength defines the maximum number of runes allowed in a tag.
MaxTagLength = 100
)
type tagParser struct{}
// NewTagParser creates a new inline parser for #tag syntax.
func NewTagParser() parser.InlineParser {
return &tagParser{}
}
// Trigger returns the characters that trigger this parser.
func (*tagParser) Trigger() []byte {
return []byte{'#'}
}
// isValidTagRune checks if a Unicode rune is valid in a tag.
// Uses Unicode categories for proper international character support.
func isValidTagRune(r rune) bool {
// Allow Unicode letters (any script: Latin, CJK, Arabic, Cyrillic, etc.)
if unicode.IsLetter(r) {
return true
}
// Allow Unicode digits
if unicode.IsNumber(r) {
return true
}
// Allow emoji and symbols (So category: Symbol, Other)
// This includes emoji, which are essential for social media-style tagging
if unicode.IsSymbol(r) {
return true
}
// Allow specific ASCII symbols for tag structure
// Underscore: word separation (snake_case)
// Hyphen: word separation (kebab-case)
// Forward slash: hierarchical tags (category/subcategory)
if r == '_' || r == '-' || r == '/' {
return true
}
return false
}
// Parse parses #tag syntax using Unicode-aware validation.
// Tags support international characters and follow these rules:
// - Must start with # followed by valid tag characters
// - Valid characters: Unicode letters, Unicode digits, underscore (_), hyphen (-), forward slash (/)
// - Maximum length: 100 runes (Unicode characters)
// - Stops at: whitespace, punctuation, or other invalid characters
func (*tagParser) Parse(_ gast.Node, block text.Reader, _ parser.Context) gast.Node {
line, _ := block.PeekLine()
// Must start with #
if len(line) == 0 || line[0] != '#' {
return nil
}
// Check if it's a heading (## or space after #)
if len(line) > 1 {
if line[1] == '#' {
// It's a heading (##), not a tag
return nil
}
if line[1] == ' ' {
// Space after # - heading or just a hash
return nil
}
} else {
// Just a lone #
return nil
}
// Parse tag using UTF-8 aware rune iteration
tagStart := 1
pos := tagStart
runeCount := 0
for pos < len(line) {
r, size := utf8.DecodeRune(line[pos:])
// Stop at invalid UTF-8
if r == utf8.RuneError && size == 1 {
break
}
// Validate character using Unicode categories
if !isValidTagRune(r) {
break
}
// Enforce max length (by rune count, not byte count)
runeCount++
if runeCount > MaxTagLength {
break
}
pos += size
}
// Must have at least one character after #
if pos <= tagStart {
return nil
}
// Extract tag (without #)
tagName := line[tagStart:pos]
// Make a copy of the tag name
tagCopy := make([]byte, len(tagName))
copy(tagCopy, tagName)
// Advance reader
block.Advance(pos)
// Create node
node := &mast.TagNode{
Tag: tagCopy,
}
return node
}

View File

@ -0,0 +1,245 @@
package parser
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/text"
mast "github.com/usememos/memos/plugin/markdown/ast"
)
func TestTagParser(t *testing.T) {
tests := []struct {
name string
input string
expectedTag string
shouldParse bool
}{
{
name: "basic tag",
input: "#tag",
expectedTag: "tag",
shouldParse: true,
},
{
name: "tag with hyphen",
input: "#work-notes",
expectedTag: "work-notes",
shouldParse: true,
},
{
name: "tag with underscore",
input: "#2024_plans",
expectedTag: "2024_plans",
shouldParse: true,
},
{
name: "numeric tag",
input: "#123",
expectedTag: "123",
shouldParse: true,
},
{
name: "tag followed by space",
input: "#tag ",
expectedTag: "tag",
shouldParse: true,
},
{
name: "tag followed by punctuation",
input: "#tag.",
expectedTag: "tag",
shouldParse: true,
},
{
name: "tag in sentence",
input: "#important task",
expectedTag: "important",
shouldParse: true,
},
{
name: "heading (##)",
input: "## Heading",
expectedTag: "",
shouldParse: false,
},
{
name: "space after hash",
input: "# heading",
expectedTag: "",
shouldParse: false,
},
{
name: "lone hash",
input: "#",
expectedTag: "",
shouldParse: false,
},
{
name: "hash with space",
input: "# ",
expectedTag: "",
shouldParse: false,
},
{
name: "special characters",
input: "#tag@special",
expectedTag: "tag",
shouldParse: true,
},
{
name: "mixed case",
input: "#WorkNotes",
expectedTag: "WorkNotes",
shouldParse: true,
},
{
name: "hierarchical tag with slash",
input: "#tag1/subtag",
expectedTag: "tag1/subtag",
shouldParse: true,
},
{
name: "hierarchical tag with multiple levels",
input: "#tag1/subtag/subtag2",
expectedTag: "tag1/subtag/subtag2",
shouldParse: true,
},
{
name: "hierarchical tag followed by space",
input: "#work/notes ",
expectedTag: "work/notes",
shouldParse: true,
},
{
name: "hierarchical tag followed by punctuation",
input: "#project/2024.",
expectedTag: "project/2024",
shouldParse: true,
},
{
name: "hierarchical tag with numbers and dashes",
input: "#work-log/2024/q1",
expectedTag: "work-log/2024/q1",
shouldParse: true,
},
{
name: "Chinese characters",
input: "#测试",
expectedTag: "测试",
shouldParse: true,
},
{
name: "Chinese tag followed by space",
input: "#测试 some text",
expectedTag: "测试",
shouldParse: true,
},
{
name: "Chinese tag followed by punctuation",
input: "#测试。",
expectedTag: "测试",
shouldParse: true,
},
{
name: "mixed Chinese and ASCII",
input: "#测试test123",
expectedTag: "测试test123",
shouldParse: true,
},
{
name: "Japanese characters",
input: "#テスト",
expectedTag: "テスト",
shouldParse: true,
},
{
name: "Korean characters",
input: "#테스트",
expectedTag: "테스트",
shouldParse: true,
},
{
name: "emoji",
input: "#test🚀",
expectedTag: "test🚀",
shouldParse: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := NewTagParser()
reader := text.NewReader([]byte(tt.input))
ctx := parser.NewContext()
node := p.Parse(nil, reader, ctx)
if tt.shouldParse {
require.NotNil(t, node, "Expected tag to be parsed")
require.IsType(t, &mast.TagNode{}, node)
tagNode, ok := node.(*mast.TagNode)
require.True(t, ok, "Expected node to be *mast.TagNode")
assert.Equal(t, tt.expectedTag, string(tagNode.Tag))
} else {
assert.Nil(t, node, "Expected tag NOT to be parsed")
}
})
}
}
func TestTagParser_Trigger(t *testing.T) {
p := NewTagParser()
triggers := p.Trigger()
assert.Equal(t, []byte{'#'}, triggers)
}
func TestTagParser_MultipleTags(t *testing.T) {
// Test that parser correctly handles multiple tags in sequence
input := "#tag1 #tag2"
p := NewTagParser()
reader := text.NewReader([]byte(input))
ctx := parser.NewContext()
// Parse first tag
node1 := p.Parse(nil, reader, ctx)
require.NotNil(t, node1)
tagNode1, ok := node1.(*mast.TagNode)
require.True(t, ok, "Expected node1 to be *mast.TagNode")
assert.Equal(t, "tag1", string(tagNode1.Tag))
// Advance past the space
reader.Advance(1)
// Parse second tag
node2 := p.Parse(nil, reader, ctx)
require.NotNil(t, node2)
tagNode2, ok := node2.(*mast.TagNode)
require.True(t, ok, "Expected node2 to be *mast.TagNode")
assert.Equal(t, "tag2", string(tagNode2.Tag))
}
func TestTagNode_Kind(t *testing.T) {
node := &mast.TagNode{
Tag: []byte("test"),
}
assert.Equal(t, mast.KindTag, node.Kind())
}
func TestTagNode_Dump(t *testing.T) {
node := &mast.TagNode{
Tag: []byte("test"),
}
// Should not panic
assert.NotPanics(t, func() {
node.Dump([]byte("#test"), 0)
})
}

View File

@ -0,0 +1,266 @@
package renderer
import (
"bytes"
"fmt"
"strings"
gast "github.com/yuin/goldmark/ast"
east "github.com/yuin/goldmark/extension/ast"
mast "github.com/usememos/memos/plugin/markdown/ast"
)
// MarkdownRenderer renders goldmark AST back to markdown text.
type MarkdownRenderer struct {
buf *bytes.Buffer
}
// NewMarkdownRenderer creates a new markdown renderer.
func NewMarkdownRenderer() *MarkdownRenderer {
return &MarkdownRenderer{
buf: &bytes.Buffer{},
}
}
// Render renders the AST node to markdown and returns the result.
func (r *MarkdownRenderer) Render(node gast.Node, source []byte) string {
r.buf.Reset()
r.renderNode(node, source, 0)
return r.buf.String()
}
// renderNode renders a single node and its children.
func (r *MarkdownRenderer) renderNode(node gast.Node, source []byte, depth int) {
switch n := node.(type) {
case *gast.Document:
r.renderChildren(n, source, depth)
case *gast.Paragraph:
r.renderChildren(n, source, depth)
if node.NextSibling() != nil {
r.buf.WriteString("\n\n")
}
case *gast.Text:
// Text nodes store their content as segments in the source
segment := n.Segment
r.buf.Write(segment.Value(source))
if n.SoftLineBreak() {
r.buf.WriteByte('\n')
} else if n.HardLineBreak() {
r.buf.WriteString(" \n")
}
case *gast.CodeSpan:
r.buf.WriteByte('`')
r.renderChildren(n, source, depth)
r.buf.WriteByte('`')
case *gast.Emphasis:
symbol := "*"
if n.Level == 2 {
symbol = "**"
}
r.buf.WriteString(symbol)
r.renderChildren(n, source, depth)
r.buf.WriteString(symbol)
case *gast.Link:
r.buf.WriteString("[")
r.renderChildren(n, source, depth)
r.buf.WriteString("](")
r.buf.Write(n.Destination)
if len(n.Title) > 0 {
r.buf.WriteString(` "`)
r.buf.Write(n.Title)
r.buf.WriteString(`"`)
}
r.buf.WriteString(")")
case *gast.AutoLink:
url := n.URL(source)
if n.AutoLinkType == gast.AutoLinkEmail {
r.buf.WriteString("<")
r.buf.Write(url)
r.buf.WriteString(">")
} else {
r.buf.Write(url)
}
case *gast.Image:
r.buf.WriteString("![")
r.renderChildren(n, source, depth)
r.buf.WriteString("](")
r.buf.Write(n.Destination)
if len(n.Title) > 0 {
r.buf.WriteString(` "`)
r.buf.Write(n.Title)
r.buf.WriteString(`"`)
}
r.buf.WriteString(")")
case *gast.Heading:
r.buf.WriteString(strings.Repeat("#", n.Level))
r.buf.WriteByte(' ')
r.renderChildren(n, source, depth)
if node.NextSibling() != nil {
r.buf.WriteString("\n\n")
}
case *gast.CodeBlock, *gast.FencedCodeBlock:
r.renderCodeBlock(n, source)
case *gast.Blockquote:
// Render each child line with "> " prefix
r.renderBlockquote(n, source, depth)
if node.NextSibling() != nil {
r.buf.WriteString("\n\n")
}
case *gast.List:
r.renderChildren(n, source, depth)
if node.NextSibling() != nil {
r.buf.WriteString("\n\n")
}
case *gast.ListItem:
r.renderListItem(n, source, depth)
case *gast.ThematicBreak:
r.buf.WriteString("---")
if node.NextSibling() != nil {
r.buf.WriteString("\n\n")
}
case *east.Strikethrough:
r.buf.WriteString("~~")
r.renderChildren(n, source, depth)
r.buf.WriteString("~~")
case *east.TaskCheckBox:
if n.IsChecked {
r.buf.WriteString("[x] ")
} else {
r.buf.WriteString("[ ] ")
}
case *east.Table:
r.renderTable(n, source)
if node.NextSibling() != nil {
r.buf.WriteString("\n\n")
}
// Custom Memos nodes
case *mast.TagNode:
r.buf.WriteByte('#')
r.buf.Write(n.Tag)
default:
// For unknown nodes, try to render children
r.renderChildren(n, source, depth)
}
}
// renderChildren renders all children of a node.
func (r *MarkdownRenderer) renderChildren(node gast.Node, source []byte, depth int) {
child := node.FirstChild()
for child != nil {
r.renderNode(child, source, depth+1)
child = child.NextSibling()
}
}
// renderCodeBlock renders a code block.
func (r *MarkdownRenderer) renderCodeBlock(node gast.Node, source []byte) {
if fenced, ok := node.(*gast.FencedCodeBlock); ok {
// Fenced code block with language
r.buf.WriteString("```")
if lang := fenced.Language(source); len(lang) > 0 {
r.buf.Write(lang)
}
r.buf.WriteByte('\n')
// Write all lines
lines := fenced.Lines()
for i := 0; i < lines.Len(); i++ {
line := lines.At(i)
r.buf.Write(line.Value(source))
}
r.buf.WriteString("```")
if node.NextSibling() != nil {
r.buf.WriteString("\n\n")
}
} else if codeBlock, ok := node.(*gast.CodeBlock); ok {
// Indented code block
lines := codeBlock.Lines()
for i := 0; i < lines.Len(); i++ {
line := lines.At(i)
r.buf.WriteString(" ")
r.buf.Write(line.Value(source))
}
if node.NextSibling() != nil {
r.buf.WriteString("\n\n")
}
}
}
// renderBlockquote renders a blockquote with "> " prefix.
func (r *MarkdownRenderer) renderBlockquote(node *gast.Blockquote, source []byte, depth int) {
// Create a temporary buffer for the blockquote content
tempBuf := &bytes.Buffer{}
tempRenderer := &MarkdownRenderer{buf: tempBuf}
tempRenderer.renderChildren(node, source, depth)
// Add "> " prefix to each line
content := tempBuf.String()
lines := strings.Split(strings.TrimRight(content, "\n"), "\n")
for i, line := range lines {
r.buf.WriteString("> ")
r.buf.WriteString(line)
if i < len(lines)-1 {
r.buf.WriteByte('\n')
}
}
}
// renderListItem renders a list item with proper indentation and markers.
func (r *MarkdownRenderer) renderListItem(node *gast.ListItem, source []byte, depth int) {
parent := node.Parent()
list, ok := parent.(*gast.List)
if !ok {
r.renderChildren(node, source, depth)
return
}
// Add indentation only for nested lists
// Document=0, List=1, ListItem=2 (no indent), nested ListItem=3+ (indent)
if depth > 2 {
indent := strings.Repeat(" ", depth-2)
r.buf.WriteString(indent)
}
// Add list marker
if list.IsOrdered() {
fmt.Fprintf(r.buf, "%d. ", list.Start)
list.Start++ // Increment for next item
} else {
r.buf.WriteString("- ")
}
// Render content
r.renderChildren(node, source, depth)
// Add newline if there's a next sibling
if node.NextSibling() != nil {
r.buf.WriteByte('\n')
}
}
// renderTable renders a table in markdown format.
func (r *MarkdownRenderer) renderTable(table *east.Table, source []byte) {
// This is a simplified table renderer
// A full implementation would need to handle alignment, etc.
r.renderChildren(table, source, 0)
}

View File

@ -0,0 +1,176 @@
package renderer
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/text"
"github.com/usememos/memos/plugin/markdown/extensions"
)
func TestMarkdownRenderer(t *testing.T) {
// Create goldmark instance with all extensions
md := goldmark.New(
goldmark.WithExtensions(
extension.GFM,
extensions.TagExtension,
),
goldmark.WithParserOptions(
parser.WithAutoHeadingID(),
),
)
tests := []struct {
name string
input string
expected string
}{
{
name: "simple text",
input: "Hello world",
expected: "Hello world",
},
{
name: "paragraph with newlines",
input: "First paragraph\n\nSecond paragraph",
expected: "First paragraph\n\nSecond paragraph",
},
{
name: "emphasis",
input: "This is *italic* and **bold** text",
expected: "This is *italic* and **bold** text",
},
{
name: "headings",
input: "# Heading 1\n\n## Heading 2\n\n### Heading 3",
expected: "# Heading 1\n\n## Heading 2\n\n### Heading 3",
},
{
name: "link",
input: "Check [this link](https://example.com)",
expected: "Check [this link](https://example.com)",
},
{
name: "image",
input: "![alt text](image.png)",
expected: "![alt text](image.png)",
},
{
name: "code inline",
input: "This is `inline code` here",
expected: "This is `inline code` here",
},
{
name: "code block fenced",
input: "```go\nfunc main() {\n}\n```",
expected: "```go\nfunc main() {\n}\n```",
},
{
name: "unordered list",
input: "- Item 1\n- Item 2\n- Item 3",
expected: "- Item 1\n- Item 2\n- Item 3",
},
{
name: "ordered list",
input: "1. First\n2. Second\n3. Third",
expected: "1. First\n2. Second\n3. Third",
},
{
name: "blockquote",
input: "> This is a quote\n> Second line",
expected: "> This is a quote\n> Second line",
},
{
name: "horizontal rule",
input: "Text before\n\n---\n\nText after",
expected: "Text before\n\n---\n\nText after",
},
{
name: "strikethrough",
input: "This is ~~deleted~~ text",
expected: "This is ~~deleted~~ text",
},
{
name: "task list",
input: "- [x] Completed task\n- [ ] Incomplete task",
expected: "- [x] Completed task\n- [ ] Incomplete task",
},
{
name: "tag",
input: "This has #tag in it",
expected: "This has #tag in it",
},
{
name: "multiple tags",
input: "#work #important meeting notes",
expected: "#work #important meeting notes",
},
{
name: "complex mixed content",
input: "# Meeting Notes\n\n**Date**: 2024-01-01\n\n## Attendees\n- Alice\n- Bob\n\n## Discussion\n\nWe discussed #project status.\n\n```python\nprint('hello')\n```",
expected: "# Meeting Notes\n\n**Date**: 2024-01-01\n\n## Attendees\n\n- Alice\n- Bob\n\n## Discussion\n\nWe discussed #project status.\n\n```python\nprint('hello')\n```",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Parse the input
source := []byte(tt.input)
reader := text.NewReader(source)
doc := md.Parser().Parse(reader)
require.NotNil(t, doc)
// Render back to markdown
renderer := NewMarkdownRenderer()
result := renderer.Render(doc, source)
// For debugging
if result != tt.expected {
t.Logf("Input: %q", tt.input)
t.Logf("Expected: %q", tt.expected)
t.Logf("Got: %q", result)
}
assert.Equal(t, tt.expected, result)
})
}
}
func TestMarkdownRendererPreservesStructure(t *testing.T) {
// Test that parsing and rendering preserves structure
md := goldmark.New(
goldmark.WithExtensions(
extension.GFM,
extensions.TagExtension,
),
)
inputs := []string{
"# Title\n\nParagraph",
"**Bold** and *italic*",
"- List\n- Items",
"#tag #another",
"> Quote",
}
renderer := NewMarkdownRenderer()
for _, input := range inputs {
t.Run(input, func(t *testing.T) {
source := []byte(input)
reader := text.NewReader(source)
doc := md.Parser().Parse(reader)
result := renderer.Render(doc, source)
// The result should be structurally similar
// (may have minor formatting differences)
assert.NotEmpty(t, result)
})
}
}

View File

@ -61,8 +61,6 @@ message Activity {
TYPE_UNSPECIFIED = 0; TYPE_UNSPECIFIED = 0;
// Memo comment activity. // Memo comment activity.
MEMO_COMMENT = 1; MEMO_COMMENT = 1;
// Version update activity.
VERSION_UPDATE = 2;
} }
// Activity levels. // Activity levels.

View File

@ -5,7 +5,6 @@ package memos.api.v1;
import "google/api/annotations.proto"; import "google/api/annotations.proto";
import "google/api/client.proto"; import "google/api/client.proto";
import "google/api/field_behavior.proto"; import "google/api/field_behavior.proto";
import "google/api/httpbody.proto";
import "google/api/resource.proto"; import "google/api/resource.proto";
import "google/protobuf/empty.proto"; import "google/protobuf/empty.proto";
import "google/protobuf/field_mask.proto"; import "google/protobuf/field_mask.proto";
@ -31,11 +30,6 @@ service AttachmentService {
option (google.api.http) = {get: "/api/v1/{name=attachments/*}"}; option (google.api.http) = {get: "/api/v1/{name=attachments/*}"};
option (google.api.method_signature) = "name"; option (google.api.method_signature) = "name";
} }
// GetAttachmentBinary returns a attachment binary by name.
rpc GetAttachmentBinary(GetAttachmentBinaryRequest) returns (google.api.HttpBody) {
option (google.api.http) = {get: "/file/{name=attachments/*}/{filename}"};
option (google.api.method_signature) = "name,filename,thumbnail";
}
// UpdateAttachment updates a attachment. // UpdateAttachment updates a attachment.
rpc UpdateAttachment(UpdateAttachmentRequest) returns (Attachment) { rpc UpdateAttachment(UpdateAttachmentRequest) returns (Attachment) {
option (google.api.http) = { option (google.api.http) = {
@ -138,21 +132,6 @@ message GetAttachmentRequest {
]; ];
} }
message GetAttachmentBinaryRequest {
// Required. The attachment name of the attachment.
// Format: attachments/{attachment}
string name = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {type: "memos.api.v1/Attachment"}
];
// The filename of the attachment. Mainly used for downloading.
string filename = 2 [(google.api.field_behavior) = REQUIRED];
// Optional. A flag indicating if the thumbnail version of the attachment should be returned.
bool thumbnail = 3 [(google.api.field_behavior) = OPTIONAL];
}
message UpdateAttachmentRequest { message UpdateAttachmentRequest {
// Required. The attachment which replaces the attachment on the server. // Required. The attachment which replaces the attachment on the server.
Attachment attachment = 1 [(google.api.field_behavior) = REQUIRED]; Attachment attachment = 1 [(google.api.field_behavior) = REQUIRED];

View File

@ -68,6 +68,10 @@ message CreateSessionRequest {
// The redirect URI used in the SSO flow. // The redirect URI used in the SSO flow.
// Required field for security validation. // Required field for security validation.
string redirect_uri = 3 [(google.api.field_behavior) = REQUIRED]; string redirect_uri = 3 [(google.api.field_behavior) = REQUIRED];
// The PKCE code verifier for enhanced security (RFC 7636).
// Optional field - if provided, enables PKCE flow protection against authorization code interception.
string code_verifier = 4 [(google.api.field_behavior) = OPTIONAL];
} }
// Provide one authentication method (username/password or SSO). // Provide one authentication method (username/password or SSO).

View File

@ -14,19 +14,19 @@ option go_package = "gen/api/v1";
service IdentityProviderService { service IdentityProviderService {
// ListIdentityProviders lists identity providers. // ListIdentityProviders lists identity providers.
rpc ListIdentityProviders(ListIdentityProvidersRequest) returns (ListIdentityProvidersResponse) { rpc ListIdentityProviders(ListIdentityProvidersRequest) returns (ListIdentityProvidersResponse) {
option (google.api.http) = {get: "/api/v1/identityProviders"}; option (google.api.http) = {get: "/api/v1/identity-providers"};
} }
// GetIdentityProvider gets an identity provider. // GetIdentityProvider gets an identity provider.
rpc GetIdentityProvider(GetIdentityProviderRequest) returns (IdentityProvider) { rpc GetIdentityProvider(GetIdentityProviderRequest) returns (IdentityProvider) {
option (google.api.http) = {get: "/api/v1/{name=identityProviders/*}"}; option (google.api.http) = {get: "/api/v1/{name=identity-providers/*}"};
option (google.api.method_signature) = "name"; option (google.api.method_signature) = "name";
} }
// CreateIdentityProvider creates an identity provider. // CreateIdentityProvider creates an identity provider.
rpc CreateIdentityProvider(CreateIdentityProviderRequest) returns (IdentityProvider) { rpc CreateIdentityProvider(CreateIdentityProviderRequest) returns (IdentityProvider) {
option (google.api.http) = { option (google.api.http) = {
post: "/api/v1/identityProviders" post: "/api/v1/identity-providers"
body: "identity_provider" body: "identity_provider"
}; };
option (google.api.method_signature) = "identity_provider"; option (google.api.method_signature) = "identity_provider";
@ -35,7 +35,7 @@ service IdentityProviderService {
// UpdateIdentityProvider updates an identity provider. // UpdateIdentityProvider updates an identity provider.
rpc UpdateIdentityProvider(UpdateIdentityProviderRequest) returns (IdentityProvider) { rpc UpdateIdentityProvider(UpdateIdentityProviderRequest) returns (IdentityProvider) {
option (google.api.http) = { option (google.api.http) = {
patch: "/api/v1/{identity_provider.name=identityProviders/*}" patch: "/api/v1/{identity_provider.name=identity-providers/*}"
body: "identity_provider" body: "identity_provider"
}; };
option (google.api.method_signature) = "identity_provider,update_mask"; option (google.api.method_signature) = "identity_provider,update_mask";
@ -43,7 +43,7 @@ service IdentityProviderService {
// DeleteIdentityProvider deletes an identity provider. // DeleteIdentityProvider deletes an identity provider.
rpc DeleteIdentityProvider(DeleteIdentityProviderRequest) returns (google.protobuf.Empty) { rpc DeleteIdentityProvider(DeleteIdentityProviderRequest) returns (google.protobuf.Empty) {
option (google.api.http) = {delete: "/api/v1/{name=identityProviders/*}"}; option (google.api.http) = {delete: "/api/v1/{name=identity-providers/*}"};
option (google.api.method_signature) = "name"; option (google.api.method_signature) = "name";
} }
} }
@ -51,14 +51,14 @@ service IdentityProviderService {
message IdentityProvider { message IdentityProvider {
option (google.api.resource) = { option (google.api.resource) = {
type: "memos.api.v1/IdentityProvider" type: "memos.api.v1/IdentityProvider"
pattern: "identityProviders/{idp}" pattern: "identity-providers/{idp}"
name_field: "name" name_field: "name"
singular: "identityProvider" singular: "identityProvider"
plural: "identityProviders" plural: "identityProviders"
}; };
// The resource name of the identity provider. // The resource name of the identity provider.
// Format: identityProviders/{idp} // Format: identity-providers/{idp}
string name = 1 [(google.api.field_behavior) = IDENTIFIER]; string name = 1 [(google.api.field_behavior) = IDENTIFIER];
// Required. The type of the identity provider. // Required. The type of the identity provider.
@ -112,7 +112,7 @@ message ListIdentityProvidersResponse {
message GetIdentityProviderRequest { message GetIdentityProviderRequest {
// Required. The resource name of the identity provider to get. // Required. The resource name of the identity provider to get.
// Format: identityProviders/{idp} // Format: identity-providers/{idp}
string name = 1 [ string name = 1 [
(google.api.field_behavior) = REQUIRED, (google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {type: "memos.api.v1/IdentityProvider"} (google.api.resource_reference) = {type: "memos.api.v1/IdentityProvider"}
@ -139,7 +139,7 @@ message UpdateIdentityProviderRequest {
message DeleteIdentityProviderRequest { message DeleteIdentityProviderRequest {
// Required. The resource name of the identity provider to delete. // Required. The resource name of the identity provider to delete.
// Format: identityProviders/{idp} // Format: identity-providers/{idp}
string name = 1 [ string name = 1 [
(google.api.field_behavior) = REQUIRED, (google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {type: "memos.api.v1/IdentityProvider"} (google.api.resource_reference) = {type: "memos.api.v1/IdentityProvider"}

View File

@ -1,149 +0,0 @@
syntax = "proto3";
package memos.api.v1;
import "google/api/annotations.proto";
import "google/api/client.proto";
import "google/api/field_behavior.proto";
import "google/api/resource.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/field_mask.proto";
import "google/protobuf/timestamp.proto";
option go_package = "gen/api/v1";
service InboxService {
// ListInboxes lists inboxes for a user.
rpc ListInboxes(ListInboxesRequest) returns (ListInboxesResponse) {
option (google.api.http) = {get: "/api/v1/{parent=users/*}/inboxes"};
option (google.api.method_signature) = "parent";
}
// UpdateInbox updates an inbox.
rpc UpdateInbox(UpdateInboxRequest) returns (Inbox) {
option (google.api.http) = {
patch: "/api/v1/{inbox.name=inboxes/*}"
body: "inbox"
};
option (google.api.method_signature) = "inbox,update_mask";
}
// DeleteInbox deletes an inbox.
rpc DeleteInbox(DeleteInboxRequest) returns (google.protobuf.Empty) {
option (google.api.http) = {delete: "/api/v1/{name=inboxes/*}"};
option (google.api.method_signature) = "name";
}
}
message Inbox {
option (google.api.resource) = {
type: "memos.api.v1/Inbox"
pattern: "inboxes/{inbox}"
name_field: "name"
singular: "inbox"
plural: "inboxes"
};
// The resource name of the inbox.
// Format: inboxes/{inbox}
string name = 1 [(google.api.field_behavior) = IDENTIFIER];
// The sender of the inbox notification.
// Format: users/{user}
string sender = 2 [(google.api.field_behavior) = OUTPUT_ONLY];
// The receiver of the inbox notification.
// Format: users/{user}
string receiver = 3 [(google.api.field_behavior) = OUTPUT_ONLY];
// The status of the inbox notification.
Status status = 4 [(google.api.field_behavior) = OPTIONAL];
// Output only. The creation timestamp.
google.protobuf.Timestamp create_time = 5 [(google.api.field_behavior) = OUTPUT_ONLY];
// The type of the inbox notification.
Type type = 6 [(google.api.field_behavior) = OUTPUT_ONLY];
// Optional. The activity ID associated with this inbox notification.
optional int32 activity_id = 7 [(google.api.field_behavior) = OPTIONAL];
// Status enumeration for inbox notifications.
enum Status {
// Unspecified status.
STATUS_UNSPECIFIED = 0;
// The notification is unread.
UNREAD = 1;
// The notification is archived.
ARCHIVED = 2;
}
// Type enumeration for inbox notifications.
enum Type {
// Unspecified type.
TYPE_UNSPECIFIED = 0;
// Memo comment notification.
MEMO_COMMENT = 1;
// Version update notification.
VERSION_UPDATE = 2;
}
}
message ListInboxesRequest {
// Required. The parent resource whose inboxes will be listed.
// Format: users/{user}
string parent = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {type: "memos.api.v1/User"}
];
// Optional. The maximum number of inboxes to return.
// The service may return fewer than this value.
// If unspecified, at most 50 inboxes will be returned.
// The maximum value is 1000; values above 1000 will be coerced to 1000.
int32 page_size = 2 [(google.api.field_behavior) = OPTIONAL];
// Optional. A page token, received from a previous `ListInboxes` call.
// Provide this to retrieve the subsequent page.
string page_token = 3 [(google.api.field_behavior) = OPTIONAL];
// Optional. Filter to apply to the list results.
// Example: "status=UNREAD" or "type=MEMO_COMMENT"
// Supported operators: =, !=
// Supported fields: status, type, sender, create_time
string filter = 4 [(google.api.field_behavior) = OPTIONAL];
// Optional. The order to sort results by.
// Example: "create_time desc" or "status asc"
string order_by = 5 [(google.api.field_behavior) = OPTIONAL];
}
message ListInboxesResponse {
// The list of inboxes.
repeated Inbox inboxes = 1;
// A token that can be sent as `page_token` to retrieve the next page.
// If this field is omitted, there are no subsequent pages.
string next_page_token = 2;
// The total count of inboxes (may be approximate).
int32 total_size = 3;
}
message UpdateInboxRequest {
// Required. The inbox to update.
Inbox inbox = 1 [(google.api.field_behavior) = REQUIRED];
// Required. The list of fields to update.
google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = REQUIRED];
// Optional. If set to true, allows updating missing fields.
bool allow_missing = 3 [(google.api.field_behavior) = OPTIONAL];
}
message DeleteInboxRequest {
// Required. The resource name of the inbox to delete.
// Format: inboxes/{inbox}
string name = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {type: "memos.api.v1/Inbox"}
];
}

View File

@ -10,30 +10,30 @@ import "google/protobuf/field_mask.proto";
option go_package = "gen/api/v1"; option go_package = "gen/api/v1";
service WorkspaceService { service InstanceService {
// Gets the workspace profile. // Gets the instance profile.
rpc GetWorkspaceProfile(GetWorkspaceProfileRequest) returns (WorkspaceProfile) { rpc GetInstanceProfile(GetInstanceProfileRequest) returns (InstanceProfile) {
option (google.api.http) = {get: "/api/v1/workspace/profile"}; option (google.api.http) = {get: "/api/v1/instance/profile"};
} }
// Gets a workspace setting. // Gets an instance setting.
rpc GetWorkspaceSetting(GetWorkspaceSettingRequest) returns (WorkspaceSetting) { rpc GetInstanceSetting(GetInstanceSettingRequest) returns (InstanceSetting) {
option (google.api.http) = {get: "/api/v1/{name=workspace/settings/*}"}; option (google.api.http) = {get: "/api/v1/{name=instance/settings/*}"};
option (google.api.method_signature) = "name"; option (google.api.method_signature) = "name";
} }
// Updates a workspace setting. // Updates an instance setting.
rpc UpdateWorkspaceSetting(UpdateWorkspaceSettingRequest) returns (WorkspaceSetting) { rpc UpdateInstanceSetting(UpdateInstanceSettingRequest) returns (InstanceSetting) {
option (google.api.http) = { option (google.api.http) = {
patch: "/api/v1/{setting.name=workspace/settings/*}" patch: "/api/v1/{setting.name=instance/settings/*}"
body: "setting" body: "setting"
}; };
option (google.api.method_signature) = "setting,update_mask"; option (google.api.method_signature) = "setting,update_mask";
} }
} }
// Workspace profile message containing basic workspace information. // Instance profile message containing basic instance information.
message WorkspaceProfile { message InstanceProfile {
// The name of instance owner. // The name of instance owner.
// Format: users/{user} // Format: users/{user}
string owner = 1; string owner = 1;
@ -48,20 +48,20 @@ message WorkspaceProfile {
string instance_url = 6; string instance_url = 6;
} }
// Request for workspace profile. // Request for instance profile.
message GetWorkspaceProfileRequest {} message GetInstanceProfileRequest {}
// A workspace setting resource. // An instance setting resource.
message WorkspaceSetting { message InstanceSetting {
option (google.api.resource) = { option (google.api.resource) = {
type: "api.memos.dev/WorkspaceSetting" type: "memos.api.v1/InstanceSetting"
pattern: "workspace/settings/{setting}" pattern: "instance/settings/{setting}"
singular: "workspaceSetting" singular: "instanceSetting"
plural: "workspaceSettings" plural: "instanceSettings"
}; };
// The name of the workspace setting. // The name of the instance setting.
// Format: workspace/settings/{setting} // Format: instance/settings/{setting}
string name = 1 [(google.api.field_behavior) = IDENTIFIER]; string name = 1 [(google.api.field_behavior) = IDENTIFIER];
oneof value { oneof value {
@ -70,7 +70,7 @@ message WorkspaceSetting {
MemoRelatedSetting memo_related_setting = 4; MemoRelatedSetting memo_related_setting = 4;
} }
// Enumeration of workspace setting keys. // Enumeration of instance setting keys.
enum Key { enum Key {
KEY_UNSPECIFIED = 0; KEY_UNSPECIFIED = 0;
// GENERAL is the key for general settings. // GENERAL is the key for general settings.
@ -81,11 +81,8 @@ message WorkspaceSetting {
MEMO_RELATED = 3; MEMO_RELATED = 3;
} }
// General workspace settings configuration. // General instance settings configuration.
message GeneralSetting { message GeneralSetting {
// theme is the name of the selected theme.
// This references a CSS file in the web/public/themes/ directory.
string theme = 1;
// disallow_user_registration disallows user registration. // disallow_user_registration disallows user registration.
bool disallow_user_registration = 2; bool disallow_user_registration = 2;
// disallow_password_auth disallows password authentication. // disallow_password_auth disallows password authentication.
@ -106,16 +103,15 @@ message WorkspaceSetting {
// disallow_change_nickname disallows changing nickname. // disallow_change_nickname disallows changing nickname.
bool disallow_change_nickname = 9; bool disallow_change_nickname = 9;
// Custom profile configuration for workspace branding. // Custom profile configuration for instance branding.
message CustomProfile { message CustomProfile {
string title = 1; string title = 1;
string description = 2; string description = 2;
string logo_url = 3; string logo_url = 3;
string locale = 4;
} }
} }
// Storage configuration settings for workspace attachments. // Storage configuration settings for instance attachments.
message StorageSetting { message StorageSetting {
// Storage type enumeration for different storage backends. // Storage type enumeration for different storage backends.
enum StorageType { enum StorageType {
@ -149,7 +145,7 @@ message WorkspaceSetting {
S3Config s3_config = 4; S3Config s3_config = 4;
} }
// Memo-related workspace settings and policies. // Memo-related instance settings and policies.
message MemoRelatedSetting { message MemoRelatedSetting {
// disallow_public_visibility disallows set memo as public visibility. // disallow_public_visibility disallows set memo as public visibility.
bool disallow_public_visibility = 1; bool disallow_public_visibility = 1;
@ -159,12 +155,8 @@ message WorkspaceSetting {
int32 content_length_limit = 3; int32 content_length_limit = 3;
// enable_double_click_edit enables editing on double click. // enable_double_click_edit enables editing on double click.
bool enable_double_click_edit = 4; bool enable_double_click_edit = 4;
// enable_link_preview enables links preview.
bool enable_link_preview = 5;
// reactions is the list of reactions. // reactions is the list of reactions.
repeated string reactions = 7; repeated string reactions = 7;
// disable_markdown_shortcuts disallow the registration of markdown shortcuts.
bool disable_markdown_shortcuts = 8;
// enable_blur_nsfw_content enables blurring of content marked as not safe for work (NSFW). // enable_blur_nsfw_content enables blurring of content marked as not safe for work (NSFW).
bool enable_blur_nsfw_content = 9; bool enable_blur_nsfw_content = 9;
// nsfw_tags is the list of tags that mark content as NSFW for blurring. // nsfw_tags is the list of tags that mark content as NSFW for blurring.
@ -172,20 +164,20 @@ message WorkspaceSetting {
} }
} }
// Request message for GetWorkspaceSetting method. // Request message for GetInstanceSetting method.
message GetWorkspaceSettingRequest { message GetInstanceSettingRequest {
// The resource name of the workspace setting. // The resource name of the instance setting.
// Format: workspace/settings/{setting} // Format: instance/settings/{setting}
string name = 1 [ string name = 1 [
(google.api.field_behavior) = REQUIRED, (google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {type: "api.memos.dev/WorkspaceSetting"} (google.api.resource_reference) = {type: "memos.api.v1/InstanceSetting"}
]; ];
} }
// Request message for UpdateWorkspaceSetting method. // Request message for UpdateInstanceSetting method.
message UpdateWorkspaceSettingRequest { message UpdateInstanceSettingRequest {
// The workspace setting resource which replaces the resource on the server. // The instance setting resource which replaces the resource on the server.
WorkspaceSetting setting = 1 [(google.api.field_behavior) = REQUIRED]; InstanceSetting setting = 1 [(google.api.field_behavior) = REQUIRED];
// The list of fields to update. // The list of fields to update.
google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = OPTIONAL]; google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = OPTIONAL];

View File

@ -1,331 +0,0 @@
syntax = "proto3";
package memos.api.v1;
import "google/api/annotations.proto";
import "google/api/field_behavior.proto";
option go_package = "gen/api/v1";
service MarkdownService {
// ParseMarkdown parses the given markdown content and returns a list of nodes.
// This is a utility method that transforms markdown text into structured nodes.
rpc ParseMarkdown(ParseMarkdownRequest) returns (ParseMarkdownResponse) {
option (google.api.http) = {
post: "/api/v1/markdown:parse"
body: "*"
};
}
// RestoreMarkdownNodes restores the given nodes to markdown content.
// This is the inverse operation of ParseMarkdown.
rpc RestoreMarkdownNodes(RestoreMarkdownNodesRequest) returns (RestoreMarkdownNodesResponse) {
option (google.api.http) = {
post: "/api/v1/markdown:restore"
body: "*"
};
}
// StringifyMarkdownNodes stringify the given nodes to plain text content.
// This removes all markdown formatting and returns plain text.
rpc StringifyMarkdownNodes(StringifyMarkdownNodesRequest) returns (StringifyMarkdownNodesResponse) {
option (google.api.http) = {
post: "/api/v1/markdown:stringify"
body: "*"
};
}
// GetLinkMetadata returns metadata for a given link.
// This is useful for generating link previews.
rpc GetLinkMetadata(GetLinkMetadataRequest) returns (LinkMetadata) {
option (google.api.http) = {get: "/api/v1/markdown/links:getMetadata"};
}
}
message ParseMarkdownRequest {
// The markdown content to parse.
string markdown = 1 [(google.api.field_behavior) = REQUIRED];
}
message ParseMarkdownResponse {
// The parsed markdown nodes.
repeated Node nodes = 1;
}
message RestoreMarkdownNodesRequest {
// The nodes to restore to markdown content.
repeated Node nodes = 1 [(google.api.field_behavior) = REQUIRED];
}
message RestoreMarkdownNodesResponse {
// The restored markdown content.
string markdown = 1;
}
message StringifyMarkdownNodesRequest {
// The nodes to stringify to plain text.
repeated Node nodes = 1 [(google.api.field_behavior) = REQUIRED];
}
message StringifyMarkdownNodesResponse {
// The plain text content.
string plain_text = 1;
}
message GetLinkMetadataRequest {
// The link URL to get metadata for.
string link = 1 [(google.api.field_behavior) = REQUIRED];
}
message LinkMetadata {
// The title of the linked page.
string title = 1;
// The description of the linked page.
string description = 2;
// The URL of the preview image for the linked page.
string image = 3;
}
enum NodeType {
NODE_UNSPECIFIED = 0;
// Block nodes.
LINE_BREAK = 1;
PARAGRAPH = 2;
CODE_BLOCK = 3;
HEADING = 4;
HORIZONTAL_RULE = 5;
BLOCKQUOTE = 6;
LIST = 7;
ORDERED_LIST_ITEM = 8;
UNORDERED_LIST_ITEM = 9;
TASK_LIST_ITEM = 10;
MATH_BLOCK = 11;
TABLE = 12;
EMBEDDED_CONTENT = 13;
// Inline nodes.
TEXT = 51;
BOLD = 52;
ITALIC = 53;
BOLD_ITALIC = 54;
CODE = 55;
IMAGE = 56;
LINK = 57;
AUTO_LINK = 58;
TAG = 59;
STRIKETHROUGH = 60;
ESCAPING_CHARACTER = 61;
MATH = 62;
HIGHLIGHT = 63;
SUBSCRIPT = 64;
SUPERSCRIPT = 65;
REFERENCED_CONTENT = 66;
SPOILER = 67;
HTML_ELEMENT = 68;
}
message Node {
NodeType type = 1;
oneof node {
// Block nodes.
LineBreakNode line_break_node = 11;
ParagraphNode paragraph_node = 12;
CodeBlockNode code_block_node = 13;
HeadingNode heading_node = 14;
HorizontalRuleNode horizontal_rule_node = 15;
BlockquoteNode blockquote_node = 16;
ListNode list_node = 17;
OrderedListItemNode ordered_list_item_node = 18;
UnorderedListItemNode unordered_list_item_node = 19;
TaskListItemNode task_list_item_node = 20;
MathBlockNode math_block_node = 21;
TableNode table_node = 22;
EmbeddedContentNode embedded_content_node = 23;
// Inline nodes.
TextNode text_node = 51;
BoldNode bold_node = 52;
ItalicNode italic_node = 53;
BoldItalicNode bold_italic_node = 54;
CodeNode code_node = 55;
ImageNode image_node = 56;
LinkNode link_node = 57;
AutoLinkNode auto_link_node = 58;
TagNode tag_node = 59;
StrikethroughNode strikethrough_node = 60;
EscapingCharacterNode escaping_character_node = 61;
MathNode math_node = 62;
HighlightNode highlight_node = 63;
SubscriptNode subscript_node = 64;
SuperscriptNode superscript_node = 65;
ReferencedContentNode referenced_content_node = 66;
SpoilerNode spoiler_node = 67;
HTMLElementNode html_element_node = 68;
}
}
message LineBreakNode {}
message ParagraphNode {
repeated Node children = 1;
}
message CodeBlockNode {
string language = 1;
string content = 2;
}
message HeadingNode {
int32 level = 1;
repeated Node children = 2;
}
message HorizontalRuleNode {
string symbol = 1;
}
message BlockquoteNode {
repeated Node children = 1;
}
message ListNode {
enum Kind {
KIND_UNSPECIFIED = 0;
ORDERED = 1;
UNORDERED = 2;
DESCRIPTION = 3;
}
Kind kind = 1;
int32 indent = 2;
repeated Node children = 3;
}
message OrderedListItemNode {
string number = 1;
int32 indent = 2;
repeated Node children = 3;
}
message UnorderedListItemNode {
string symbol = 1;
int32 indent = 2;
repeated Node children = 3;
}
message TaskListItemNode {
string symbol = 1;
int32 indent = 2;
bool complete = 3;
repeated Node children = 4;
}
message MathBlockNode {
string content = 1;
}
message TableNode {
repeated Node header = 1;
repeated string delimiter = 2;
message Row {
repeated Node cells = 1;
}
repeated Row rows = 3;
}
message EmbeddedContentNode {
// The resource name of the embedded content.
string resource_name = 1;
// Additional parameters for the embedded content.
string params = 2;
}
message TextNode {
string content = 1;
}
message BoldNode {
string symbol = 1;
repeated Node children = 2;
}
message ItalicNode {
string symbol = 1;
repeated Node children = 2;
}
message BoldItalicNode {
string symbol = 1;
string content = 2;
}
message CodeNode {
string content = 1;
}
message ImageNode {
string alt_text = 1;
string url = 2;
}
message LinkNode {
repeated Node content = 1;
string url = 2;
}
message AutoLinkNode {
string url = 1;
bool is_raw_text = 2;
}
message TagNode {
string content = 1;
}
message StrikethroughNode {
string content = 1;
}
message EscapingCharacterNode {
string symbol = 1;
}
message MathNode {
string content = 1;
}
message HighlightNode {
string content = 1;
}
message SubscriptNode {
string content = 1;
}
message SuperscriptNode {
string content = 1;
}
message ReferencedContentNode {
// The resource name of the referenced content.
string resource_name = 1;
// Additional parameters for the referenced content.
string params = 2;
}
message SpoilerNode {
string content = 1;
}
message HTMLElementNode {
string tag_name = 1;
map<string, string> attributes = 2;
repeated Node children = 3;
bool is_self_closing = 4;
}

View File

@ -4,7 +4,6 @@ package memos.api.v1;
import "api/v1/attachment_service.proto"; import "api/v1/attachment_service.proto";
import "api/v1/common.proto"; import "api/v1/common.proto";
import "api/v1/markdown_service.proto";
import "google/api/annotations.proto"; import "google/api/annotations.proto";
import "google/api/client.proto"; import "google/api/client.proto";
import "google/api/field_behavior.proto"; import "google/api/field_behavior.proto";
@ -47,22 +46,6 @@ service MemoService {
option (google.api.http) = {delete: "/api/v1/{name=memos/*}"}; option (google.api.http) = {delete: "/api/v1/{name=memos/*}"};
option (google.api.method_signature) = "name"; option (google.api.method_signature) = "name";
} }
// RenameMemoTag renames a tag for a memo.
rpc RenameMemoTag(RenameMemoTagRequest) returns (google.protobuf.Empty) {
option (google.api.http) = {
patch: "/api/v1/{parent=memos/*}/tags:rename"
body: "*"
};
option (google.api.method_signature) = "parent,old_tag,new_tag";
}
// DeleteMemoTag deletes a tag for a memo.
rpc DeleteMemoTag(DeleteMemoTagRequest) returns (google.protobuf.Empty) {
option (google.api.http) = {
post: "/api/v1/{parent=memos/*}/tags:delete"
body: "*"
};
option (google.api.method_signature) = "parent,tag";
}
// SetMemoAttachments sets attachments for a memo. // SetMemoAttachments sets attachments for a memo.
rpc SetMemoAttachments(SetMemoAttachmentsRequest) returns (google.protobuf.Empty) { rpc SetMemoAttachments(SetMemoAttachmentsRequest) returns (google.protobuf.Empty) {
option (google.api.http) = { option (google.api.http) = {
@ -202,9 +185,6 @@ message Memo {
// Required. The content of the memo in Markdown format. // Required. The content of the memo in Markdown format.
string content = 7 [(google.api.field_behavior) = REQUIRED]; string content = 7 [(google.api.field_behavior) = REQUIRED];
// Output only. The parsed nodes from the content.
repeated Node nodes = 8 [(google.api.field_behavior) = OUTPUT_ONLY];
// The visibility of the memo. // The visibility of the memo.
Visibility visibility = 9 [(google.api.field_behavior) = REQUIRED]; Visibility visibility = 9 [(google.api.field_behavior) = REQUIRED];
@ -266,12 +246,6 @@ message CreateMemoRequest {
// Optional. The memo ID to use for this memo. // Optional. The memo ID to use for this memo.
// If empty, a unique ID will be generated. // If empty, a unique ID will be generated.
string memo_id = 2 [(google.api.field_behavior) = OPTIONAL]; string memo_id = 2 [(google.api.field_behavior) = OPTIONAL];
// Optional. If set, validate the request but don't actually create the memo.
bool validate_only = 3 [(google.api.field_behavior) = OPTIONAL];
// Optional. An idempotency token.
string request_id = 4 [(google.api.field_behavior) = OPTIONAL];
} }
message ListMemosRequest { message ListMemosRequest {
@ -312,9 +286,6 @@ message ListMemosResponse {
// A token that can be sent as `page_token` to retrieve the next page. // A token that can be sent as `page_token` to retrieve the next page.
// If this field is omitted, there are no subsequent pages. // If this field is omitted, there are no subsequent pages.
string next_page_token = 2; string next_page_token = 2;
// The total count of memos (may be approximate).
int32 total_size = 3;
} }
message GetMemoRequest { message GetMemoRequest {
@ -324,10 +295,6 @@ message GetMemoRequest {
(google.api.field_behavior) = REQUIRED, (google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {type: "memos.api.v1/Memo"} (google.api.resource_reference) = {type: "memos.api.v1/Memo"}
]; ];
// Optional. The fields to return in the response.
// If not specified, all fields are returned.
google.protobuf.FieldMask read_mask = 2 [(google.api.field_behavior) = OPTIONAL];
} }
message UpdateMemoRequest { message UpdateMemoRequest {
@ -337,9 +304,6 @@ message UpdateMemoRequest {
// Required. The list of fields to update. // Required. The list of fields to update.
google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = REQUIRED]; google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = REQUIRED];
// Optional. If set to true, allows updating sensitive fields.
bool allow_missing = 3 [(google.api.field_behavior) = OPTIONAL];
} }
message DeleteMemoRequest { message DeleteMemoRequest {
@ -354,36 +318,6 @@ message DeleteMemoRequest {
bool force = 2 [(google.api.field_behavior) = OPTIONAL]; bool force = 2 [(google.api.field_behavior) = OPTIONAL];
} }
message RenameMemoTagRequest {
// Required. The parent, who owns the tags.
// Format: memos/{memo}. Use "memos/-" to rename all tags.
string parent = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {type: "memos.api.v1/Memo"}
];
// Required. The old tag name to rename.
string old_tag = 2 [(google.api.field_behavior) = REQUIRED];
// Required. The new tag name.
string new_tag = 3 [(google.api.field_behavior) = REQUIRED];
}
message DeleteMemoTagRequest {
// Required. The parent, who owns the tags.
// Format: memos/{memo}. Use "memos/-" to delete all tags.
string parent = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {type: "memos.api.v1/Memo"}
];
// Required. The tag name to delete.
string tag = 2 [(google.api.field_behavior) = REQUIRED];
// Optional. Whether to delete related memos.
bool delete_related_memos = 3 [(google.api.field_behavior) = OPTIONAL];
}
message SetMemoAttachmentsRequest { message SetMemoAttachmentsRequest {
// Required. The resource name of the memo. // Required. The resource name of the memo.
// Format: memos/{memo} // Format: memos/{memo}
@ -417,9 +351,6 @@ message ListMemoAttachmentsResponse {
// A token for the next page of results. // A token for the next page of results.
string next_page_token = 2; string next_page_token = 2;
// The total count of attachments.
int32 total_size = 3;
} }
message MemoRelation { message MemoRelation {
@ -484,9 +415,6 @@ message ListMemoRelationsResponse {
// A token for the next page of results. // A token for the next page of results.
string next_page_token = 2; string next_page_token = 2;
// The total count of relations.
int32 total_size = 3;
} }
message CreateMemoCommentRequest { message CreateMemoCommentRequest {

View File

@ -6,7 +6,6 @@ import "api/v1/common.proto";
import "google/api/annotations.proto"; import "google/api/annotations.proto";
import "google/api/client.proto"; import "google/api/client.proto";
import "google/api/field_behavior.proto"; import "google/api/field_behavior.proto";
import "google/api/httpbody.proto";
import "google/api/resource.proto"; import "google/api/resource.proto";
import "google/protobuf/empty.proto"; import "google/protobuf/empty.proto";
import "google/protobuf/field_mask.proto"; import "google/protobuf/field_mask.proto";
@ -53,12 +52,6 @@ service UserService {
option (google.api.method_signature) = "name"; option (google.api.method_signature) = "name";
} }
// GetUserAvatar gets the avatar of a user.
rpc GetUserAvatar(GetUserAvatarRequest) returns (google.api.HttpBody) {
option (google.api.http) = {get: "/api/v1/{name=users/*}/avatar"};
option (google.api.method_signature) = "name";
}
// ListAllUserStats returns statistics for all users. // ListAllUserStats returns statistics for all users.
rpc ListAllUserStats(ListAllUserStatsRequest) returns (ListAllUserStatsResponse) { rpc ListAllUserStats(ListAllUserStatsRequest) returns (ListAllUserStatsResponse) {
option (google.api.http) = {get: "/api/v1/users:stats"}; option (google.api.http) = {get: "/api/v1/users:stats"};
@ -153,6 +146,27 @@ service UserService {
option (google.api.http) = {delete: "/api/v1/{name=users/*/webhooks/*}"}; option (google.api.http) = {delete: "/api/v1/{name=users/*/webhooks/*}"};
option (google.api.method_signature) = "name"; option (google.api.method_signature) = "name";
} }
// ListUserNotifications lists notifications for a user.
rpc ListUserNotifications(ListUserNotificationsRequest) returns (ListUserNotificationsResponse) {
option (google.api.http) = {get: "/api/v1/{parent=users/*}/notifications"};
option (google.api.method_signature) = "parent";
}
// UpdateUserNotification updates a notification.
rpc UpdateUserNotification(UpdateUserNotificationRequest) returns (UserNotification) {
option (google.api.http) = {
patch: "/api/v1/{notification.name=users/*/notifications/*}"
body: "notification"
};
option (google.api.method_signature) = "notification,update_mask";
}
// DeleteUserNotification deletes a notification.
rpc DeleteUserNotification(DeleteUserNotificationRequest) returns (google.protobuf.Empty) {
option (google.api.http) = {delete: "/api/v1/{name=users/*/notifications/*}"};
option (google.api.method_signature) = "name";
}
} }
message User { message User {
@ -303,15 +317,6 @@ message DeleteUserRequest {
bool force = 2 [(google.api.field_behavior) = OPTIONAL]; bool force = 2 [(google.api.field_behavior) = OPTIONAL];
} }
message GetUserAvatarRequest {
// Required. The resource name of the user.
// Format: users/{user}
string name = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {type: "memos.api.v1/User"}
];
}
// User statistics messages // User statistics messages
message UserStats { message UserStats {
option (google.api.resource) = { option (google.api.resource) = {
@ -672,3 +677,81 @@ message DeleteUserWebhookRequest {
// Format: users/{user}/webhooks/{webhook} // Format: users/{user}/webhooks/{webhook}
string name = 1 [(google.api.field_behavior) = REQUIRED]; string name = 1 [(google.api.field_behavior) = REQUIRED];
} }
message UserNotification {
option (google.api.resource) = {
type: "memos.api.v1/UserNotification"
pattern: "users/{user}/notifications/{notification}"
name_field: "name"
singular: "notification"
plural: "notifications"
};
// The resource name of the notification.
// Format: users/{user}/notifications/{notification}
string name = 1 [
(google.api.field_behavior) = OUTPUT_ONLY,
(google.api.field_behavior) = IDENTIFIER
];
// The sender of the notification.
// Format: users/{user}
string sender = 2 [
(google.api.field_behavior) = OUTPUT_ONLY,
(google.api.resource_reference) = {type: "memos.api.v1/User"}
];
// The status of the notification.
Status status = 3 [(google.api.field_behavior) = OPTIONAL];
// The creation timestamp.
google.protobuf.Timestamp create_time = 4 [(google.api.field_behavior) = OUTPUT_ONLY];
// The type of the notification.
Type type = 5 [(google.api.field_behavior) = OUTPUT_ONLY];
// The activity ID associated with this notification.
optional int32 activity_id = 6 [(google.api.field_behavior) = OPTIONAL];
enum Status {
STATUS_UNSPECIFIED = 0;
UNREAD = 1;
ARCHIVED = 2;
}
enum Type {
TYPE_UNSPECIFIED = 0;
MEMO_COMMENT = 1;
}
}
message ListUserNotificationsRequest {
// The parent user resource.
// Format: users/{user}
string parent = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {type: "memos.api.v1/User"}
];
int32 page_size = 2 [(google.api.field_behavior) = OPTIONAL];
string page_token = 3 [(google.api.field_behavior) = OPTIONAL];
string filter = 4 [(google.api.field_behavior) = OPTIONAL];
}
message ListUserNotificationsResponse {
repeated UserNotification notifications = 1;
string next_page_token = 2;
}
message UpdateUserNotificationRequest {
UserNotification notification = 1 [(google.api.field_behavior) = REQUIRED];
google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = REQUIRED];
}
message DeleteUserNotificationRequest {
// Format: users/{user}/notifications/{notification}
string name = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {type: "memos.api.v1/UserNotification"}
];
}

View File

@ -14,19 +14,17 @@ plugins:
- remote: buf.build/grpc/go - remote: buf.build/grpc/go
out: gen out: gen
opt: paths=source_relative opt: paths=source_relative
- remote: buf.build/connectrpc/go
out: gen
opt: paths=source_relative
- remote: buf.build/grpc-ecosystem/gateway - remote: buf.build/grpc-ecosystem/gateway
out: gen out: gen
opt: paths=source_relative opt: paths=source_relative
- remote: buf.build/community/google-gnostic-openapi - remote: buf.build/community/google-gnostic-openapi
out: gen out: gen
opt: paths=source_relative,enum_type=string opt: enum_type=string
- remote: buf.build/community/stephenh-ts-proto - remote: buf.build/bufbuild/es
out: ../web/src/types/proto out: ../web/src/types/proto
opt: opt:
- env=browser - target=ts
- useOptionals=messages include_imports: true
- outputServices=generic-definitions
- outputJsonMethods=false
- useExactTypes=false
- esModuleInterop=true
- stringEnums=true

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.10 // protoc-gen-go v1.36.11
// protoc (unknown) // protoc (unknown)
// source: api/v1/activity_service.proto // source: api/v1/activity_service.proto
@ -31,8 +31,6 @@ const (
Activity_TYPE_UNSPECIFIED Activity_Type = 0 Activity_TYPE_UNSPECIFIED Activity_Type = 0
// Memo comment activity. // Memo comment activity.
Activity_MEMO_COMMENT Activity_Type = 1 Activity_MEMO_COMMENT Activity_Type = 1
// Version update activity.
Activity_VERSION_UPDATE Activity_Type = 2
) )
// Enum value maps for Activity_Type. // Enum value maps for Activity_Type.
@ -40,12 +38,10 @@ var (
Activity_Type_name = map[int32]string{ Activity_Type_name = map[int32]string{
0: "TYPE_UNSPECIFIED", 0: "TYPE_UNSPECIFIED",
1: "MEMO_COMMENT", 1: "MEMO_COMMENT",
2: "VERSION_UPDATE",
} }
Activity_Type_value = map[string]int32{ Activity_Type_value = map[string]int32{
"TYPE_UNSPECIFIED": 0, "TYPE_UNSPECIFIED": 0,
"MEMO_COMMENT": 1, "MEMO_COMMENT": 1,
"VERSION_UPDATE": 2,
} }
) )
@ -513,7 +509,7 @@ var File_api_v1_activity_service_proto protoreflect.FileDescriptor
const file_api_v1_activity_service_proto_rawDesc = "" + const file_api_v1_activity_service_proto_rawDesc = "" +
"\n" + "\n" +
"\x1dapi/v1/activity_service.proto\x12\fmemos.api.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x86\x04\n" + "\x1dapi/v1/activity_service.proto\x12\fmemos.api.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xf2\x03\n" +
"\bActivity\x12\x1a\n" + "\bActivity\x12\x1a\n" +
"\x04name\x18\x01 \x01(\tB\x06\xe0A\x03\xe0A\bR\x04name\x12\x1d\n" + "\x04name\x18\x01 \x01(\tB\x06\xe0A\x03\xe0A\bR\x04name\x12\x1d\n" +
"\acreator\x18\x02 \x01(\tB\x03\xe0A\x03R\acreator\x124\n" + "\acreator\x18\x02 \x01(\tB\x03\xe0A\x03R\acreator\x124\n" +
@ -521,11 +517,10 @@ const file_api_v1_activity_service_proto_rawDesc = "" +
"\x05level\x18\x04 \x01(\x0e2\x1c.memos.api.v1.Activity.LevelB\x03\xe0A\x03R\x05level\x12@\n" + "\x05level\x18\x04 \x01(\x0e2\x1c.memos.api.v1.Activity.LevelB\x03\xe0A\x03R\x05level\x12@\n" +
"\vcreate_time\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampB\x03\xe0A\x03R\n" + "\vcreate_time\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampB\x03\xe0A\x03R\n" +
"createTime\x12<\n" + "createTime\x12<\n" +
"\apayload\x18\x06 \x01(\v2\x1d.memos.api.v1.ActivityPayloadB\x03\xe0A\x03R\apayload\"B\n" + "\apayload\x18\x06 \x01(\v2\x1d.memos.api.v1.ActivityPayloadB\x03\xe0A\x03R\apayload\".\n" +
"\x04Type\x12\x14\n" + "\x04Type\x12\x14\n" +
"\x10TYPE_UNSPECIFIED\x10\x00\x12\x10\n" + "\x10TYPE_UNSPECIFIED\x10\x00\x12\x10\n" +
"\fMEMO_COMMENT\x10\x01\x12\x12\n" + "\fMEMO_COMMENT\x10\x01\"=\n" +
"\x0eVERSION_UPDATE\x10\x02\"=\n" +
"\x05Level\x12\x15\n" + "\x05Level\x12\x15\n" +
"\x11LEVEL_UNSPECIFIED\x10\x00\x12\b\n" + "\x11LEVEL_UNSPECIFIED\x10\x00\x12\b\n" +
"\x04INFO\x10\x01\x12\b\n" + "\x04INFO\x10\x01\x12\b\n" +

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions: // versions:
// - protoc-gen-go-grpc v1.5.1 // - protoc-gen-go-grpc v1.6.0
// - protoc (unknown) // - protoc (unknown)
// source: api/v1/activity_service.proto // source: api/v1/activity_service.proto
@ -80,10 +80,10 @@ type ActivityServiceServer interface {
type UnimplementedActivityServiceServer struct{} type UnimplementedActivityServiceServer struct{}
func (UnimplementedActivityServiceServer) ListActivities(context.Context, *ListActivitiesRequest) (*ListActivitiesResponse, error) { func (UnimplementedActivityServiceServer) ListActivities(context.Context, *ListActivitiesRequest) (*ListActivitiesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListActivities not implemented") return nil, status.Error(codes.Unimplemented, "method ListActivities not implemented")
} }
func (UnimplementedActivityServiceServer) GetActivity(context.Context, *GetActivityRequest) (*Activity, error) { func (UnimplementedActivityServiceServer) GetActivity(context.Context, *GetActivityRequest) (*Activity, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetActivity not implemented") return nil, status.Error(codes.Unimplemented, "method GetActivity not implemented")
} }
func (UnimplementedActivityServiceServer) mustEmbedUnimplementedActivityServiceServer() {} func (UnimplementedActivityServiceServer) mustEmbedUnimplementedActivityServiceServer() {}
func (UnimplementedActivityServiceServer) testEmbeddedByValue() {} func (UnimplementedActivityServiceServer) testEmbeddedByValue() {}
@ -96,7 +96,7 @@ type UnsafeActivityServiceServer interface {
} }
func RegisterActivityServiceServer(s grpc.ServiceRegistrar, srv ActivityServiceServer) { func RegisterActivityServiceServer(s grpc.ServiceRegistrar, srv ActivityServiceServer) {
// If the following call pancis, it indicates UnimplementedActivityServiceServer was // If the following call panics, it indicates UnimplementedActivityServiceServer was
// embedded by pointer and is nil. This will cause panics if an // embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization // unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O. // time to prevent it from happening at runtime later due to I/O.

View File

@ -0,0 +1,142 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: api/v1/activity_service.proto
package apiv1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "github.com/usememos/memos/proto/gen/api/v1"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// ActivityServiceName is the fully-qualified name of the ActivityService service.
ActivityServiceName = "memos.api.v1.ActivityService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// ActivityServiceListActivitiesProcedure is the fully-qualified name of the ActivityService's
// ListActivities RPC.
ActivityServiceListActivitiesProcedure = "/memos.api.v1.ActivityService/ListActivities"
// ActivityServiceGetActivityProcedure is the fully-qualified name of the ActivityService's
// GetActivity RPC.
ActivityServiceGetActivityProcedure = "/memos.api.v1.ActivityService/GetActivity"
)
// ActivityServiceClient is a client for the memos.api.v1.ActivityService service.
type ActivityServiceClient interface {
// ListActivities returns a list of activities.
ListActivities(context.Context, *connect.Request[v1.ListActivitiesRequest]) (*connect.Response[v1.ListActivitiesResponse], error)
// GetActivity returns the activity with the given id.
GetActivity(context.Context, *connect.Request[v1.GetActivityRequest]) (*connect.Response[v1.Activity], error)
}
// NewActivityServiceClient constructs a client for the memos.api.v1.ActivityService service. By
// default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses,
// and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the
// connect.WithGRPC() or connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewActivityServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) ActivityServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
activityServiceMethods := v1.File_api_v1_activity_service_proto.Services().ByName("ActivityService").Methods()
return &activityServiceClient{
listActivities: connect.NewClient[v1.ListActivitiesRequest, v1.ListActivitiesResponse](
httpClient,
baseURL+ActivityServiceListActivitiesProcedure,
connect.WithSchema(activityServiceMethods.ByName("ListActivities")),
connect.WithClientOptions(opts...),
),
getActivity: connect.NewClient[v1.GetActivityRequest, v1.Activity](
httpClient,
baseURL+ActivityServiceGetActivityProcedure,
connect.WithSchema(activityServiceMethods.ByName("GetActivity")),
connect.WithClientOptions(opts...),
),
}
}
// activityServiceClient implements ActivityServiceClient.
type activityServiceClient struct {
listActivities *connect.Client[v1.ListActivitiesRequest, v1.ListActivitiesResponse]
getActivity *connect.Client[v1.GetActivityRequest, v1.Activity]
}
// ListActivities calls memos.api.v1.ActivityService.ListActivities.
func (c *activityServiceClient) ListActivities(ctx context.Context, req *connect.Request[v1.ListActivitiesRequest]) (*connect.Response[v1.ListActivitiesResponse], error) {
return c.listActivities.CallUnary(ctx, req)
}
// GetActivity calls memos.api.v1.ActivityService.GetActivity.
func (c *activityServiceClient) GetActivity(ctx context.Context, req *connect.Request[v1.GetActivityRequest]) (*connect.Response[v1.Activity], error) {
return c.getActivity.CallUnary(ctx, req)
}
// ActivityServiceHandler is an implementation of the memos.api.v1.ActivityService service.
type ActivityServiceHandler interface {
// ListActivities returns a list of activities.
ListActivities(context.Context, *connect.Request[v1.ListActivitiesRequest]) (*connect.Response[v1.ListActivitiesResponse], error)
// GetActivity returns the activity with the given id.
GetActivity(context.Context, *connect.Request[v1.GetActivityRequest]) (*connect.Response[v1.Activity], error)
}
// NewActivityServiceHandler builds an HTTP handler from the service implementation. It returns the
// path on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewActivityServiceHandler(svc ActivityServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
activityServiceMethods := v1.File_api_v1_activity_service_proto.Services().ByName("ActivityService").Methods()
activityServiceListActivitiesHandler := connect.NewUnaryHandler(
ActivityServiceListActivitiesProcedure,
svc.ListActivities,
connect.WithSchema(activityServiceMethods.ByName("ListActivities")),
connect.WithHandlerOptions(opts...),
)
activityServiceGetActivityHandler := connect.NewUnaryHandler(
ActivityServiceGetActivityProcedure,
svc.GetActivity,
connect.WithSchema(activityServiceMethods.ByName("GetActivity")),
connect.WithHandlerOptions(opts...),
)
return "/memos.api.v1.ActivityService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case ActivityServiceListActivitiesProcedure:
activityServiceListActivitiesHandler.ServeHTTP(w, r)
case ActivityServiceGetActivityProcedure:
activityServiceGetActivityHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedActivityServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedActivityServiceHandler struct{}
func (UnimplementedActivityServiceHandler) ListActivities(context.Context, *connect.Request[v1.ListActivitiesRequest]) (*connect.Response[v1.ListActivitiesResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.ActivityService.ListActivities is not implemented"))
}
func (UnimplementedActivityServiceHandler) GetActivity(context.Context, *connect.Request[v1.GetActivityRequest]) (*connect.Response[v1.Activity], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.ActivityService.GetActivity is not implemented"))
}

View File

@ -0,0 +1,236 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: api/v1/attachment_service.proto
package apiv1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "github.com/usememos/memos/proto/gen/api/v1"
emptypb "google.golang.org/protobuf/types/known/emptypb"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// AttachmentServiceName is the fully-qualified name of the AttachmentService service.
AttachmentServiceName = "memos.api.v1.AttachmentService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// AttachmentServiceCreateAttachmentProcedure is the fully-qualified name of the AttachmentService's
// CreateAttachment RPC.
AttachmentServiceCreateAttachmentProcedure = "/memos.api.v1.AttachmentService/CreateAttachment"
// AttachmentServiceListAttachmentsProcedure is the fully-qualified name of the AttachmentService's
// ListAttachments RPC.
AttachmentServiceListAttachmentsProcedure = "/memos.api.v1.AttachmentService/ListAttachments"
// AttachmentServiceGetAttachmentProcedure is the fully-qualified name of the AttachmentService's
// GetAttachment RPC.
AttachmentServiceGetAttachmentProcedure = "/memos.api.v1.AttachmentService/GetAttachment"
// AttachmentServiceUpdateAttachmentProcedure is the fully-qualified name of the AttachmentService's
// UpdateAttachment RPC.
AttachmentServiceUpdateAttachmentProcedure = "/memos.api.v1.AttachmentService/UpdateAttachment"
// AttachmentServiceDeleteAttachmentProcedure is the fully-qualified name of the AttachmentService's
// DeleteAttachment RPC.
AttachmentServiceDeleteAttachmentProcedure = "/memos.api.v1.AttachmentService/DeleteAttachment"
)
// AttachmentServiceClient is a client for the memos.api.v1.AttachmentService service.
type AttachmentServiceClient interface {
// CreateAttachment creates a new attachment.
CreateAttachment(context.Context, *connect.Request[v1.CreateAttachmentRequest]) (*connect.Response[v1.Attachment], error)
// ListAttachments lists all attachments.
ListAttachments(context.Context, *connect.Request[v1.ListAttachmentsRequest]) (*connect.Response[v1.ListAttachmentsResponse], error)
// GetAttachment returns a attachment by name.
GetAttachment(context.Context, *connect.Request[v1.GetAttachmentRequest]) (*connect.Response[v1.Attachment], error)
// UpdateAttachment updates a attachment.
UpdateAttachment(context.Context, *connect.Request[v1.UpdateAttachmentRequest]) (*connect.Response[v1.Attachment], error)
// DeleteAttachment deletes a attachment by name.
DeleteAttachment(context.Context, *connect.Request[v1.DeleteAttachmentRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewAttachmentServiceClient constructs a client for the memos.api.v1.AttachmentService service. By
// default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses,
// and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the
// connect.WithGRPC() or connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewAttachmentServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) AttachmentServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
attachmentServiceMethods := v1.File_api_v1_attachment_service_proto.Services().ByName("AttachmentService").Methods()
return &attachmentServiceClient{
createAttachment: connect.NewClient[v1.CreateAttachmentRequest, v1.Attachment](
httpClient,
baseURL+AttachmentServiceCreateAttachmentProcedure,
connect.WithSchema(attachmentServiceMethods.ByName("CreateAttachment")),
connect.WithClientOptions(opts...),
),
listAttachments: connect.NewClient[v1.ListAttachmentsRequest, v1.ListAttachmentsResponse](
httpClient,
baseURL+AttachmentServiceListAttachmentsProcedure,
connect.WithSchema(attachmentServiceMethods.ByName("ListAttachments")),
connect.WithClientOptions(opts...),
),
getAttachment: connect.NewClient[v1.GetAttachmentRequest, v1.Attachment](
httpClient,
baseURL+AttachmentServiceGetAttachmentProcedure,
connect.WithSchema(attachmentServiceMethods.ByName("GetAttachment")),
connect.WithClientOptions(opts...),
),
updateAttachment: connect.NewClient[v1.UpdateAttachmentRequest, v1.Attachment](
httpClient,
baseURL+AttachmentServiceUpdateAttachmentProcedure,
connect.WithSchema(attachmentServiceMethods.ByName("UpdateAttachment")),
connect.WithClientOptions(opts...),
),
deleteAttachment: connect.NewClient[v1.DeleteAttachmentRequest, emptypb.Empty](
httpClient,
baseURL+AttachmentServiceDeleteAttachmentProcedure,
connect.WithSchema(attachmentServiceMethods.ByName("DeleteAttachment")),
connect.WithClientOptions(opts...),
),
}
}
// attachmentServiceClient implements AttachmentServiceClient.
type attachmentServiceClient struct {
createAttachment *connect.Client[v1.CreateAttachmentRequest, v1.Attachment]
listAttachments *connect.Client[v1.ListAttachmentsRequest, v1.ListAttachmentsResponse]
getAttachment *connect.Client[v1.GetAttachmentRequest, v1.Attachment]
updateAttachment *connect.Client[v1.UpdateAttachmentRequest, v1.Attachment]
deleteAttachment *connect.Client[v1.DeleteAttachmentRequest, emptypb.Empty]
}
// CreateAttachment calls memos.api.v1.AttachmentService.CreateAttachment.
func (c *attachmentServiceClient) CreateAttachment(ctx context.Context, req *connect.Request[v1.CreateAttachmentRequest]) (*connect.Response[v1.Attachment], error) {
return c.createAttachment.CallUnary(ctx, req)
}
// ListAttachments calls memos.api.v1.AttachmentService.ListAttachments.
func (c *attachmentServiceClient) ListAttachments(ctx context.Context, req *connect.Request[v1.ListAttachmentsRequest]) (*connect.Response[v1.ListAttachmentsResponse], error) {
return c.listAttachments.CallUnary(ctx, req)
}
// GetAttachment calls memos.api.v1.AttachmentService.GetAttachment.
func (c *attachmentServiceClient) GetAttachment(ctx context.Context, req *connect.Request[v1.GetAttachmentRequest]) (*connect.Response[v1.Attachment], error) {
return c.getAttachment.CallUnary(ctx, req)
}
// UpdateAttachment calls memos.api.v1.AttachmentService.UpdateAttachment.
func (c *attachmentServiceClient) UpdateAttachment(ctx context.Context, req *connect.Request[v1.UpdateAttachmentRequest]) (*connect.Response[v1.Attachment], error) {
return c.updateAttachment.CallUnary(ctx, req)
}
// DeleteAttachment calls memos.api.v1.AttachmentService.DeleteAttachment.
func (c *attachmentServiceClient) DeleteAttachment(ctx context.Context, req *connect.Request[v1.DeleteAttachmentRequest]) (*connect.Response[emptypb.Empty], error) {
return c.deleteAttachment.CallUnary(ctx, req)
}
// AttachmentServiceHandler is an implementation of the memos.api.v1.AttachmentService service.
type AttachmentServiceHandler interface {
// CreateAttachment creates a new attachment.
CreateAttachment(context.Context, *connect.Request[v1.CreateAttachmentRequest]) (*connect.Response[v1.Attachment], error)
// ListAttachments lists all attachments.
ListAttachments(context.Context, *connect.Request[v1.ListAttachmentsRequest]) (*connect.Response[v1.ListAttachmentsResponse], error)
// GetAttachment returns a attachment by name.
GetAttachment(context.Context, *connect.Request[v1.GetAttachmentRequest]) (*connect.Response[v1.Attachment], error)
// UpdateAttachment updates a attachment.
UpdateAttachment(context.Context, *connect.Request[v1.UpdateAttachmentRequest]) (*connect.Response[v1.Attachment], error)
// DeleteAttachment deletes a attachment by name.
DeleteAttachment(context.Context, *connect.Request[v1.DeleteAttachmentRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewAttachmentServiceHandler builds an HTTP handler from the service implementation. It returns
// the path on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewAttachmentServiceHandler(svc AttachmentServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
attachmentServiceMethods := v1.File_api_v1_attachment_service_proto.Services().ByName("AttachmentService").Methods()
attachmentServiceCreateAttachmentHandler := connect.NewUnaryHandler(
AttachmentServiceCreateAttachmentProcedure,
svc.CreateAttachment,
connect.WithSchema(attachmentServiceMethods.ByName("CreateAttachment")),
connect.WithHandlerOptions(opts...),
)
attachmentServiceListAttachmentsHandler := connect.NewUnaryHandler(
AttachmentServiceListAttachmentsProcedure,
svc.ListAttachments,
connect.WithSchema(attachmentServiceMethods.ByName("ListAttachments")),
connect.WithHandlerOptions(opts...),
)
attachmentServiceGetAttachmentHandler := connect.NewUnaryHandler(
AttachmentServiceGetAttachmentProcedure,
svc.GetAttachment,
connect.WithSchema(attachmentServiceMethods.ByName("GetAttachment")),
connect.WithHandlerOptions(opts...),
)
attachmentServiceUpdateAttachmentHandler := connect.NewUnaryHandler(
AttachmentServiceUpdateAttachmentProcedure,
svc.UpdateAttachment,
connect.WithSchema(attachmentServiceMethods.ByName("UpdateAttachment")),
connect.WithHandlerOptions(opts...),
)
attachmentServiceDeleteAttachmentHandler := connect.NewUnaryHandler(
AttachmentServiceDeleteAttachmentProcedure,
svc.DeleteAttachment,
connect.WithSchema(attachmentServiceMethods.ByName("DeleteAttachment")),
connect.WithHandlerOptions(opts...),
)
return "/memos.api.v1.AttachmentService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case AttachmentServiceCreateAttachmentProcedure:
attachmentServiceCreateAttachmentHandler.ServeHTTP(w, r)
case AttachmentServiceListAttachmentsProcedure:
attachmentServiceListAttachmentsHandler.ServeHTTP(w, r)
case AttachmentServiceGetAttachmentProcedure:
attachmentServiceGetAttachmentHandler.ServeHTTP(w, r)
case AttachmentServiceUpdateAttachmentProcedure:
attachmentServiceUpdateAttachmentHandler.ServeHTTP(w, r)
case AttachmentServiceDeleteAttachmentProcedure:
attachmentServiceDeleteAttachmentHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedAttachmentServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedAttachmentServiceHandler struct{}
func (UnimplementedAttachmentServiceHandler) CreateAttachment(context.Context, *connect.Request[v1.CreateAttachmentRequest]) (*connect.Response[v1.Attachment], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AttachmentService.CreateAttachment is not implemented"))
}
func (UnimplementedAttachmentServiceHandler) ListAttachments(context.Context, *connect.Request[v1.ListAttachmentsRequest]) (*connect.Response[v1.ListAttachmentsResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AttachmentService.ListAttachments is not implemented"))
}
func (UnimplementedAttachmentServiceHandler) GetAttachment(context.Context, *connect.Request[v1.GetAttachmentRequest]) (*connect.Response[v1.Attachment], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AttachmentService.GetAttachment is not implemented"))
}
func (UnimplementedAttachmentServiceHandler) UpdateAttachment(context.Context, *connect.Request[v1.UpdateAttachmentRequest]) (*connect.Response[v1.Attachment], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AttachmentService.UpdateAttachment is not implemented"))
}
func (UnimplementedAttachmentServiceHandler) DeleteAttachment(context.Context, *connect.Request[v1.DeleteAttachmentRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AttachmentService.DeleteAttachment is not implemented"))
}

View File

@ -0,0 +1,180 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: api/v1/auth_service.proto
package apiv1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "github.com/usememos/memos/proto/gen/api/v1"
emptypb "google.golang.org/protobuf/types/known/emptypb"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// AuthServiceName is the fully-qualified name of the AuthService service.
AuthServiceName = "memos.api.v1.AuthService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// AuthServiceGetCurrentSessionProcedure is the fully-qualified name of the AuthService's
// GetCurrentSession RPC.
AuthServiceGetCurrentSessionProcedure = "/memos.api.v1.AuthService/GetCurrentSession"
// AuthServiceCreateSessionProcedure is the fully-qualified name of the AuthService's CreateSession
// RPC.
AuthServiceCreateSessionProcedure = "/memos.api.v1.AuthService/CreateSession"
// AuthServiceDeleteSessionProcedure is the fully-qualified name of the AuthService's DeleteSession
// RPC.
AuthServiceDeleteSessionProcedure = "/memos.api.v1.AuthService/DeleteSession"
)
// AuthServiceClient is a client for the memos.api.v1.AuthService service.
type AuthServiceClient interface {
// GetCurrentSession returns the current active session information.
// This method is idempotent and safe, suitable for checking current session state.
GetCurrentSession(context.Context, *connect.Request[v1.GetCurrentSessionRequest]) (*connect.Response[v1.GetCurrentSessionResponse], error)
// CreateSession authenticates a user and creates a new session.
// Returns the authenticated user information upon successful authentication.
CreateSession(context.Context, *connect.Request[v1.CreateSessionRequest]) (*connect.Response[v1.CreateSessionResponse], error)
// DeleteSession terminates the current user session.
// This is an idempotent operation that invalidates the user's authentication.
DeleteSession(context.Context, *connect.Request[v1.DeleteSessionRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewAuthServiceClient constructs a client for the memos.api.v1.AuthService service. By default, it
// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
// connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewAuthServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) AuthServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
authServiceMethods := v1.File_api_v1_auth_service_proto.Services().ByName("AuthService").Methods()
return &authServiceClient{
getCurrentSession: connect.NewClient[v1.GetCurrentSessionRequest, v1.GetCurrentSessionResponse](
httpClient,
baseURL+AuthServiceGetCurrentSessionProcedure,
connect.WithSchema(authServiceMethods.ByName("GetCurrentSession")),
connect.WithClientOptions(opts...),
),
createSession: connect.NewClient[v1.CreateSessionRequest, v1.CreateSessionResponse](
httpClient,
baseURL+AuthServiceCreateSessionProcedure,
connect.WithSchema(authServiceMethods.ByName("CreateSession")),
connect.WithClientOptions(opts...),
),
deleteSession: connect.NewClient[v1.DeleteSessionRequest, emptypb.Empty](
httpClient,
baseURL+AuthServiceDeleteSessionProcedure,
connect.WithSchema(authServiceMethods.ByName("DeleteSession")),
connect.WithClientOptions(opts...),
),
}
}
// authServiceClient implements AuthServiceClient.
type authServiceClient struct {
getCurrentSession *connect.Client[v1.GetCurrentSessionRequest, v1.GetCurrentSessionResponse]
createSession *connect.Client[v1.CreateSessionRequest, v1.CreateSessionResponse]
deleteSession *connect.Client[v1.DeleteSessionRequest, emptypb.Empty]
}
// GetCurrentSession calls memos.api.v1.AuthService.GetCurrentSession.
func (c *authServiceClient) GetCurrentSession(ctx context.Context, req *connect.Request[v1.GetCurrentSessionRequest]) (*connect.Response[v1.GetCurrentSessionResponse], error) {
return c.getCurrentSession.CallUnary(ctx, req)
}
// CreateSession calls memos.api.v1.AuthService.CreateSession.
func (c *authServiceClient) CreateSession(ctx context.Context, req *connect.Request[v1.CreateSessionRequest]) (*connect.Response[v1.CreateSessionResponse], error) {
return c.createSession.CallUnary(ctx, req)
}
// DeleteSession calls memos.api.v1.AuthService.DeleteSession.
func (c *authServiceClient) DeleteSession(ctx context.Context, req *connect.Request[v1.DeleteSessionRequest]) (*connect.Response[emptypb.Empty], error) {
return c.deleteSession.CallUnary(ctx, req)
}
// AuthServiceHandler is an implementation of the memos.api.v1.AuthService service.
type AuthServiceHandler interface {
// GetCurrentSession returns the current active session information.
// This method is idempotent and safe, suitable for checking current session state.
GetCurrentSession(context.Context, *connect.Request[v1.GetCurrentSessionRequest]) (*connect.Response[v1.GetCurrentSessionResponse], error)
// CreateSession authenticates a user and creates a new session.
// Returns the authenticated user information upon successful authentication.
CreateSession(context.Context, *connect.Request[v1.CreateSessionRequest]) (*connect.Response[v1.CreateSessionResponse], error)
// DeleteSession terminates the current user session.
// This is an idempotent operation that invalidates the user's authentication.
DeleteSession(context.Context, *connect.Request[v1.DeleteSessionRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewAuthServiceHandler builds an HTTP handler from the service implementation. It returns the path
// on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewAuthServiceHandler(svc AuthServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
authServiceMethods := v1.File_api_v1_auth_service_proto.Services().ByName("AuthService").Methods()
authServiceGetCurrentSessionHandler := connect.NewUnaryHandler(
AuthServiceGetCurrentSessionProcedure,
svc.GetCurrentSession,
connect.WithSchema(authServiceMethods.ByName("GetCurrentSession")),
connect.WithHandlerOptions(opts...),
)
authServiceCreateSessionHandler := connect.NewUnaryHandler(
AuthServiceCreateSessionProcedure,
svc.CreateSession,
connect.WithSchema(authServiceMethods.ByName("CreateSession")),
connect.WithHandlerOptions(opts...),
)
authServiceDeleteSessionHandler := connect.NewUnaryHandler(
AuthServiceDeleteSessionProcedure,
svc.DeleteSession,
connect.WithSchema(authServiceMethods.ByName("DeleteSession")),
connect.WithHandlerOptions(opts...),
)
return "/memos.api.v1.AuthService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case AuthServiceGetCurrentSessionProcedure:
authServiceGetCurrentSessionHandler.ServeHTTP(w, r)
case AuthServiceCreateSessionProcedure:
authServiceCreateSessionHandler.ServeHTTP(w, r)
case AuthServiceDeleteSessionProcedure:
authServiceDeleteSessionHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedAuthServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedAuthServiceHandler struct{}
func (UnimplementedAuthServiceHandler) GetCurrentSession(context.Context, *connect.Request[v1.GetCurrentSessionRequest]) (*connect.Response[v1.GetCurrentSessionResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AuthService.GetCurrentSession is not implemented"))
}
func (UnimplementedAuthServiceHandler) CreateSession(context.Context, *connect.Request[v1.CreateSessionRequest]) (*connect.Response[v1.CreateSessionResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AuthService.CreateSession is not implemented"))
}
func (UnimplementedAuthServiceHandler) DeleteSession(context.Context, *connect.Request[v1.DeleteSessionRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.AuthService.DeleteSession is not implemented"))
}

View File

@ -0,0 +1,237 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: api/v1/idp_service.proto
package apiv1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "github.com/usememos/memos/proto/gen/api/v1"
emptypb "google.golang.org/protobuf/types/known/emptypb"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// IdentityProviderServiceName is the fully-qualified name of the IdentityProviderService service.
IdentityProviderServiceName = "memos.api.v1.IdentityProviderService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// IdentityProviderServiceListIdentityProvidersProcedure is the fully-qualified name of the
// IdentityProviderService's ListIdentityProviders RPC.
IdentityProviderServiceListIdentityProvidersProcedure = "/memos.api.v1.IdentityProviderService/ListIdentityProviders"
// IdentityProviderServiceGetIdentityProviderProcedure is the fully-qualified name of the
// IdentityProviderService's GetIdentityProvider RPC.
IdentityProviderServiceGetIdentityProviderProcedure = "/memos.api.v1.IdentityProviderService/GetIdentityProvider"
// IdentityProviderServiceCreateIdentityProviderProcedure is the fully-qualified name of the
// IdentityProviderService's CreateIdentityProvider RPC.
IdentityProviderServiceCreateIdentityProviderProcedure = "/memos.api.v1.IdentityProviderService/CreateIdentityProvider"
// IdentityProviderServiceUpdateIdentityProviderProcedure is the fully-qualified name of the
// IdentityProviderService's UpdateIdentityProvider RPC.
IdentityProviderServiceUpdateIdentityProviderProcedure = "/memos.api.v1.IdentityProviderService/UpdateIdentityProvider"
// IdentityProviderServiceDeleteIdentityProviderProcedure is the fully-qualified name of the
// IdentityProviderService's DeleteIdentityProvider RPC.
IdentityProviderServiceDeleteIdentityProviderProcedure = "/memos.api.v1.IdentityProviderService/DeleteIdentityProvider"
)
// IdentityProviderServiceClient is a client for the memos.api.v1.IdentityProviderService service.
type IdentityProviderServiceClient interface {
// ListIdentityProviders lists identity providers.
ListIdentityProviders(context.Context, *connect.Request[v1.ListIdentityProvidersRequest]) (*connect.Response[v1.ListIdentityProvidersResponse], error)
// GetIdentityProvider gets an identity provider.
GetIdentityProvider(context.Context, *connect.Request[v1.GetIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error)
// CreateIdentityProvider creates an identity provider.
CreateIdentityProvider(context.Context, *connect.Request[v1.CreateIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error)
// UpdateIdentityProvider updates an identity provider.
UpdateIdentityProvider(context.Context, *connect.Request[v1.UpdateIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error)
// DeleteIdentityProvider deletes an identity provider.
DeleteIdentityProvider(context.Context, *connect.Request[v1.DeleteIdentityProviderRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewIdentityProviderServiceClient constructs a client for the memos.api.v1.IdentityProviderService
// service. By default, it uses the Connect protocol with the binary Protobuf Codec, asks for
// gzipped responses, and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply
// the connect.WithGRPC() or connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewIdentityProviderServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) IdentityProviderServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
identityProviderServiceMethods := v1.File_api_v1_idp_service_proto.Services().ByName("IdentityProviderService").Methods()
return &identityProviderServiceClient{
listIdentityProviders: connect.NewClient[v1.ListIdentityProvidersRequest, v1.ListIdentityProvidersResponse](
httpClient,
baseURL+IdentityProviderServiceListIdentityProvidersProcedure,
connect.WithSchema(identityProviderServiceMethods.ByName("ListIdentityProviders")),
connect.WithClientOptions(opts...),
),
getIdentityProvider: connect.NewClient[v1.GetIdentityProviderRequest, v1.IdentityProvider](
httpClient,
baseURL+IdentityProviderServiceGetIdentityProviderProcedure,
connect.WithSchema(identityProviderServiceMethods.ByName("GetIdentityProvider")),
connect.WithClientOptions(opts...),
),
createIdentityProvider: connect.NewClient[v1.CreateIdentityProviderRequest, v1.IdentityProvider](
httpClient,
baseURL+IdentityProviderServiceCreateIdentityProviderProcedure,
connect.WithSchema(identityProviderServiceMethods.ByName("CreateIdentityProvider")),
connect.WithClientOptions(opts...),
),
updateIdentityProvider: connect.NewClient[v1.UpdateIdentityProviderRequest, v1.IdentityProvider](
httpClient,
baseURL+IdentityProviderServiceUpdateIdentityProviderProcedure,
connect.WithSchema(identityProviderServiceMethods.ByName("UpdateIdentityProvider")),
connect.WithClientOptions(opts...),
),
deleteIdentityProvider: connect.NewClient[v1.DeleteIdentityProviderRequest, emptypb.Empty](
httpClient,
baseURL+IdentityProviderServiceDeleteIdentityProviderProcedure,
connect.WithSchema(identityProviderServiceMethods.ByName("DeleteIdentityProvider")),
connect.WithClientOptions(opts...),
),
}
}
// identityProviderServiceClient implements IdentityProviderServiceClient.
type identityProviderServiceClient struct {
listIdentityProviders *connect.Client[v1.ListIdentityProvidersRequest, v1.ListIdentityProvidersResponse]
getIdentityProvider *connect.Client[v1.GetIdentityProviderRequest, v1.IdentityProvider]
createIdentityProvider *connect.Client[v1.CreateIdentityProviderRequest, v1.IdentityProvider]
updateIdentityProvider *connect.Client[v1.UpdateIdentityProviderRequest, v1.IdentityProvider]
deleteIdentityProvider *connect.Client[v1.DeleteIdentityProviderRequest, emptypb.Empty]
}
// ListIdentityProviders calls memos.api.v1.IdentityProviderService.ListIdentityProviders.
func (c *identityProviderServiceClient) ListIdentityProviders(ctx context.Context, req *connect.Request[v1.ListIdentityProvidersRequest]) (*connect.Response[v1.ListIdentityProvidersResponse], error) {
return c.listIdentityProviders.CallUnary(ctx, req)
}
// GetIdentityProvider calls memos.api.v1.IdentityProviderService.GetIdentityProvider.
func (c *identityProviderServiceClient) GetIdentityProvider(ctx context.Context, req *connect.Request[v1.GetIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error) {
return c.getIdentityProvider.CallUnary(ctx, req)
}
// CreateIdentityProvider calls memos.api.v1.IdentityProviderService.CreateIdentityProvider.
func (c *identityProviderServiceClient) CreateIdentityProvider(ctx context.Context, req *connect.Request[v1.CreateIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error) {
return c.createIdentityProvider.CallUnary(ctx, req)
}
// UpdateIdentityProvider calls memos.api.v1.IdentityProviderService.UpdateIdentityProvider.
func (c *identityProviderServiceClient) UpdateIdentityProvider(ctx context.Context, req *connect.Request[v1.UpdateIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error) {
return c.updateIdentityProvider.CallUnary(ctx, req)
}
// DeleteIdentityProvider calls memos.api.v1.IdentityProviderService.DeleteIdentityProvider.
func (c *identityProviderServiceClient) DeleteIdentityProvider(ctx context.Context, req *connect.Request[v1.DeleteIdentityProviderRequest]) (*connect.Response[emptypb.Empty], error) {
return c.deleteIdentityProvider.CallUnary(ctx, req)
}
// IdentityProviderServiceHandler is an implementation of the memos.api.v1.IdentityProviderService
// service.
type IdentityProviderServiceHandler interface {
// ListIdentityProviders lists identity providers.
ListIdentityProviders(context.Context, *connect.Request[v1.ListIdentityProvidersRequest]) (*connect.Response[v1.ListIdentityProvidersResponse], error)
// GetIdentityProvider gets an identity provider.
GetIdentityProvider(context.Context, *connect.Request[v1.GetIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error)
// CreateIdentityProvider creates an identity provider.
CreateIdentityProvider(context.Context, *connect.Request[v1.CreateIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error)
// UpdateIdentityProvider updates an identity provider.
UpdateIdentityProvider(context.Context, *connect.Request[v1.UpdateIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error)
// DeleteIdentityProvider deletes an identity provider.
DeleteIdentityProvider(context.Context, *connect.Request[v1.DeleteIdentityProviderRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewIdentityProviderServiceHandler builds an HTTP handler from the service implementation. It
// returns the path on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewIdentityProviderServiceHandler(svc IdentityProviderServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
identityProviderServiceMethods := v1.File_api_v1_idp_service_proto.Services().ByName("IdentityProviderService").Methods()
identityProviderServiceListIdentityProvidersHandler := connect.NewUnaryHandler(
IdentityProviderServiceListIdentityProvidersProcedure,
svc.ListIdentityProviders,
connect.WithSchema(identityProviderServiceMethods.ByName("ListIdentityProviders")),
connect.WithHandlerOptions(opts...),
)
identityProviderServiceGetIdentityProviderHandler := connect.NewUnaryHandler(
IdentityProviderServiceGetIdentityProviderProcedure,
svc.GetIdentityProvider,
connect.WithSchema(identityProviderServiceMethods.ByName("GetIdentityProvider")),
connect.WithHandlerOptions(opts...),
)
identityProviderServiceCreateIdentityProviderHandler := connect.NewUnaryHandler(
IdentityProviderServiceCreateIdentityProviderProcedure,
svc.CreateIdentityProvider,
connect.WithSchema(identityProviderServiceMethods.ByName("CreateIdentityProvider")),
connect.WithHandlerOptions(opts...),
)
identityProviderServiceUpdateIdentityProviderHandler := connect.NewUnaryHandler(
IdentityProviderServiceUpdateIdentityProviderProcedure,
svc.UpdateIdentityProvider,
connect.WithSchema(identityProviderServiceMethods.ByName("UpdateIdentityProvider")),
connect.WithHandlerOptions(opts...),
)
identityProviderServiceDeleteIdentityProviderHandler := connect.NewUnaryHandler(
IdentityProviderServiceDeleteIdentityProviderProcedure,
svc.DeleteIdentityProvider,
connect.WithSchema(identityProviderServiceMethods.ByName("DeleteIdentityProvider")),
connect.WithHandlerOptions(opts...),
)
return "/memos.api.v1.IdentityProviderService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case IdentityProviderServiceListIdentityProvidersProcedure:
identityProviderServiceListIdentityProvidersHandler.ServeHTTP(w, r)
case IdentityProviderServiceGetIdentityProviderProcedure:
identityProviderServiceGetIdentityProviderHandler.ServeHTTP(w, r)
case IdentityProviderServiceCreateIdentityProviderProcedure:
identityProviderServiceCreateIdentityProviderHandler.ServeHTTP(w, r)
case IdentityProviderServiceUpdateIdentityProviderProcedure:
identityProviderServiceUpdateIdentityProviderHandler.ServeHTTP(w, r)
case IdentityProviderServiceDeleteIdentityProviderProcedure:
identityProviderServiceDeleteIdentityProviderHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedIdentityProviderServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedIdentityProviderServiceHandler struct{}
func (UnimplementedIdentityProviderServiceHandler) ListIdentityProviders(context.Context, *connect.Request[v1.ListIdentityProvidersRequest]) (*connect.Response[v1.ListIdentityProvidersResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.IdentityProviderService.ListIdentityProviders is not implemented"))
}
func (UnimplementedIdentityProviderServiceHandler) GetIdentityProvider(context.Context, *connect.Request[v1.GetIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.IdentityProviderService.GetIdentityProvider is not implemented"))
}
func (UnimplementedIdentityProviderServiceHandler) CreateIdentityProvider(context.Context, *connect.Request[v1.CreateIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.IdentityProviderService.CreateIdentityProvider is not implemented"))
}
func (UnimplementedIdentityProviderServiceHandler) UpdateIdentityProvider(context.Context, *connect.Request[v1.UpdateIdentityProviderRequest]) (*connect.Response[v1.IdentityProvider], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.IdentityProviderService.UpdateIdentityProvider is not implemented"))
}
func (UnimplementedIdentityProviderServiceHandler) DeleteIdentityProvider(context.Context, *connect.Request[v1.DeleteIdentityProviderRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.IdentityProviderService.DeleteIdentityProvider is not implemented"))
}

View File

@ -0,0 +1,173 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: api/v1/instance_service.proto
package apiv1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "github.com/usememos/memos/proto/gen/api/v1"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// InstanceServiceName is the fully-qualified name of the InstanceService service.
InstanceServiceName = "memos.api.v1.InstanceService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// InstanceServiceGetInstanceProfileProcedure is the fully-qualified name of the InstanceService's
// GetInstanceProfile RPC.
InstanceServiceGetInstanceProfileProcedure = "/memos.api.v1.InstanceService/GetInstanceProfile"
// InstanceServiceGetInstanceSettingProcedure is the fully-qualified name of the InstanceService's
// GetInstanceSetting RPC.
InstanceServiceGetInstanceSettingProcedure = "/memos.api.v1.InstanceService/GetInstanceSetting"
// InstanceServiceUpdateInstanceSettingProcedure is the fully-qualified name of the
// InstanceService's UpdateInstanceSetting RPC.
InstanceServiceUpdateInstanceSettingProcedure = "/memos.api.v1.InstanceService/UpdateInstanceSetting"
)
// InstanceServiceClient is a client for the memos.api.v1.InstanceService service.
type InstanceServiceClient interface {
// Gets the instance profile.
GetInstanceProfile(context.Context, *connect.Request[v1.GetInstanceProfileRequest]) (*connect.Response[v1.InstanceProfile], error)
// Gets an instance setting.
GetInstanceSetting(context.Context, *connect.Request[v1.GetInstanceSettingRequest]) (*connect.Response[v1.InstanceSetting], error)
// Updates an instance setting.
UpdateInstanceSetting(context.Context, *connect.Request[v1.UpdateInstanceSettingRequest]) (*connect.Response[v1.InstanceSetting], error)
}
// NewInstanceServiceClient constructs a client for the memos.api.v1.InstanceService service. By
// default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses,
// and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the
// connect.WithGRPC() or connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewInstanceServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) InstanceServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
instanceServiceMethods := v1.File_api_v1_instance_service_proto.Services().ByName("InstanceService").Methods()
return &instanceServiceClient{
getInstanceProfile: connect.NewClient[v1.GetInstanceProfileRequest, v1.InstanceProfile](
httpClient,
baseURL+InstanceServiceGetInstanceProfileProcedure,
connect.WithSchema(instanceServiceMethods.ByName("GetInstanceProfile")),
connect.WithClientOptions(opts...),
),
getInstanceSetting: connect.NewClient[v1.GetInstanceSettingRequest, v1.InstanceSetting](
httpClient,
baseURL+InstanceServiceGetInstanceSettingProcedure,
connect.WithSchema(instanceServiceMethods.ByName("GetInstanceSetting")),
connect.WithClientOptions(opts...),
),
updateInstanceSetting: connect.NewClient[v1.UpdateInstanceSettingRequest, v1.InstanceSetting](
httpClient,
baseURL+InstanceServiceUpdateInstanceSettingProcedure,
connect.WithSchema(instanceServiceMethods.ByName("UpdateInstanceSetting")),
connect.WithClientOptions(opts...),
),
}
}
// instanceServiceClient implements InstanceServiceClient.
type instanceServiceClient struct {
getInstanceProfile *connect.Client[v1.GetInstanceProfileRequest, v1.InstanceProfile]
getInstanceSetting *connect.Client[v1.GetInstanceSettingRequest, v1.InstanceSetting]
updateInstanceSetting *connect.Client[v1.UpdateInstanceSettingRequest, v1.InstanceSetting]
}
// GetInstanceProfile calls memos.api.v1.InstanceService.GetInstanceProfile.
func (c *instanceServiceClient) GetInstanceProfile(ctx context.Context, req *connect.Request[v1.GetInstanceProfileRequest]) (*connect.Response[v1.InstanceProfile], error) {
return c.getInstanceProfile.CallUnary(ctx, req)
}
// GetInstanceSetting calls memos.api.v1.InstanceService.GetInstanceSetting.
func (c *instanceServiceClient) GetInstanceSetting(ctx context.Context, req *connect.Request[v1.GetInstanceSettingRequest]) (*connect.Response[v1.InstanceSetting], error) {
return c.getInstanceSetting.CallUnary(ctx, req)
}
// UpdateInstanceSetting calls memos.api.v1.InstanceService.UpdateInstanceSetting.
func (c *instanceServiceClient) UpdateInstanceSetting(ctx context.Context, req *connect.Request[v1.UpdateInstanceSettingRequest]) (*connect.Response[v1.InstanceSetting], error) {
return c.updateInstanceSetting.CallUnary(ctx, req)
}
// InstanceServiceHandler is an implementation of the memos.api.v1.InstanceService service.
type InstanceServiceHandler interface {
// Gets the instance profile.
GetInstanceProfile(context.Context, *connect.Request[v1.GetInstanceProfileRequest]) (*connect.Response[v1.InstanceProfile], error)
// Gets an instance setting.
GetInstanceSetting(context.Context, *connect.Request[v1.GetInstanceSettingRequest]) (*connect.Response[v1.InstanceSetting], error)
// Updates an instance setting.
UpdateInstanceSetting(context.Context, *connect.Request[v1.UpdateInstanceSettingRequest]) (*connect.Response[v1.InstanceSetting], error)
}
// NewInstanceServiceHandler builds an HTTP handler from the service implementation. It returns the
// path on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewInstanceServiceHandler(svc InstanceServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
instanceServiceMethods := v1.File_api_v1_instance_service_proto.Services().ByName("InstanceService").Methods()
instanceServiceGetInstanceProfileHandler := connect.NewUnaryHandler(
InstanceServiceGetInstanceProfileProcedure,
svc.GetInstanceProfile,
connect.WithSchema(instanceServiceMethods.ByName("GetInstanceProfile")),
connect.WithHandlerOptions(opts...),
)
instanceServiceGetInstanceSettingHandler := connect.NewUnaryHandler(
InstanceServiceGetInstanceSettingProcedure,
svc.GetInstanceSetting,
connect.WithSchema(instanceServiceMethods.ByName("GetInstanceSetting")),
connect.WithHandlerOptions(opts...),
)
instanceServiceUpdateInstanceSettingHandler := connect.NewUnaryHandler(
InstanceServiceUpdateInstanceSettingProcedure,
svc.UpdateInstanceSetting,
connect.WithSchema(instanceServiceMethods.ByName("UpdateInstanceSetting")),
connect.WithHandlerOptions(opts...),
)
return "/memos.api.v1.InstanceService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case InstanceServiceGetInstanceProfileProcedure:
instanceServiceGetInstanceProfileHandler.ServeHTTP(w, r)
case InstanceServiceGetInstanceSettingProcedure:
instanceServiceGetInstanceSettingHandler.ServeHTTP(w, r)
case InstanceServiceUpdateInstanceSettingProcedure:
instanceServiceUpdateInstanceSettingHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedInstanceServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedInstanceServiceHandler struct{}
func (UnimplementedInstanceServiceHandler) GetInstanceProfile(context.Context, *connect.Request[v1.GetInstanceProfileRequest]) (*connect.Response[v1.InstanceProfile], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.InstanceService.GetInstanceProfile is not implemented"))
}
func (UnimplementedInstanceServiceHandler) GetInstanceSetting(context.Context, *connect.Request[v1.GetInstanceSettingRequest]) (*connect.Response[v1.InstanceSetting], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.InstanceService.GetInstanceSetting is not implemented"))
}
func (UnimplementedInstanceServiceHandler) UpdateInstanceSetting(context.Context, *connect.Request[v1.UpdateInstanceSettingRequest]) (*connect.Response[v1.InstanceSetting], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.InstanceService.UpdateInstanceSetting is not implemented"))
}

View File

@ -0,0 +1,510 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: api/v1/memo_service.proto
package apiv1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "github.com/usememos/memos/proto/gen/api/v1"
emptypb "google.golang.org/protobuf/types/known/emptypb"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// MemoServiceName is the fully-qualified name of the MemoService service.
MemoServiceName = "memos.api.v1.MemoService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// MemoServiceCreateMemoProcedure is the fully-qualified name of the MemoService's CreateMemo RPC.
MemoServiceCreateMemoProcedure = "/memos.api.v1.MemoService/CreateMemo"
// MemoServiceListMemosProcedure is the fully-qualified name of the MemoService's ListMemos RPC.
MemoServiceListMemosProcedure = "/memos.api.v1.MemoService/ListMemos"
// MemoServiceGetMemoProcedure is the fully-qualified name of the MemoService's GetMemo RPC.
MemoServiceGetMemoProcedure = "/memos.api.v1.MemoService/GetMemo"
// MemoServiceUpdateMemoProcedure is the fully-qualified name of the MemoService's UpdateMemo RPC.
MemoServiceUpdateMemoProcedure = "/memos.api.v1.MemoService/UpdateMemo"
// MemoServiceDeleteMemoProcedure is the fully-qualified name of the MemoService's DeleteMemo RPC.
MemoServiceDeleteMemoProcedure = "/memos.api.v1.MemoService/DeleteMemo"
// MemoServiceSetMemoAttachmentsProcedure is the fully-qualified name of the MemoService's
// SetMemoAttachments RPC.
MemoServiceSetMemoAttachmentsProcedure = "/memos.api.v1.MemoService/SetMemoAttachments"
// MemoServiceListMemoAttachmentsProcedure is the fully-qualified name of the MemoService's
// ListMemoAttachments RPC.
MemoServiceListMemoAttachmentsProcedure = "/memos.api.v1.MemoService/ListMemoAttachments"
// MemoServiceSetMemoRelationsProcedure is the fully-qualified name of the MemoService's
// SetMemoRelations RPC.
MemoServiceSetMemoRelationsProcedure = "/memos.api.v1.MemoService/SetMemoRelations"
// MemoServiceListMemoRelationsProcedure is the fully-qualified name of the MemoService's
// ListMemoRelations RPC.
MemoServiceListMemoRelationsProcedure = "/memos.api.v1.MemoService/ListMemoRelations"
// MemoServiceCreateMemoCommentProcedure is the fully-qualified name of the MemoService's
// CreateMemoComment RPC.
MemoServiceCreateMemoCommentProcedure = "/memos.api.v1.MemoService/CreateMemoComment"
// MemoServiceListMemoCommentsProcedure is the fully-qualified name of the MemoService's
// ListMemoComments RPC.
MemoServiceListMemoCommentsProcedure = "/memos.api.v1.MemoService/ListMemoComments"
// MemoServiceListMemoReactionsProcedure is the fully-qualified name of the MemoService's
// ListMemoReactions RPC.
MemoServiceListMemoReactionsProcedure = "/memos.api.v1.MemoService/ListMemoReactions"
// MemoServiceUpsertMemoReactionProcedure is the fully-qualified name of the MemoService's
// UpsertMemoReaction RPC.
MemoServiceUpsertMemoReactionProcedure = "/memos.api.v1.MemoService/UpsertMemoReaction"
// MemoServiceDeleteMemoReactionProcedure is the fully-qualified name of the MemoService's
// DeleteMemoReaction RPC.
MemoServiceDeleteMemoReactionProcedure = "/memos.api.v1.MemoService/DeleteMemoReaction"
)
// MemoServiceClient is a client for the memos.api.v1.MemoService service.
type MemoServiceClient interface {
// CreateMemo creates a memo.
CreateMemo(context.Context, *connect.Request[v1.CreateMemoRequest]) (*connect.Response[v1.Memo], error)
// ListMemos lists memos with pagination and filter.
ListMemos(context.Context, *connect.Request[v1.ListMemosRequest]) (*connect.Response[v1.ListMemosResponse], error)
// GetMemo gets a memo.
GetMemo(context.Context, *connect.Request[v1.GetMemoRequest]) (*connect.Response[v1.Memo], error)
// UpdateMemo updates a memo.
UpdateMemo(context.Context, *connect.Request[v1.UpdateMemoRequest]) (*connect.Response[v1.Memo], error)
// DeleteMemo deletes a memo.
DeleteMemo(context.Context, *connect.Request[v1.DeleteMemoRequest]) (*connect.Response[emptypb.Empty], error)
// SetMemoAttachments sets attachments for a memo.
SetMemoAttachments(context.Context, *connect.Request[v1.SetMemoAttachmentsRequest]) (*connect.Response[emptypb.Empty], error)
// ListMemoAttachments lists attachments for a memo.
ListMemoAttachments(context.Context, *connect.Request[v1.ListMemoAttachmentsRequest]) (*connect.Response[v1.ListMemoAttachmentsResponse], error)
// SetMemoRelations sets relations for a memo.
SetMemoRelations(context.Context, *connect.Request[v1.SetMemoRelationsRequest]) (*connect.Response[emptypb.Empty], error)
// ListMemoRelations lists relations for a memo.
ListMemoRelations(context.Context, *connect.Request[v1.ListMemoRelationsRequest]) (*connect.Response[v1.ListMemoRelationsResponse], error)
// CreateMemoComment creates a comment for a memo.
CreateMemoComment(context.Context, *connect.Request[v1.CreateMemoCommentRequest]) (*connect.Response[v1.Memo], error)
// ListMemoComments lists comments for a memo.
ListMemoComments(context.Context, *connect.Request[v1.ListMemoCommentsRequest]) (*connect.Response[v1.ListMemoCommentsResponse], error)
// ListMemoReactions lists reactions for a memo.
ListMemoReactions(context.Context, *connect.Request[v1.ListMemoReactionsRequest]) (*connect.Response[v1.ListMemoReactionsResponse], error)
// UpsertMemoReaction upserts a reaction for a memo.
UpsertMemoReaction(context.Context, *connect.Request[v1.UpsertMemoReactionRequest]) (*connect.Response[v1.Reaction], error)
// DeleteMemoReaction deletes a reaction for a memo.
DeleteMemoReaction(context.Context, *connect.Request[v1.DeleteMemoReactionRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewMemoServiceClient constructs a client for the memos.api.v1.MemoService service. By default, it
// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
// connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewMemoServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) MemoServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
memoServiceMethods := v1.File_api_v1_memo_service_proto.Services().ByName("MemoService").Methods()
return &memoServiceClient{
createMemo: connect.NewClient[v1.CreateMemoRequest, v1.Memo](
httpClient,
baseURL+MemoServiceCreateMemoProcedure,
connect.WithSchema(memoServiceMethods.ByName("CreateMemo")),
connect.WithClientOptions(opts...),
),
listMemos: connect.NewClient[v1.ListMemosRequest, v1.ListMemosResponse](
httpClient,
baseURL+MemoServiceListMemosProcedure,
connect.WithSchema(memoServiceMethods.ByName("ListMemos")),
connect.WithClientOptions(opts...),
),
getMemo: connect.NewClient[v1.GetMemoRequest, v1.Memo](
httpClient,
baseURL+MemoServiceGetMemoProcedure,
connect.WithSchema(memoServiceMethods.ByName("GetMemo")),
connect.WithClientOptions(opts...),
),
updateMemo: connect.NewClient[v1.UpdateMemoRequest, v1.Memo](
httpClient,
baseURL+MemoServiceUpdateMemoProcedure,
connect.WithSchema(memoServiceMethods.ByName("UpdateMemo")),
connect.WithClientOptions(opts...),
),
deleteMemo: connect.NewClient[v1.DeleteMemoRequest, emptypb.Empty](
httpClient,
baseURL+MemoServiceDeleteMemoProcedure,
connect.WithSchema(memoServiceMethods.ByName("DeleteMemo")),
connect.WithClientOptions(opts...),
),
setMemoAttachments: connect.NewClient[v1.SetMemoAttachmentsRequest, emptypb.Empty](
httpClient,
baseURL+MemoServiceSetMemoAttachmentsProcedure,
connect.WithSchema(memoServiceMethods.ByName("SetMemoAttachments")),
connect.WithClientOptions(opts...),
),
listMemoAttachments: connect.NewClient[v1.ListMemoAttachmentsRequest, v1.ListMemoAttachmentsResponse](
httpClient,
baseURL+MemoServiceListMemoAttachmentsProcedure,
connect.WithSchema(memoServiceMethods.ByName("ListMemoAttachments")),
connect.WithClientOptions(opts...),
),
setMemoRelations: connect.NewClient[v1.SetMemoRelationsRequest, emptypb.Empty](
httpClient,
baseURL+MemoServiceSetMemoRelationsProcedure,
connect.WithSchema(memoServiceMethods.ByName("SetMemoRelations")),
connect.WithClientOptions(opts...),
),
listMemoRelations: connect.NewClient[v1.ListMemoRelationsRequest, v1.ListMemoRelationsResponse](
httpClient,
baseURL+MemoServiceListMemoRelationsProcedure,
connect.WithSchema(memoServiceMethods.ByName("ListMemoRelations")),
connect.WithClientOptions(opts...),
),
createMemoComment: connect.NewClient[v1.CreateMemoCommentRequest, v1.Memo](
httpClient,
baseURL+MemoServiceCreateMemoCommentProcedure,
connect.WithSchema(memoServiceMethods.ByName("CreateMemoComment")),
connect.WithClientOptions(opts...),
),
listMemoComments: connect.NewClient[v1.ListMemoCommentsRequest, v1.ListMemoCommentsResponse](
httpClient,
baseURL+MemoServiceListMemoCommentsProcedure,
connect.WithSchema(memoServiceMethods.ByName("ListMemoComments")),
connect.WithClientOptions(opts...),
),
listMemoReactions: connect.NewClient[v1.ListMemoReactionsRequest, v1.ListMemoReactionsResponse](
httpClient,
baseURL+MemoServiceListMemoReactionsProcedure,
connect.WithSchema(memoServiceMethods.ByName("ListMemoReactions")),
connect.WithClientOptions(opts...),
),
upsertMemoReaction: connect.NewClient[v1.UpsertMemoReactionRequest, v1.Reaction](
httpClient,
baseURL+MemoServiceUpsertMemoReactionProcedure,
connect.WithSchema(memoServiceMethods.ByName("UpsertMemoReaction")),
connect.WithClientOptions(opts...),
),
deleteMemoReaction: connect.NewClient[v1.DeleteMemoReactionRequest, emptypb.Empty](
httpClient,
baseURL+MemoServiceDeleteMemoReactionProcedure,
connect.WithSchema(memoServiceMethods.ByName("DeleteMemoReaction")),
connect.WithClientOptions(opts...),
),
}
}
// memoServiceClient implements MemoServiceClient.
type memoServiceClient struct {
createMemo *connect.Client[v1.CreateMemoRequest, v1.Memo]
listMemos *connect.Client[v1.ListMemosRequest, v1.ListMemosResponse]
getMemo *connect.Client[v1.GetMemoRequest, v1.Memo]
updateMemo *connect.Client[v1.UpdateMemoRequest, v1.Memo]
deleteMemo *connect.Client[v1.DeleteMemoRequest, emptypb.Empty]
setMemoAttachments *connect.Client[v1.SetMemoAttachmentsRequest, emptypb.Empty]
listMemoAttachments *connect.Client[v1.ListMemoAttachmentsRequest, v1.ListMemoAttachmentsResponse]
setMemoRelations *connect.Client[v1.SetMemoRelationsRequest, emptypb.Empty]
listMemoRelations *connect.Client[v1.ListMemoRelationsRequest, v1.ListMemoRelationsResponse]
createMemoComment *connect.Client[v1.CreateMemoCommentRequest, v1.Memo]
listMemoComments *connect.Client[v1.ListMemoCommentsRequest, v1.ListMemoCommentsResponse]
listMemoReactions *connect.Client[v1.ListMemoReactionsRequest, v1.ListMemoReactionsResponse]
upsertMemoReaction *connect.Client[v1.UpsertMemoReactionRequest, v1.Reaction]
deleteMemoReaction *connect.Client[v1.DeleteMemoReactionRequest, emptypb.Empty]
}
// CreateMemo calls memos.api.v1.MemoService.CreateMemo.
func (c *memoServiceClient) CreateMemo(ctx context.Context, req *connect.Request[v1.CreateMemoRequest]) (*connect.Response[v1.Memo], error) {
return c.createMemo.CallUnary(ctx, req)
}
// ListMemos calls memos.api.v1.MemoService.ListMemos.
func (c *memoServiceClient) ListMemos(ctx context.Context, req *connect.Request[v1.ListMemosRequest]) (*connect.Response[v1.ListMemosResponse], error) {
return c.listMemos.CallUnary(ctx, req)
}
// GetMemo calls memos.api.v1.MemoService.GetMemo.
func (c *memoServiceClient) GetMemo(ctx context.Context, req *connect.Request[v1.GetMemoRequest]) (*connect.Response[v1.Memo], error) {
return c.getMemo.CallUnary(ctx, req)
}
// UpdateMemo calls memos.api.v1.MemoService.UpdateMemo.
func (c *memoServiceClient) UpdateMemo(ctx context.Context, req *connect.Request[v1.UpdateMemoRequest]) (*connect.Response[v1.Memo], error) {
return c.updateMemo.CallUnary(ctx, req)
}
// DeleteMemo calls memos.api.v1.MemoService.DeleteMemo.
func (c *memoServiceClient) DeleteMemo(ctx context.Context, req *connect.Request[v1.DeleteMemoRequest]) (*connect.Response[emptypb.Empty], error) {
return c.deleteMemo.CallUnary(ctx, req)
}
// SetMemoAttachments calls memos.api.v1.MemoService.SetMemoAttachments.
func (c *memoServiceClient) SetMemoAttachments(ctx context.Context, req *connect.Request[v1.SetMemoAttachmentsRequest]) (*connect.Response[emptypb.Empty], error) {
return c.setMemoAttachments.CallUnary(ctx, req)
}
// ListMemoAttachments calls memos.api.v1.MemoService.ListMemoAttachments.
func (c *memoServiceClient) ListMemoAttachments(ctx context.Context, req *connect.Request[v1.ListMemoAttachmentsRequest]) (*connect.Response[v1.ListMemoAttachmentsResponse], error) {
return c.listMemoAttachments.CallUnary(ctx, req)
}
// SetMemoRelations calls memos.api.v1.MemoService.SetMemoRelations.
func (c *memoServiceClient) SetMemoRelations(ctx context.Context, req *connect.Request[v1.SetMemoRelationsRequest]) (*connect.Response[emptypb.Empty], error) {
return c.setMemoRelations.CallUnary(ctx, req)
}
// ListMemoRelations calls memos.api.v1.MemoService.ListMemoRelations.
func (c *memoServiceClient) ListMemoRelations(ctx context.Context, req *connect.Request[v1.ListMemoRelationsRequest]) (*connect.Response[v1.ListMemoRelationsResponse], error) {
return c.listMemoRelations.CallUnary(ctx, req)
}
// CreateMemoComment calls memos.api.v1.MemoService.CreateMemoComment.
func (c *memoServiceClient) CreateMemoComment(ctx context.Context, req *connect.Request[v1.CreateMemoCommentRequest]) (*connect.Response[v1.Memo], error) {
return c.createMemoComment.CallUnary(ctx, req)
}
// ListMemoComments calls memos.api.v1.MemoService.ListMemoComments.
func (c *memoServiceClient) ListMemoComments(ctx context.Context, req *connect.Request[v1.ListMemoCommentsRequest]) (*connect.Response[v1.ListMemoCommentsResponse], error) {
return c.listMemoComments.CallUnary(ctx, req)
}
// ListMemoReactions calls memos.api.v1.MemoService.ListMemoReactions.
func (c *memoServiceClient) ListMemoReactions(ctx context.Context, req *connect.Request[v1.ListMemoReactionsRequest]) (*connect.Response[v1.ListMemoReactionsResponse], error) {
return c.listMemoReactions.CallUnary(ctx, req)
}
// UpsertMemoReaction calls memos.api.v1.MemoService.UpsertMemoReaction.
func (c *memoServiceClient) UpsertMemoReaction(ctx context.Context, req *connect.Request[v1.UpsertMemoReactionRequest]) (*connect.Response[v1.Reaction], error) {
return c.upsertMemoReaction.CallUnary(ctx, req)
}
// DeleteMemoReaction calls memos.api.v1.MemoService.DeleteMemoReaction.
func (c *memoServiceClient) DeleteMemoReaction(ctx context.Context, req *connect.Request[v1.DeleteMemoReactionRequest]) (*connect.Response[emptypb.Empty], error) {
return c.deleteMemoReaction.CallUnary(ctx, req)
}
// MemoServiceHandler is an implementation of the memos.api.v1.MemoService service.
type MemoServiceHandler interface {
// CreateMemo creates a memo.
CreateMemo(context.Context, *connect.Request[v1.CreateMemoRequest]) (*connect.Response[v1.Memo], error)
// ListMemos lists memos with pagination and filter.
ListMemos(context.Context, *connect.Request[v1.ListMemosRequest]) (*connect.Response[v1.ListMemosResponse], error)
// GetMemo gets a memo.
GetMemo(context.Context, *connect.Request[v1.GetMemoRequest]) (*connect.Response[v1.Memo], error)
// UpdateMemo updates a memo.
UpdateMemo(context.Context, *connect.Request[v1.UpdateMemoRequest]) (*connect.Response[v1.Memo], error)
// DeleteMemo deletes a memo.
DeleteMemo(context.Context, *connect.Request[v1.DeleteMemoRequest]) (*connect.Response[emptypb.Empty], error)
// SetMemoAttachments sets attachments for a memo.
SetMemoAttachments(context.Context, *connect.Request[v1.SetMemoAttachmentsRequest]) (*connect.Response[emptypb.Empty], error)
// ListMemoAttachments lists attachments for a memo.
ListMemoAttachments(context.Context, *connect.Request[v1.ListMemoAttachmentsRequest]) (*connect.Response[v1.ListMemoAttachmentsResponse], error)
// SetMemoRelations sets relations for a memo.
SetMemoRelations(context.Context, *connect.Request[v1.SetMemoRelationsRequest]) (*connect.Response[emptypb.Empty], error)
// ListMemoRelations lists relations for a memo.
ListMemoRelations(context.Context, *connect.Request[v1.ListMemoRelationsRequest]) (*connect.Response[v1.ListMemoRelationsResponse], error)
// CreateMemoComment creates a comment for a memo.
CreateMemoComment(context.Context, *connect.Request[v1.CreateMemoCommentRequest]) (*connect.Response[v1.Memo], error)
// ListMemoComments lists comments for a memo.
ListMemoComments(context.Context, *connect.Request[v1.ListMemoCommentsRequest]) (*connect.Response[v1.ListMemoCommentsResponse], error)
// ListMemoReactions lists reactions for a memo.
ListMemoReactions(context.Context, *connect.Request[v1.ListMemoReactionsRequest]) (*connect.Response[v1.ListMemoReactionsResponse], error)
// UpsertMemoReaction upserts a reaction for a memo.
UpsertMemoReaction(context.Context, *connect.Request[v1.UpsertMemoReactionRequest]) (*connect.Response[v1.Reaction], error)
// DeleteMemoReaction deletes a reaction for a memo.
DeleteMemoReaction(context.Context, *connect.Request[v1.DeleteMemoReactionRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewMemoServiceHandler builds an HTTP handler from the service implementation. It returns the path
// on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewMemoServiceHandler(svc MemoServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
memoServiceMethods := v1.File_api_v1_memo_service_proto.Services().ByName("MemoService").Methods()
memoServiceCreateMemoHandler := connect.NewUnaryHandler(
MemoServiceCreateMemoProcedure,
svc.CreateMemo,
connect.WithSchema(memoServiceMethods.ByName("CreateMemo")),
connect.WithHandlerOptions(opts...),
)
memoServiceListMemosHandler := connect.NewUnaryHandler(
MemoServiceListMemosProcedure,
svc.ListMemos,
connect.WithSchema(memoServiceMethods.ByName("ListMemos")),
connect.WithHandlerOptions(opts...),
)
memoServiceGetMemoHandler := connect.NewUnaryHandler(
MemoServiceGetMemoProcedure,
svc.GetMemo,
connect.WithSchema(memoServiceMethods.ByName("GetMemo")),
connect.WithHandlerOptions(opts...),
)
memoServiceUpdateMemoHandler := connect.NewUnaryHandler(
MemoServiceUpdateMemoProcedure,
svc.UpdateMemo,
connect.WithSchema(memoServiceMethods.ByName("UpdateMemo")),
connect.WithHandlerOptions(opts...),
)
memoServiceDeleteMemoHandler := connect.NewUnaryHandler(
MemoServiceDeleteMemoProcedure,
svc.DeleteMemo,
connect.WithSchema(memoServiceMethods.ByName("DeleteMemo")),
connect.WithHandlerOptions(opts...),
)
memoServiceSetMemoAttachmentsHandler := connect.NewUnaryHandler(
MemoServiceSetMemoAttachmentsProcedure,
svc.SetMemoAttachments,
connect.WithSchema(memoServiceMethods.ByName("SetMemoAttachments")),
connect.WithHandlerOptions(opts...),
)
memoServiceListMemoAttachmentsHandler := connect.NewUnaryHandler(
MemoServiceListMemoAttachmentsProcedure,
svc.ListMemoAttachments,
connect.WithSchema(memoServiceMethods.ByName("ListMemoAttachments")),
connect.WithHandlerOptions(opts...),
)
memoServiceSetMemoRelationsHandler := connect.NewUnaryHandler(
MemoServiceSetMemoRelationsProcedure,
svc.SetMemoRelations,
connect.WithSchema(memoServiceMethods.ByName("SetMemoRelations")),
connect.WithHandlerOptions(opts...),
)
memoServiceListMemoRelationsHandler := connect.NewUnaryHandler(
MemoServiceListMemoRelationsProcedure,
svc.ListMemoRelations,
connect.WithSchema(memoServiceMethods.ByName("ListMemoRelations")),
connect.WithHandlerOptions(opts...),
)
memoServiceCreateMemoCommentHandler := connect.NewUnaryHandler(
MemoServiceCreateMemoCommentProcedure,
svc.CreateMemoComment,
connect.WithSchema(memoServiceMethods.ByName("CreateMemoComment")),
connect.WithHandlerOptions(opts...),
)
memoServiceListMemoCommentsHandler := connect.NewUnaryHandler(
MemoServiceListMemoCommentsProcedure,
svc.ListMemoComments,
connect.WithSchema(memoServiceMethods.ByName("ListMemoComments")),
connect.WithHandlerOptions(opts...),
)
memoServiceListMemoReactionsHandler := connect.NewUnaryHandler(
MemoServiceListMemoReactionsProcedure,
svc.ListMemoReactions,
connect.WithSchema(memoServiceMethods.ByName("ListMemoReactions")),
connect.WithHandlerOptions(opts...),
)
memoServiceUpsertMemoReactionHandler := connect.NewUnaryHandler(
MemoServiceUpsertMemoReactionProcedure,
svc.UpsertMemoReaction,
connect.WithSchema(memoServiceMethods.ByName("UpsertMemoReaction")),
connect.WithHandlerOptions(opts...),
)
memoServiceDeleteMemoReactionHandler := connect.NewUnaryHandler(
MemoServiceDeleteMemoReactionProcedure,
svc.DeleteMemoReaction,
connect.WithSchema(memoServiceMethods.ByName("DeleteMemoReaction")),
connect.WithHandlerOptions(opts...),
)
return "/memos.api.v1.MemoService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case MemoServiceCreateMemoProcedure:
memoServiceCreateMemoHandler.ServeHTTP(w, r)
case MemoServiceListMemosProcedure:
memoServiceListMemosHandler.ServeHTTP(w, r)
case MemoServiceGetMemoProcedure:
memoServiceGetMemoHandler.ServeHTTP(w, r)
case MemoServiceUpdateMemoProcedure:
memoServiceUpdateMemoHandler.ServeHTTP(w, r)
case MemoServiceDeleteMemoProcedure:
memoServiceDeleteMemoHandler.ServeHTTP(w, r)
case MemoServiceSetMemoAttachmentsProcedure:
memoServiceSetMemoAttachmentsHandler.ServeHTTP(w, r)
case MemoServiceListMemoAttachmentsProcedure:
memoServiceListMemoAttachmentsHandler.ServeHTTP(w, r)
case MemoServiceSetMemoRelationsProcedure:
memoServiceSetMemoRelationsHandler.ServeHTTP(w, r)
case MemoServiceListMemoRelationsProcedure:
memoServiceListMemoRelationsHandler.ServeHTTP(w, r)
case MemoServiceCreateMemoCommentProcedure:
memoServiceCreateMemoCommentHandler.ServeHTTP(w, r)
case MemoServiceListMemoCommentsProcedure:
memoServiceListMemoCommentsHandler.ServeHTTP(w, r)
case MemoServiceListMemoReactionsProcedure:
memoServiceListMemoReactionsHandler.ServeHTTP(w, r)
case MemoServiceUpsertMemoReactionProcedure:
memoServiceUpsertMemoReactionHandler.ServeHTTP(w, r)
case MemoServiceDeleteMemoReactionProcedure:
memoServiceDeleteMemoReactionHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedMemoServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedMemoServiceHandler struct{}
func (UnimplementedMemoServiceHandler) CreateMemo(context.Context, *connect.Request[v1.CreateMemoRequest]) (*connect.Response[v1.Memo], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.CreateMemo is not implemented"))
}
func (UnimplementedMemoServiceHandler) ListMemos(context.Context, *connect.Request[v1.ListMemosRequest]) (*connect.Response[v1.ListMemosResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.ListMemos is not implemented"))
}
func (UnimplementedMemoServiceHandler) GetMemo(context.Context, *connect.Request[v1.GetMemoRequest]) (*connect.Response[v1.Memo], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.GetMemo is not implemented"))
}
func (UnimplementedMemoServiceHandler) UpdateMemo(context.Context, *connect.Request[v1.UpdateMemoRequest]) (*connect.Response[v1.Memo], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.UpdateMemo is not implemented"))
}
func (UnimplementedMemoServiceHandler) DeleteMemo(context.Context, *connect.Request[v1.DeleteMemoRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.DeleteMemo is not implemented"))
}
func (UnimplementedMemoServiceHandler) SetMemoAttachments(context.Context, *connect.Request[v1.SetMemoAttachmentsRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.SetMemoAttachments is not implemented"))
}
func (UnimplementedMemoServiceHandler) ListMemoAttachments(context.Context, *connect.Request[v1.ListMemoAttachmentsRequest]) (*connect.Response[v1.ListMemoAttachmentsResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.ListMemoAttachments is not implemented"))
}
func (UnimplementedMemoServiceHandler) SetMemoRelations(context.Context, *connect.Request[v1.SetMemoRelationsRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.SetMemoRelations is not implemented"))
}
func (UnimplementedMemoServiceHandler) ListMemoRelations(context.Context, *connect.Request[v1.ListMemoRelationsRequest]) (*connect.Response[v1.ListMemoRelationsResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.ListMemoRelations is not implemented"))
}
func (UnimplementedMemoServiceHandler) CreateMemoComment(context.Context, *connect.Request[v1.CreateMemoCommentRequest]) (*connect.Response[v1.Memo], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.CreateMemoComment is not implemented"))
}
func (UnimplementedMemoServiceHandler) ListMemoComments(context.Context, *connect.Request[v1.ListMemoCommentsRequest]) (*connect.Response[v1.ListMemoCommentsResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.ListMemoComments is not implemented"))
}
func (UnimplementedMemoServiceHandler) ListMemoReactions(context.Context, *connect.Request[v1.ListMemoReactionsRequest]) (*connect.Response[v1.ListMemoReactionsResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.ListMemoReactions is not implemented"))
}
func (UnimplementedMemoServiceHandler) UpsertMemoReaction(context.Context, *connect.Request[v1.UpsertMemoReactionRequest]) (*connect.Response[v1.Reaction], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.UpsertMemoReaction is not implemented"))
}
func (UnimplementedMemoServiceHandler) DeleteMemoReaction(context.Context, *connect.Request[v1.DeleteMemoReactionRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.MemoService.DeleteMemoReaction is not implemented"))
}

View File

@ -0,0 +1,236 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: api/v1/shortcut_service.proto
package apiv1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "github.com/usememos/memos/proto/gen/api/v1"
emptypb "google.golang.org/protobuf/types/known/emptypb"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// ShortcutServiceName is the fully-qualified name of the ShortcutService service.
ShortcutServiceName = "memos.api.v1.ShortcutService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// ShortcutServiceListShortcutsProcedure is the fully-qualified name of the ShortcutService's
// ListShortcuts RPC.
ShortcutServiceListShortcutsProcedure = "/memos.api.v1.ShortcutService/ListShortcuts"
// ShortcutServiceGetShortcutProcedure is the fully-qualified name of the ShortcutService's
// GetShortcut RPC.
ShortcutServiceGetShortcutProcedure = "/memos.api.v1.ShortcutService/GetShortcut"
// ShortcutServiceCreateShortcutProcedure is the fully-qualified name of the ShortcutService's
// CreateShortcut RPC.
ShortcutServiceCreateShortcutProcedure = "/memos.api.v1.ShortcutService/CreateShortcut"
// ShortcutServiceUpdateShortcutProcedure is the fully-qualified name of the ShortcutService's
// UpdateShortcut RPC.
ShortcutServiceUpdateShortcutProcedure = "/memos.api.v1.ShortcutService/UpdateShortcut"
// ShortcutServiceDeleteShortcutProcedure is the fully-qualified name of the ShortcutService's
// DeleteShortcut RPC.
ShortcutServiceDeleteShortcutProcedure = "/memos.api.v1.ShortcutService/DeleteShortcut"
)
// ShortcutServiceClient is a client for the memos.api.v1.ShortcutService service.
type ShortcutServiceClient interface {
// ListShortcuts returns a list of shortcuts for a user.
ListShortcuts(context.Context, *connect.Request[v1.ListShortcutsRequest]) (*connect.Response[v1.ListShortcutsResponse], error)
// GetShortcut gets a shortcut by name.
GetShortcut(context.Context, *connect.Request[v1.GetShortcutRequest]) (*connect.Response[v1.Shortcut], error)
// CreateShortcut creates a new shortcut for a user.
CreateShortcut(context.Context, *connect.Request[v1.CreateShortcutRequest]) (*connect.Response[v1.Shortcut], error)
// UpdateShortcut updates a shortcut for a user.
UpdateShortcut(context.Context, *connect.Request[v1.UpdateShortcutRequest]) (*connect.Response[v1.Shortcut], error)
// DeleteShortcut deletes a shortcut for a user.
DeleteShortcut(context.Context, *connect.Request[v1.DeleteShortcutRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewShortcutServiceClient constructs a client for the memos.api.v1.ShortcutService service. By
// default, it uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses,
// and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the
// connect.WithGRPC() or connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewShortcutServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) ShortcutServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
shortcutServiceMethods := v1.File_api_v1_shortcut_service_proto.Services().ByName("ShortcutService").Methods()
return &shortcutServiceClient{
listShortcuts: connect.NewClient[v1.ListShortcutsRequest, v1.ListShortcutsResponse](
httpClient,
baseURL+ShortcutServiceListShortcutsProcedure,
connect.WithSchema(shortcutServiceMethods.ByName("ListShortcuts")),
connect.WithClientOptions(opts...),
),
getShortcut: connect.NewClient[v1.GetShortcutRequest, v1.Shortcut](
httpClient,
baseURL+ShortcutServiceGetShortcutProcedure,
connect.WithSchema(shortcutServiceMethods.ByName("GetShortcut")),
connect.WithClientOptions(opts...),
),
createShortcut: connect.NewClient[v1.CreateShortcutRequest, v1.Shortcut](
httpClient,
baseURL+ShortcutServiceCreateShortcutProcedure,
connect.WithSchema(shortcutServiceMethods.ByName("CreateShortcut")),
connect.WithClientOptions(opts...),
),
updateShortcut: connect.NewClient[v1.UpdateShortcutRequest, v1.Shortcut](
httpClient,
baseURL+ShortcutServiceUpdateShortcutProcedure,
connect.WithSchema(shortcutServiceMethods.ByName("UpdateShortcut")),
connect.WithClientOptions(opts...),
),
deleteShortcut: connect.NewClient[v1.DeleteShortcutRequest, emptypb.Empty](
httpClient,
baseURL+ShortcutServiceDeleteShortcutProcedure,
connect.WithSchema(shortcutServiceMethods.ByName("DeleteShortcut")),
connect.WithClientOptions(opts...),
),
}
}
// shortcutServiceClient implements ShortcutServiceClient.
type shortcutServiceClient struct {
listShortcuts *connect.Client[v1.ListShortcutsRequest, v1.ListShortcutsResponse]
getShortcut *connect.Client[v1.GetShortcutRequest, v1.Shortcut]
createShortcut *connect.Client[v1.CreateShortcutRequest, v1.Shortcut]
updateShortcut *connect.Client[v1.UpdateShortcutRequest, v1.Shortcut]
deleteShortcut *connect.Client[v1.DeleteShortcutRequest, emptypb.Empty]
}
// ListShortcuts calls memos.api.v1.ShortcutService.ListShortcuts.
func (c *shortcutServiceClient) ListShortcuts(ctx context.Context, req *connect.Request[v1.ListShortcutsRequest]) (*connect.Response[v1.ListShortcutsResponse], error) {
return c.listShortcuts.CallUnary(ctx, req)
}
// GetShortcut calls memos.api.v1.ShortcutService.GetShortcut.
func (c *shortcutServiceClient) GetShortcut(ctx context.Context, req *connect.Request[v1.GetShortcutRequest]) (*connect.Response[v1.Shortcut], error) {
return c.getShortcut.CallUnary(ctx, req)
}
// CreateShortcut calls memos.api.v1.ShortcutService.CreateShortcut.
func (c *shortcutServiceClient) CreateShortcut(ctx context.Context, req *connect.Request[v1.CreateShortcutRequest]) (*connect.Response[v1.Shortcut], error) {
return c.createShortcut.CallUnary(ctx, req)
}
// UpdateShortcut calls memos.api.v1.ShortcutService.UpdateShortcut.
func (c *shortcutServiceClient) UpdateShortcut(ctx context.Context, req *connect.Request[v1.UpdateShortcutRequest]) (*connect.Response[v1.Shortcut], error) {
return c.updateShortcut.CallUnary(ctx, req)
}
// DeleteShortcut calls memos.api.v1.ShortcutService.DeleteShortcut.
func (c *shortcutServiceClient) DeleteShortcut(ctx context.Context, req *connect.Request[v1.DeleteShortcutRequest]) (*connect.Response[emptypb.Empty], error) {
return c.deleteShortcut.CallUnary(ctx, req)
}
// ShortcutServiceHandler is an implementation of the memos.api.v1.ShortcutService service.
type ShortcutServiceHandler interface {
// ListShortcuts returns a list of shortcuts for a user.
ListShortcuts(context.Context, *connect.Request[v1.ListShortcutsRequest]) (*connect.Response[v1.ListShortcutsResponse], error)
// GetShortcut gets a shortcut by name.
GetShortcut(context.Context, *connect.Request[v1.GetShortcutRequest]) (*connect.Response[v1.Shortcut], error)
// CreateShortcut creates a new shortcut for a user.
CreateShortcut(context.Context, *connect.Request[v1.CreateShortcutRequest]) (*connect.Response[v1.Shortcut], error)
// UpdateShortcut updates a shortcut for a user.
UpdateShortcut(context.Context, *connect.Request[v1.UpdateShortcutRequest]) (*connect.Response[v1.Shortcut], error)
// DeleteShortcut deletes a shortcut for a user.
DeleteShortcut(context.Context, *connect.Request[v1.DeleteShortcutRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewShortcutServiceHandler builds an HTTP handler from the service implementation. It returns the
// path on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewShortcutServiceHandler(svc ShortcutServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
shortcutServiceMethods := v1.File_api_v1_shortcut_service_proto.Services().ByName("ShortcutService").Methods()
shortcutServiceListShortcutsHandler := connect.NewUnaryHandler(
ShortcutServiceListShortcutsProcedure,
svc.ListShortcuts,
connect.WithSchema(shortcutServiceMethods.ByName("ListShortcuts")),
connect.WithHandlerOptions(opts...),
)
shortcutServiceGetShortcutHandler := connect.NewUnaryHandler(
ShortcutServiceGetShortcutProcedure,
svc.GetShortcut,
connect.WithSchema(shortcutServiceMethods.ByName("GetShortcut")),
connect.WithHandlerOptions(opts...),
)
shortcutServiceCreateShortcutHandler := connect.NewUnaryHandler(
ShortcutServiceCreateShortcutProcedure,
svc.CreateShortcut,
connect.WithSchema(shortcutServiceMethods.ByName("CreateShortcut")),
connect.WithHandlerOptions(opts...),
)
shortcutServiceUpdateShortcutHandler := connect.NewUnaryHandler(
ShortcutServiceUpdateShortcutProcedure,
svc.UpdateShortcut,
connect.WithSchema(shortcutServiceMethods.ByName("UpdateShortcut")),
connect.WithHandlerOptions(opts...),
)
shortcutServiceDeleteShortcutHandler := connect.NewUnaryHandler(
ShortcutServiceDeleteShortcutProcedure,
svc.DeleteShortcut,
connect.WithSchema(shortcutServiceMethods.ByName("DeleteShortcut")),
connect.WithHandlerOptions(opts...),
)
return "/memos.api.v1.ShortcutService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case ShortcutServiceListShortcutsProcedure:
shortcutServiceListShortcutsHandler.ServeHTTP(w, r)
case ShortcutServiceGetShortcutProcedure:
shortcutServiceGetShortcutHandler.ServeHTTP(w, r)
case ShortcutServiceCreateShortcutProcedure:
shortcutServiceCreateShortcutHandler.ServeHTTP(w, r)
case ShortcutServiceUpdateShortcutProcedure:
shortcutServiceUpdateShortcutHandler.ServeHTTP(w, r)
case ShortcutServiceDeleteShortcutProcedure:
shortcutServiceDeleteShortcutHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedShortcutServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedShortcutServiceHandler struct{}
func (UnimplementedShortcutServiceHandler) ListShortcuts(context.Context, *connect.Request[v1.ListShortcutsRequest]) (*connect.Response[v1.ListShortcutsResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.ShortcutService.ListShortcuts is not implemented"))
}
func (UnimplementedShortcutServiceHandler) GetShortcut(context.Context, *connect.Request[v1.GetShortcutRequest]) (*connect.Response[v1.Shortcut], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.ShortcutService.GetShortcut is not implemented"))
}
func (UnimplementedShortcutServiceHandler) CreateShortcut(context.Context, *connect.Request[v1.CreateShortcutRequest]) (*connect.Response[v1.Shortcut], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.ShortcutService.CreateShortcut is not implemented"))
}
func (UnimplementedShortcutServiceHandler) UpdateShortcut(context.Context, *connect.Request[v1.UpdateShortcutRequest]) (*connect.Response[v1.Shortcut], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.ShortcutService.UpdateShortcut is not implemented"))
}
func (UnimplementedShortcutServiceHandler) DeleteShortcut(context.Context, *connect.Request[v1.DeleteShortcutRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.ShortcutService.DeleteShortcut is not implemented"))
}

View File

@ -0,0 +1,764 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: api/v1/user_service.proto
package apiv1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "github.com/usememos/memos/proto/gen/api/v1"
emptypb "google.golang.org/protobuf/types/known/emptypb"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// UserServiceName is the fully-qualified name of the UserService service.
UserServiceName = "memos.api.v1.UserService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// UserServiceListUsersProcedure is the fully-qualified name of the UserService's ListUsers RPC.
UserServiceListUsersProcedure = "/memos.api.v1.UserService/ListUsers"
// UserServiceGetUserProcedure is the fully-qualified name of the UserService's GetUser RPC.
UserServiceGetUserProcedure = "/memos.api.v1.UserService/GetUser"
// UserServiceCreateUserProcedure is the fully-qualified name of the UserService's CreateUser RPC.
UserServiceCreateUserProcedure = "/memos.api.v1.UserService/CreateUser"
// UserServiceUpdateUserProcedure is the fully-qualified name of the UserService's UpdateUser RPC.
UserServiceUpdateUserProcedure = "/memos.api.v1.UserService/UpdateUser"
// UserServiceDeleteUserProcedure is the fully-qualified name of the UserService's DeleteUser RPC.
UserServiceDeleteUserProcedure = "/memos.api.v1.UserService/DeleteUser"
// UserServiceListAllUserStatsProcedure is the fully-qualified name of the UserService's
// ListAllUserStats RPC.
UserServiceListAllUserStatsProcedure = "/memos.api.v1.UserService/ListAllUserStats"
// UserServiceGetUserStatsProcedure is the fully-qualified name of the UserService's GetUserStats
// RPC.
UserServiceGetUserStatsProcedure = "/memos.api.v1.UserService/GetUserStats"
// UserServiceGetUserSettingProcedure is the fully-qualified name of the UserService's
// GetUserSetting RPC.
UserServiceGetUserSettingProcedure = "/memos.api.v1.UserService/GetUserSetting"
// UserServiceUpdateUserSettingProcedure is the fully-qualified name of the UserService's
// UpdateUserSetting RPC.
UserServiceUpdateUserSettingProcedure = "/memos.api.v1.UserService/UpdateUserSetting"
// UserServiceListUserSettingsProcedure is the fully-qualified name of the UserService's
// ListUserSettings RPC.
UserServiceListUserSettingsProcedure = "/memos.api.v1.UserService/ListUserSettings"
// UserServiceListUserAccessTokensProcedure is the fully-qualified name of the UserService's
// ListUserAccessTokens RPC.
UserServiceListUserAccessTokensProcedure = "/memos.api.v1.UserService/ListUserAccessTokens"
// UserServiceCreateUserAccessTokenProcedure is the fully-qualified name of the UserService's
// CreateUserAccessToken RPC.
UserServiceCreateUserAccessTokenProcedure = "/memos.api.v1.UserService/CreateUserAccessToken"
// UserServiceDeleteUserAccessTokenProcedure is the fully-qualified name of the UserService's
// DeleteUserAccessToken RPC.
UserServiceDeleteUserAccessTokenProcedure = "/memos.api.v1.UserService/DeleteUserAccessToken"
// UserServiceListUserSessionsProcedure is the fully-qualified name of the UserService's
// ListUserSessions RPC.
UserServiceListUserSessionsProcedure = "/memos.api.v1.UserService/ListUserSessions"
// UserServiceRevokeUserSessionProcedure is the fully-qualified name of the UserService's
// RevokeUserSession RPC.
UserServiceRevokeUserSessionProcedure = "/memos.api.v1.UserService/RevokeUserSession"
// UserServiceListUserWebhooksProcedure is the fully-qualified name of the UserService's
// ListUserWebhooks RPC.
UserServiceListUserWebhooksProcedure = "/memos.api.v1.UserService/ListUserWebhooks"
// UserServiceCreateUserWebhookProcedure is the fully-qualified name of the UserService's
// CreateUserWebhook RPC.
UserServiceCreateUserWebhookProcedure = "/memos.api.v1.UserService/CreateUserWebhook"
// UserServiceUpdateUserWebhookProcedure is the fully-qualified name of the UserService's
// UpdateUserWebhook RPC.
UserServiceUpdateUserWebhookProcedure = "/memos.api.v1.UserService/UpdateUserWebhook"
// UserServiceDeleteUserWebhookProcedure is the fully-qualified name of the UserService's
// DeleteUserWebhook RPC.
UserServiceDeleteUserWebhookProcedure = "/memos.api.v1.UserService/DeleteUserWebhook"
// UserServiceListUserNotificationsProcedure is the fully-qualified name of the UserService's
// ListUserNotifications RPC.
UserServiceListUserNotificationsProcedure = "/memos.api.v1.UserService/ListUserNotifications"
// UserServiceUpdateUserNotificationProcedure is the fully-qualified name of the UserService's
// UpdateUserNotification RPC.
UserServiceUpdateUserNotificationProcedure = "/memos.api.v1.UserService/UpdateUserNotification"
// UserServiceDeleteUserNotificationProcedure is the fully-qualified name of the UserService's
// DeleteUserNotification RPC.
UserServiceDeleteUserNotificationProcedure = "/memos.api.v1.UserService/DeleteUserNotification"
)
// UserServiceClient is a client for the memos.api.v1.UserService service.
type UserServiceClient interface {
// ListUsers returns a list of users.
ListUsers(context.Context, *connect.Request[v1.ListUsersRequest]) (*connect.Response[v1.ListUsersResponse], error)
// GetUser gets a user by ID or username.
// Supports both numeric IDs and username strings:
// - users/{id} (e.g., users/101)
// - users/{username} (e.g., users/steven)
GetUser(context.Context, *connect.Request[v1.GetUserRequest]) (*connect.Response[v1.User], error)
// CreateUser creates a new user.
CreateUser(context.Context, *connect.Request[v1.CreateUserRequest]) (*connect.Response[v1.User], error)
// UpdateUser updates a user.
UpdateUser(context.Context, *connect.Request[v1.UpdateUserRequest]) (*connect.Response[v1.User], error)
// DeleteUser deletes a user.
DeleteUser(context.Context, *connect.Request[v1.DeleteUserRequest]) (*connect.Response[emptypb.Empty], error)
// ListAllUserStats returns statistics for all users.
ListAllUserStats(context.Context, *connect.Request[v1.ListAllUserStatsRequest]) (*connect.Response[v1.ListAllUserStatsResponse], error)
// GetUserStats returns statistics for a specific user.
GetUserStats(context.Context, *connect.Request[v1.GetUserStatsRequest]) (*connect.Response[v1.UserStats], error)
// GetUserSetting returns the user setting.
GetUserSetting(context.Context, *connect.Request[v1.GetUserSettingRequest]) (*connect.Response[v1.UserSetting], error)
// UpdateUserSetting updates the user setting.
UpdateUserSetting(context.Context, *connect.Request[v1.UpdateUserSettingRequest]) (*connect.Response[v1.UserSetting], error)
// ListUserSettings returns a list of user settings.
ListUserSettings(context.Context, *connect.Request[v1.ListUserSettingsRequest]) (*connect.Response[v1.ListUserSettingsResponse], error)
// ListUserAccessTokens returns a list of access tokens for a user.
ListUserAccessTokens(context.Context, *connect.Request[v1.ListUserAccessTokensRequest]) (*connect.Response[v1.ListUserAccessTokensResponse], error)
// CreateUserAccessToken creates a new access token for a user.
CreateUserAccessToken(context.Context, *connect.Request[v1.CreateUserAccessTokenRequest]) (*connect.Response[v1.UserAccessToken], error)
// DeleteUserAccessToken deletes an access token.
DeleteUserAccessToken(context.Context, *connect.Request[v1.DeleteUserAccessTokenRequest]) (*connect.Response[emptypb.Empty], error)
// ListUserSessions returns a list of active sessions for a user.
ListUserSessions(context.Context, *connect.Request[v1.ListUserSessionsRequest]) (*connect.Response[v1.ListUserSessionsResponse], error)
// RevokeUserSession revokes a specific session for a user.
RevokeUserSession(context.Context, *connect.Request[v1.RevokeUserSessionRequest]) (*connect.Response[emptypb.Empty], error)
// ListUserWebhooks returns a list of webhooks for a user.
ListUserWebhooks(context.Context, *connect.Request[v1.ListUserWebhooksRequest]) (*connect.Response[v1.ListUserWebhooksResponse], error)
// CreateUserWebhook creates a new webhook for a user.
CreateUserWebhook(context.Context, *connect.Request[v1.CreateUserWebhookRequest]) (*connect.Response[v1.UserWebhook], error)
// UpdateUserWebhook updates an existing webhook for a user.
UpdateUserWebhook(context.Context, *connect.Request[v1.UpdateUserWebhookRequest]) (*connect.Response[v1.UserWebhook], error)
// DeleteUserWebhook deletes a webhook for a user.
DeleteUserWebhook(context.Context, *connect.Request[v1.DeleteUserWebhookRequest]) (*connect.Response[emptypb.Empty], error)
// ListUserNotifications lists notifications for a user.
ListUserNotifications(context.Context, *connect.Request[v1.ListUserNotificationsRequest]) (*connect.Response[v1.ListUserNotificationsResponse], error)
// UpdateUserNotification updates a notification.
UpdateUserNotification(context.Context, *connect.Request[v1.UpdateUserNotificationRequest]) (*connect.Response[v1.UserNotification], error)
// DeleteUserNotification deletes a notification.
DeleteUserNotification(context.Context, *connect.Request[v1.DeleteUserNotificationRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewUserServiceClient constructs a client for the memos.api.v1.UserService service. By default, it
// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
// connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewUserServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) UserServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
userServiceMethods := v1.File_api_v1_user_service_proto.Services().ByName("UserService").Methods()
return &userServiceClient{
listUsers: connect.NewClient[v1.ListUsersRequest, v1.ListUsersResponse](
httpClient,
baseURL+UserServiceListUsersProcedure,
connect.WithSchema(userServiceMethods.ByName("ListUsers")),
connect.WithClientOptions(opts...),
),
getUser: connect.NewClient[v1.GetUserRequest, v1.User](
httpClient,
baseURL+UserServiceGetUserProcedure,
connect.WithSchema(userServiceMethods.ByName("GetUser")),
connect.WithClientOptions(opts...),
),
createUser: connect.NewClient[v1.CreateUserRequest, v1.User](
httpClient,
baseURL+UserServiceCreateUserProcedure,
connect.WithSchema(userServiceMethods.ByName("CreateUser")),
connect.WithClientOptions(opts...),
),
updateUser: connect.NewClient[v1.UpdateUserRequest, v1.User](
httpClient,
baseURL+UserServiceUpdateUserProcedure,
connect.WithSchema(userServiceMethods.ByName("UpdateUser")),
connect.WithClientOptions(opts...),
),
deleteUser: connect.NewClient[v1.DeleteUserRequest, emptypb.Empty](
httpClient,
baseURL+UserServiceDeleteUserProcedure,
connect.WithSchema(userServiceMethods.ByName("DeleteUser")),
connect.WithClientOptions(opts...),
),
listAllUserStats: connect.NewClient[v1.ListAllUserStatsRequest, v1.ListAllUserStatsResponse](
httpClient,
baseURL+UserServiceListAllUserStatsProcedure,
connect.WithSchema(userServiceMethods.ByName("ListAllUserStats")),
connect.WithClientOptions(opts...),
),
getUserStats: connect.NewClient[v1.GetUserStatsRequest, v1.UserStats](
httpClient,
baseURL+UserServiceGetUserStatsProcedure,
connect.WithSchema(userServiceMethods.ByName("GetUserStats")),
connect.WithClientOptions(opts...),
),
getUserSetting: connect.NewClient[v1.GetUserSettingRequest, v1.UserSetting](
httpClient,
baseURL+UserServiceGetUserSettingProcedure,
connect.WithSchema(userServiceMethods.ByName("GetUserSetting")),
connect.WithClientOptions(opts...),
),
updateUserSetting: connect.NewClient[v1.UpdateUserSettingRequest, v1.UserSetting](
httpClient,
baseURL+UserServiceUpdateUserSettingProcedure,
connect.WithSchema(userServiceMethods.ByName("UpdateUserSetting")),
connect.WithClientOptions(opts...),
),
listUserSettings: connect.NewClient[v1.ListUserSettingsRequest, v1.ListUserSettingsResponse](
httpClient,
baseURL+UserServiceListUserSettingsProcedure,
connect.WithSchema(userServiceMethods.ByName("ListUserSettings")),
connect.WithClientOptions(opts...),
),
listUserAccessTokens: connect.NewClient[v1.ListUserAccessTokensRequest, v1.ListUserAccessTokensResponse](
httpClient,
baseURL+UserServiceListUserAccessTokensProcedure,
connect.WithSchema(userServiceMethods.ByName("ListUserAccessTokens")),
connect.WithClientOptions(opts...),
),
createUserAccessToken: connect.NewClient[v1.CreateUserAccessTokenRequest, v1.UserAccessToken](
httpClient,
baseURL+UserServiceCreateUserAccessTokenProcedure,
connect.WithSchema(userServiceMethods.ByName("CreateUserAccessToken")),
connect.WithClientOptions(opts...),
),
deleteUserAccessToken: connect.NewClient[v1.DeleteUserAccessTokenRequest, emptypb.Empty](
httpClient,
baseURL+UserServiceDeleteUserAccessTokenProcedure,
connect.WithSchema(userServiceMethods.ByName("DeleteUserAccessToken")),
connect.WithClientOptions(opts...),
),
listUserSessions: connect.NewClient[v1.ListUserSessionsRequest, v1.ListUserSessionsResponse](
httpClient,
baseURL+UserServiceListUserSessionsProcedure,
connect.WithSchema(userServiceMethods.ByName("ListUserSessions")),
connect.WithClientOptions(opts...),
),
revokeUserSession: connect.NewClient[v1.RevokeUserSessionRequest, emptypb.Empty](
httpClient,
baseURL+UserServiceRevokeUserSessionProcedure,
connect.WithSchema(userServiceMethods.ByName("RevokeUserSession")),
connect.WithClientOptions(opts...),
),
listUserWebhooks: connect.NewClient[v1.ListUserWebhooksRequest, v1.ListUserWebhooksResponse](
httpClient,
baseURL+UserServiceListUserWebhooksProcedure,
connect.WithSchema(userServiceMethods.ByName("ListUserWebhooks")),
connect.WithClientOptions(opts...),
),
createUserWebhook: connect.NewClient[v1.CreateUserWebhookRequest, v1.UserWebhook](
httpClient,
baseURL+UserServiceCreateUserWebhookProcedure,
connect.WithSchema(userServiceMethods.ByName("CreateUserWebhook")),
connect.WithClientOptions(opts...),
),
updateUserWebhook: connect.NewClient[v1.UpdateUserWebhookRequest, v1.UserWebhook](
httpClient,
baseURL+UserServiceUpdateUserWebhookProcedure,
connect.WithSchema(userServiceMethods.ByName("UpdateUserWebhook")),
connect.WithClientOptions(opts...),
),
deleteUserWebhook: connect.NewClient[v1.DeleteUserWebhookRequest, emptypb.Empty](
httpClient,
baseURL+UserServiceDeleteUserWebhookProcedure,
connect.WithSchema(userServiceMethods.ByName("DeleteUserWebhook")),
connect.WithClientOptions(opts...),
),
listUserNotifications: connect.NewClient[v1.ListUserNotificationsRequest, v1.ListUserNotificationsResponse](
httpClient,
baseURL+UserServiceListUserNotificationsProcedure,
connect.WithSchema(userServiceMethods.ByName("ListUserNotifications")),
connect.WithClientOptions(opts...),
),
updateUserNotification: connect.NewClient[v1.UpdateUserNotificationRequest, v1.UserNotification](
httpClient,
baseURL+UserServiceUpdateUserNotificationProcedure,
connect.WithSchema(userServiceMethods.ByName("UpdateUserNotification")),
connect.WithClientOptions(opts...),
),
deleteUserNotification: connect.NewClient[v1.DeleteUserNotificationRequest, emptypb.Empty](
httpClient,
baseURL+UserServiceDeleteUserNotificationProcedure,
connect.WithSchema(userServiceMethods.ByName("DeleteUserNotification")),
connect.WithClientOptions(opts...),
),
}
}
// userServiceClient implements UserServiceClient.
type userServiceClient struct {
listUsers *connect.Client[v1.ListUsersRequest, v1.ListUsersResponse]
getUser *connect.Client[v1.GetUserRequest, v1.User]
createUser *connect.Client[v1.CreateUserRequest, v1.User]
updateUser *connect.Client[v1.UpdateUserRequest, v1.User]
deleteUser *connect.Client[v1.DeleteUserRequest, emptypb.Empty]
listAllUserStats *connect.Client[v1.ListAllUserStatsRequest, v1.ListAllUserStatsResponse]
getUserStats *connect.Client[v1.GetUserStatsRequest, v1.UserStats]
getUserSetting *connect.Client[v1.GetUserSettingRequest, v1.UserSetting]
updateUserSetting *connect.Client[v1.UpdateUserSettingRequest, v1.UserSetting]
listUserSettings *connect.Client[v1.ListUserSettingsRequest, v1.ListUserSettingsResponse]
listUserAccessTokens *connect.Client[v1.ListUserAccessTokensRequest, v1.ListUserAccessTokensResponse]
createUserAccessToken *connect.Client[v1.CreateUserAccessTokenRequest, v1.UserAccessToken]
deleteUserAccessToken *connect.Client[v1.DeleteUserAccessTokenRequest, emptypb.Empty]
listUserSessions *connect.Client[v1.ListUserSessionsRequest, v1.ListUserSessionsResponse]
revokeUserSession *connect.Client[v1.RevokeUserSessionRequest, emptypb.Empty]
listUserWebhooks *connect.Client[v1.ListUserWebhooksRequest, v1.ListUserWebhooksResponse]
createUserWebhook *connect.Client[v1.CreateUserWebhookRequest, v1.UserWebhook]
updateUserWebhook *connect.Client[v1.UpdateUserWebhookRequest, v1.UserWebhook]
deleteUserWebhook *connect.Client[v1.DeleteUserWebhookRequest, emptypb.Empty]
listUserNotifications *connect.Client[v1.ListUserNotificationsRequest, v1.ListUserNotificationsResponse]
updateUserNotification *connect.Client[v1.UpdateUserNotificationRequest, v1.UserNotification]
deleteUserNotification *connect.Client[v1.DeleteUserNotificationRequest, emptypb.Empty]
}
// ListUsers calls memos.api.v1.UserService.ListUsers.
func (c *userServiceClient) ListUsers(ctx context.Context, req *connect.Request[v1.ListUsersRequest]) (*connect.Response[v1.ListUsersResponse], error) {
return c.listUsers.CallUnary(ctx, req)
}
// GetUser calls memos.api.v1.UserService.GetUser.
func (c *userServiceClient) GetUser(ctx context.Context, req *connect.Request[v1.GetUserRequest]) (*connect.Response[v1.User], error) {
return c.getUser.CallUnary(ctx, req)
}
// CreateUser calls memos.api.v1.UserService.CreateUser.
func (c *userServiceClient) CreateUser(ctx context.Context, req *connect.Request[v1.CreateUserRequest]) (*connect.Response[v1.User], error) {
return c.createUser.CallUnary(ctx, req)
}
// UpdateUser calls memos.api.v1.UserService.UpdateUser.
func (c *userServiceClient) UpdateUser(ctx context.Context, req *connect.Request[v1.UpdateUserRequest]) (*connect.Response[v1.User], error) {
return c.updateUser.CallUnary(ctx, req)
}
// DeleteUser calls memos.api.v1.UserService.DeleteUser.
func (c *userServiceClient) DeleteUser(ctx context.Context, req *connect.Request[v1.DeleteUserRequest]) (*connect.Response[emptypb.Empty], error) {
return c.deleteUser.CallUnary(ctx, req)
}
// ListAllUserStats calls memos.api.v1.UserService.ListAllUserStats.
func (c *userServiceClient) ListAllUserStats(ctx context.Context, req *connect.Request[v1.ListAllUserStatsRequest]) (*connect.Response[v1.ListAllUserStatsResponse], error) {
return c.listAllUserStats.CallUnary(ctx, req)
}
// GetUserStats calls memos.api.v1.UserService.GetUserStats.
func (c *userServiceClient) GetUserStats(ctx context.Context, req *connect.Request[v1.GetUserStatsRequest]) (*connect.Response[v1.UserStats], error) {
return c.getUserStats.CallUnary(ctx, req)
}
// GetUserSetting calls memos.api.v1.UserService.GetUserSetting.
func (c *userServiceClient) GetUserSetting(ctx context.Context, req *connect.Request[v1.GetUserSettingRequest]) (*connect.Response[v1.UserSetting], error) {
return c.getUserSetting.CallUnary(ctx, req)
}
// UpdateUserSetting calls memos.api.v1.UserService.UpdateUserSetting.
func (c *userServiceClient) UpdateUserSetting(ctx context.Context, req *connect.Request[v1.UpdateUserSettingRequest]) (*connect.Response[v1.UserSetting], error) {
return c.updateUserSetting.CallUnary(ctx, req)
}
// ListUserSettings calls memos.api.v1.UserService.ListUserSettings.
func (c *userServiceClient) ListUserSettings(ctx context.Context, req *connect.Request[v1.ListUserSettingsRequest]) (*connect.Response[v1.ListUserSettingsResponse], error) {
return c.listUserSettings.CallUnary(ctx, req)
}
// ListUserAccessTokens calls memos.api.v1.UserService.ListUserAccessTokens.
func (c *userServiceClient) ListUserAccessTokens(ctx context.Context, req *connect.Request[v1.ListUserAccessTokensRequest]) (*connect.Response[v1.ListUserAccessTokensResponse], error) {
return c.listUserAccessTokens.CallUnary(ctx, req)
}
// CreateUserAccessToken calls memos.api.v1.UserService.CreateUserAccessToken.
func (c *userServiceClient) CreateUserAccessToken(ctx context.Context, req *connect.Request[v1.CreateUserAccessTokenRequest]) (*connect.Response[v1.UserAccessToken], error) {
return c.createUserAccessToken.CallUnary(ctx, req)
}
// DeleteUserAccessToken calls memos.api.v1.UserService.DeleteUserAccessToken.
func (c *userServiceClient) DeleteUserAccessToken(ctx context.Context, req *connect.Request[v1.DeleteUserAccessTokenRequest]) (*connect.Response[emptypb.Empty], error) {
return c.deleteUserAccessToken.CallUnary(ctx, req)
}
// ListUserSessions calls memos.api.v1.UserService.ListUserSessions.
func (c *userServiceClient) ListUserSessions(ctx context.Context, req *connect.Request[v1.ListUserSessionsRequest]) (*connect.Response[v1.ListUserSessionsResponse], error) {
return c.listUserSessions.CallUnary(ctx, req)
}
// RevokeUserSession calls memos.api.v1.UserService.RevokeUserSession.
func (c *userServiceClient) RevokeUserSession(ctx context.Context, req *connect.Request[v1.RevokeUserSessionRequest]) (*connect.Response[emptypb.Empty], error) {
return c.revokeUserSession.CallUnary(ctx, req)
}
// ListUserWebhooks calls memos.api.v1.UserService.ListUserWebhooks.
func (c *userServiceClient) ListUserWebhooks(ctx context.Context, req *connect.Request[v1.ListUserWebhooksRequest]) (*connect.Response[v1.ListUserWebhooksResponse], error) {
return c.listUserWebhooks.CallUnary(ctx, req)
}
// CreateUserWebhook calls memos.api.v1.UserService.CreateUserWebhook.
func (c *userServiceClient) CreateUserWebhook(ctx context.Context, req *connect.Request[v1.CreateUserWebhookRequest]) (*connect.Response[v1.UserWebhook], error) {
return c.createUserWebhook.CallUnary(ctx, req)
}
// UpdateUserWebhook calls memos.api.v1.UserService.UpdateUserWebhook.
func (c *userServiceClient) UpdateUserWebhook(ctx context.Context, req *connect.Request[v1.UpdateUserWebhookRequest]) (*connect.Response[v1.UserWebhook], error) {
return c.updateUserWebhook.CallUnary(ctx, req)
}
// DeleteUserWebhook calls memos.api.v1.UserService.DeleteUserWebhook.
func (c *userServiceClient) DeleteUserWebhook(ctx context.Context, req *connect.Request[v1.DeleteUserWebhookRequest]) (*connect.Response[emptypb.Empty], error) {
return c.deleteUserWebhook.CallUnary(ctx, req)
}
// ListUserNotifications calls memos.api.v1.UserService.ListUserNotifications.
func (c *userServiceClient) ListUserNotifications(ctx context.Context, req *connect.Request[v1.ListUserNotificationsRequest]) (*connect.Response[v1.ListUserNotificationsResponse], error) {
return c.listUserNotifications.CallUnary(ctx, req)
}
// UpdateUserNotification calls memos.api.v1.UserService.UpdateUserNotification.
func (c *userServiceClient) UpdateUserNotification(ctx context.Context, req *connect.Request[v1.UpdateUserNotificationRequest]) (*connect.Response[v1.UserNotification], error) {
return c.updateUserNotification.CallUnary(ctx, req)
}
// DeleteUserNotification calls memos.api.v1.UserService.DeleteUserNotification.
func (c *userServiceClient) DeleteUserNotification(ctx context.Context, req *connect.Request[v1.DeleteUserNotificationRequest]) (*connect.Response[emptypb.Empty], error) {
return c.deleteUserNotification.CallUnary(ctx, req)
}
// UserServiceHandler is an implementation of the memos.api.v1.UserService service.
type UserServiceHandler interface {
// ListUsers returns a list of users.
ListUsers(context.Context, *connect.Request[v1.ListUsersRequest]) (*connect.Response[v1.ListUsersResponse], error)
// GetUser gets a user by ID or username.
// Supports both numeric IDs and username strings:
// - users/{id} (e.g., users/101)
// - users/{username} (e.g., users/steven)
GetUser(context.Context, *connect.Request[v1.GetUserRequest]) (*connect.Response[v1.User], error)
// CreateUser creates a new user.
CreateUser(context.Context, *connect.Request[v1.CreateUserRequest]) (*connect.Response[v1.User], error)
// UpdateUser updates a user.
UpdateUser(context.Context, *connect.Request[v1.UpdateUserRequest]) (*connect.Response[v1.User], error)
// DeleteUser deletes a user.
DeleteUser(context.Context, *connect.Request[v1.DeleteUserRequest]) (*connect.Response[emptypb.Empty], error)
// ListAllUserStats returns statistics for all users.
ListAllUserStats(context.Context, *connect.Request[v1.ListAllUserStatsRequest]) (*connect.Response[v1.ListAllUserStatsResponse], error)
// GetUserStats returns statistics for a specific user.
GetUserStats(context.Context, *connect.Request[v1.GetUserStatsRequest]) (*connect.Response[v1.UserStats], error)
// GetUserSetting returns the user setting.
GetUserSetting(context.Context, *connect.Request[v1.GetUserSettingRequest]) (*connect.Response[v1.UserSetting], error)
// UpdateUserSetting updates the user setting.
UpdateUserSetting(context.Context, *connect.Request[v1.UpdateUserSettingRequest]) (*connect.Response[v1.UserSetting], error)
// ListUserSettings returns a list of user settings.
ListUserSettings(context.Context, *connect.Request[v1.ListUserSettingsRequest]) (*connect.Response[v1.ListUserSettingsResponse], error)
// ListUserAccessTokens returns a list of access tokens for a user.
ListUserAccessTokens(context.Context, *connect.Request[v1.ListUserAccessTokensRequest]) (*connect.Response[v1.ListUserAccessTokensResponse], error)
// CreateUserAccessToken creates a new access token for a user.
CreateUserAccessToken(context.Context, *connect.Request[v1.CreateUserAccessTokenRequest]) (*connect.Response[v1.UserAccessToken], error)
// DeleteUserAccessToken deletes an access token.
DeleteUserAccessToken(context.Context, *connect.Request[v1.DeleteUserAccessTokenRequest]) (*connect.Response[emptypb.Empty], error)
// ListUserSessions returns a list of active sessions for a user.
ListUserSessions(context.Context, *connect.Request[v1.ListUserSessionsRequest]) (*connect.Response[v1.ListUserSessionsResponse], error)
// RevokeUserSession revokes a specific session for a user.
RevokeUserSession(context.Context, *connect.Request[v1.RevokeUserSessionRequest]) (*connect.Response[emptypb.Empty], error)
// ListUserWebhooks returns a list of webhooks for a user.
ListUserWebhooks(context.Context, *connect.Request[v1.ListUserWebhooksRequest]) (*connect.Response[v1.ListUserWebhooksResponse], error)
// CreateUserWebhook creates a new webhook for a user.
CreateUserWebhook(context.Context, *connect.Request[v1.CreateUserWebhookRequest]) (*connect.Response[v1.UserWebhook], error)
// UpdateUserWebhook updates an existing webhook for a user.
UpdateUserWebhook(context.Context, *connect.Request[v1.UpdateUserWebhookRequest]) (*connect.Response[v1.UserWebhook], error)
// DeleteUserWebhook deletes a webhook for a user.
DeleteUserWebhook(context.Context, *connect.Request[v1.DeleteUserWebhookRequest]) (*connect.Response[emptypb.Empty], error)
// ListUserNotifications lists notifications for a user.
ListUserNotifications(context.Context, *connect.Request[v1.ListUserNotificationsRequest]) (*connect.Response[v1.ListUserNotificationsResponse], error)
// UpdateUserNotification updates a notification.
UpdateUserNotification(context.Context, *connect.Request[v1.UpdateUserNotificationRequest]) (*connect.Response[v1.UserNotification], error)
// DeleteUserNotification deletes a notification.
DeleteUserNotification(context.Context, *connect.Request[v1.DeleteUserNotificationRequest]) (*connect.Response[emptypb.Empty], error)
}
// NewUserServiceHandler builds an HTTP handler from the service implementation. It returns the path
// on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewUserServiceHandler(svc UserServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
userServiceMethods := v1.File_api_v1_user_service_proto.Services().ByName("UserService").Methods()
userServiceListUsersHandler := connect.NewUnaryHandler(
UserServiceListUsersProcedure,
svc.ListUsers,
connect.WithSchema(userServiceMethods.ByName("ListUsers")),
connect.WithHandlerOptions(opts...),
)
userServiceGetUserHandler := connect.NewUnaryHandler(
UserServiceGetUserProcedure,
svc.GetUser,
connect.WithSchema(userServiceMethods.ByName("GetUser")),
connect.WithHandlerOptions(opts...),
)
userServiceCreateUserHandler := connect.NewUnaryHandler(
UserServiceCreateUserProcedure,
svc.CreateUser,
connect.WithSchema(userServiceMethods.ByName("CreateUser")),
connect.WithHandlerOptions(opts...),
)
userServiceUpdateUserHandler := connect.NewUnaryHandler(
UserServiceUpdateUserProcedure,
svc.UpdateUser,
connect.WithSchema(userServiceMethods.ByName("UpdateUser")),
connect.WithHandlerOptions(opts...),
)
userServiceDeleteUserHandler := connect.NewUnaryHandler(
UserServiceDeleteUserProcedure,
svc.DeleteUser,
connect.WithSchema(userServiceMethods.ByName("DeleteUser")),
connect.WithHandlerOptions(opts...),
)
userServiceListAllUserStatsHandler := connect.NewUnaryHandler(
UserServiceListAllUserStatsProcedure,
svc.ListAllUserStats,
connect.WithSchema(userServiceMethods.ByName("ListAllUserStats")),
connect.WithHandlerOptions(opts...),
)
userServiceGetUserStatsHandler := connect.NewUnaryHandler(
UserServiceGetUserStatsProcedure,
svc.GetUserStats,
connect.WithSchema(userServiceMethods.ByName("GetUserStats")),
connect.WithHandlerOptions(opts...),
)
userServiceGetUserSettingHandler := connect.NewUnaryHandler(
UserServiceGetUserSettingProcedure,
svc.GetUserSetting,
connect.WithSchema(userServiceMethods.ByName("GetUserSetting")),
connect.WithHandlerOptions(opts...),
)
userServiceUpdateUserSettingHandler := connect.NewUnaryHandler(
UserServiceUpdateUserSettingProcedure,
svc.UpdateUserSetting,
connect.WithSchema(userServiceMethods.ByName("UpdateUserSetting")),
connect.WithHandlerOptions(opts...),
)
userServiceListUserSettingsHandler := connect.NewUnaryHandler(
UserServiceListUserSettingsProcedure,
svc.ListUserSettings,
connect.WithSchema(userServiceMethods.ByName("ListUserSettings")),
connect.WithHandlerOptions(opts...),
)
userServiceListUserAccessTokensHandler := connect.NewUnaryHandler(
UserServiceListUserAccessTokensProcedure,
svc.ListUserAccessTokens,
connect.WithSchema(userServiceMethods.ByName("ListUserAccessTokens")),
connect.WithHandlerOptions(opts...),
)
userServiceCreateUserAccessTokenHandler := connect.NewUnaryHandler(
UserServiceCreateUserAccessTokenProcedure,
svc.CreateUserAccessToken,
connect.WithSchema(userServiceMethods.ByName("CreateUserAccessToken")),
connect.WithHandlerOptions(opts...),
)
userServiceDeleteUserAccessTokenHandler := connect.NewUnaryHandler(
UserServiceDeleteUserAccessTokenProcedure,
svc.DeleteUserAccessToken,
connect.WithSchema(userServiceMethods.ByName("DeleteUserAccessToken")),
connect.WithHandlerOptions(opts...),
)
userServiceListUserSessionsHandler := connect.NewUnaryHandler(
UserServiceListUserSessionsProcedure,
svc.ListUserSessions,
connect.WithSchema(userServiceMethods.ByName("ListUserSessions")),
connect.WithHandlerOptions(opts...),
)
userServiceRevokeUserSessionHandler := connect.NewUnaryHandler(
UserServiceRevokeUserSessionProcedure,
svc.RevokeUserSession,
connect.WithSchema(userServiceMethods.ByName("RevokeUserSession")),
connect.WithHandlerOptions(opts...),
)
userServiceListUserWebhooksHandler := connect.NewUnaryHandler(
UserServiceListUserWebhooksProcedure,
svc.ListUserWebhooks,
connect.WithSchema(userServiceMethods.ByName("ListUserWebhooks")),
connect.WithHandlerOptions(opts...),
)
userServiceCreateUserWebhookHandler := connect.NewUnaryHandler(
UserServiceCreateUserWebhookProcedure,
svc.CreateUserWebhook,
connect.WithSchema(userServiceMethods.ByName("CreateUserWebhook")),
connect.WithHandlerOptions(opts...),
)
userServiceUpdateUserWebhookHandler := connect.NewUnaryHandler(
UserServiceUpdateUserWebhookProcedure,
svc.UpdateUserWebhook,
connect.WithSchema(userServiceMethods.ByName("UpdateUserWebhook")),
connect.WithHandlerOptions(opts...),
)
userServiceDeleteUserWebhookHandler := connect.NewUnaryHandler(
UserServiceDeleteUserWebhookProcedure,
svc.DeleteUserWebhook,
connect.WithSchema(userServiceMethods.ByName("DeleteUserWebhook")),
connect.WithHandlerOptions(opts...),
)
userServiceListUserNotificationsHandler := connect.NewUnaryHandler(
UserServiceListUserNotificationsProcedure,
svc.ListUserNotifications,
connect.WithSchema(userServiceMethods.ByName("ListUserNotifications")),
connect.WithHandlerOptions(opts...),
)
userServiceUpdateUserNotificationHandler := connect.NewUnaryHandler(
UserServiceUpdateUserNotificationProcedure,
svc.UpdateUserNotification,
connect.WithSchema(userServiceMethods.ByName("UpdateUserNotification")),
connect.WithHandlerOptions(opts...),
)
userServiceDeleteUserNotificationHandler := connect.NewUnaryHandler(
UserServiceDeleteUserNotificationProcedure,
svc.DeleteUserNotification,
connect.WithSchema(userServiceMethods.ByName("DeleteUserNotification")),
connect.WithHandlerOptions(opts...),
)
return "/memos.api.v1.UserService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case UserServiceListUsersProcedure:
userServiceListUsersHandler.ServeHTTP(w, r)
case UserServiceGetUserProcedure:
userServiceGetUserHandler.ServeHTTP(w, r)
case UserServiceCreateUserProcedure:
userServiceCreateUserHandler.ServeHTTP(w, r)
case UserServiceUpdateUserProcedure:
userServiceUpdateUserHandler.ServeHTTP(w, r)
case UserServiceDeleteUserProcedure:
userServiceDeleteUserHandler.ServeHTTP(w, r)
case UserServiceListAllUserStatsProcedure:
userServiceListAllUserStatsHandler.ServeHTTP(w, r)
case UserServiceGetUserStatsProcedure:
userServiceGetUserStatsHandler.ServeHTTP(w, r)
case UserServiceGetUserSettingProcedure:
userServiceGetUserSettingHandler.ServeHTTP(w, r)
case UserServiceUpdateUserSettingProcedure:
userServiceUpdateUserSettingHandler.ServeHTTP(w, r)
case UserServiceListUserSettingsProcedure:
userServiceListUserSettingsHandler.ServeHTTP(w, r)
case UserServiceListUserAccessTokensProcedure:
userServiceListUserAccessTokensHandler.ServeHTTP(w, r)
case UserServiceCreateUserAccessTokenProcedure:
userServiceCreateUserAccessTokenHandler.ServeHTTP(w, r)
case UserServiceDeleteUserAccessTokenProcedure:
userServiceDeleteUserAccessTokenHandler.ServeHTTP(w, r)
case UserServiceListUserSessionsProcedure:
userServiceListUserSessionsHandler.ServeHTTP(w, r)
case UserServiceRevokeUserSessionProcedure:
userServiceRevokeUserSessionHandler.ServeHTTP(w, r)
case UserServiceListUserWebhooksProcedure:
userServiceListUserWebhooksHandler.ServeHTTP(w, r)
case UserServiceCreateUserWebhookProcedure:
userServiceCreateUserWebhookHandler.ServeHTTP(w, r)
case UserServiceUpdateUserWebhookProcedure:
userServiceUpdateUserWebhookHandler.ServeHTTP(w, r)
case UserServiceDeleteUserWebhookProcedure:
userServiceDeleteUserWebhookHandler.ServeHTTP(w, r)
case UserServiceListUserNotificationsProcedure:
userServiceListUserNotificationsHandler.ServeHTTP(w, r)
case UserServiceUpdateUserNotificationProcedure:
userServiceUpdateUserNotificationHandler.ServeHTTP(w, r)
case UserServiceDeleteUserNotificationProcedure:
userServiceDeleteUserNotificationHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedUserServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedUserServiceHandler struct{}
func (UnimplementedUserServiceHandler) ListUsers(context.Context, *connect.Request[v1.ListUsersRequest]) (*connect.Response[v1.ListUsersResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.ListUsers is not implemented"))
}
func (UnimplementedUserServiceHandler) GetUser(context.Context, *connect.Request[v1.GetUserRequest]) (*connect.Response[v1.User], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.GetUser is not implemented"))
}
func (UnimplementedUserServiceHandler) CreateUser(context.Context, *connect.Request[v1.CreateUserRequest]) (*connect.Response[v1.User], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.CreateUser is not implemented"))
}
func (UnimplementedUserServiceHandler) UpdateUser(context.Context, *connect.Request[v1.UpdateUserRequest]) (*connect.Response[v1.User], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.UpdateUser is not implemented"))
}
func (UnimplementedUserServiceHandler) DeleteUser(context.Context, *connect.Request[v1.DeleteUserRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.DeleteUser is not implemented"))
}
func (UnimplementedUserServiceHandler) ListAllUserStats(context.Context, *connect.Request[v1.ListAllUserStatsRequest]) (*connect.Response[v1.ListAllUserStatsResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.ListAllUserStats is not implemented"))
}
func (UnimplementedUserServiceHandler) GetUserStats(context.Context, *connect.Request[v1.GetUserStatsRequest]) (*connect.Response[v1.UserStats], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.GetUserStats is not implemented"))
}
func (UnimplementedUserServiceHandler) GetUserSetting(context.Context, *connect.Request[v1.GetUserSettingRequest]) (*connect.Response[v1.UserSetting], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.GetUserSetting is not implemented"))
}
func (UnimplementedUserServiceHandler) UpdateUserSetting(context.Context, *connect.Request[v1.UpdateUserSettingRequest]) (*connect.Response[v1.UserSetting], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.UpdateUserSetting is not implemented"))
}
func (UnimplementedUserServiceHandler) ListUserSettings(context.Context, *connect.Request[v1.ListUserSettingsRequest]) (*connect.Response[v1.ListUserSettingsResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.ListUserSettings is not implemented"))
}
func (UnimplementedUserServiceHandler) ListUserAccessTokens(context.Context, *connect.Request[v1.ListUserAccessTokensRequest]) (*connect.Response[v1.ListUserAccessTokensResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.ListUserAccessTokens is not implemented"))
}
func (UnimplementedUserServiceHandler) CreateUserAccessToken(context.Context, *connect.Request[v1.CreateUserAccessTokenRequest]) (*connect.Response[v1.UserAccessToken], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.CreateUserAccessToken is not implemented"))
}
func (UnimplementedUserServiceHandler) DeleteUserAccessToken(context.Context, *connect.Request[v1.DeleteUserAccessTokenRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.DeleteUserAccessToken is not implemented"))
}
func (UnimplementedUserServiceHandler) ListUserSessions(context.Context, *connect.Request[v1.ListUserSessionsRequest]) (*connect.Response[v1.ListUserSessionsResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.ListUserSessions is not implemented"))
}
func (UnimplementedUserServiceHandler) RevokeUserSession(context.Context, *connect.Request[v1.RevokeUserSessionRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.RevokeUserSession is not implemented"))
}
func (UnimplementedUserServiceHandler) ListUserWebhooks(context.Context, *connect.Request[v1.ListUserWebhooksRequest]) (*connect.Response[v1.ListUserWebhooksResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.ListUserWebhooks is not implemented"))
}
func (UnimplementedUserServiceHandler) CreateUserWebhook(context.Context, *connect.Request[v1.CreateUserWebhookRequest]) (*connect.Response[v1.UserWebhook], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.CreateUserWebhook is not implemented"))
}
func (UnimplementedUserServiceHandler) UpdateUserWebhook(context.Context, *connect.Request[v1.UpdateUserWebhookRequest]) (*connect.Response[v1.UserWebhook], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.UpdateUserWebhook is not implemented"))
}
func (UnimplementedUserServiceHandler) DeleteUserWebhook(context.Context, *connect.Request[v1.DeleteUserWebhookRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.DeleteUserWebhook is not implemented"))
}
func (UnimplementedUserServiceHandler) ListUserNotifications(context.Context, *connect.Request[v1.ListUserNotificationsRequest]) (*connect.Response[v1.ListUserNotificationsResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.ListUserNotifications is not implemented"))
}
func (UnimplementedUserServiceHandler) UpdateUserNotification(context.Context, *connect.Request[v1.UpdateUserNotificationRequest]) (*connect.Response[v1.UserNotification], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.UpdateUserNotification is not implemented"))
}
func (UnimplementedUserServiceHandler) DeleteUserNotification(context.Context, *connect.Request[v1.DeleteUserNotificationRequest]) (*connect.Response[emptypb.Empty], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("memos.api.v1.UserService.DeleteUserNotification is not implemented"))
}

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.10 // protoc-gen-go v1.36.11
// protoc (unknown) // protoc (unknown)
// source: api/v1/attachment_service.proto // source: api/v1/attachment_service.proto
@ -8,7 +8,6 @@ package apiv1
import ( import (
_ "google.golang.org/genproto/googleapis/api/annotations" _ "google.golang.org/genproto/googleapis/api/annotations"
httpbody "google.golang.org/genproto/googleapis/api/httpbody"
protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl" protoimpl "google.golang.org/protobuf/runtime/protoimpl"
emptypb "google.golang.org/protobuf/types/known/emptypb" emptypb "google.golang.org/protobuf/types/known/emptypb"
@ -381,70 +380,6 @@ func (x *GetAttachmentRequest) GetName() string {
return "" return ""
} }
type GetAttachmentBinaryRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Required. The attachment name of the attachment.
// Format: attachments/{attachment}
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// The filename of the attachment. Mainly used for downloading.
Filename string `protobuf:"bytes,2,opt,name=filename,proto3" json:"filename,omitempty"`
// Optional. A flag indicating if the thumbnail version of the attachment should be returned.
Thumbnail bool `protobuf:"varint,3,opt,name=thumbnail,proto3" json:"thumbnail,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetAttachmentBinaryRequest) Reset() {
*x = GetAttachmentBinaryRequest{}
mi := &file_api_v1_attachment_service_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetAttachmentBinaryRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetAttachmentBinaryRequest) ProtoMessage() {}
func (x *GetAttachmentBinaryRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_v1_attachment_service_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetAttachmentBinaryRequest.ProtoReflect.Descriptor instead.
func (*GetAttachmentBinaryRequest) Descriptor() ([]byte, []int) {
return file_api_v1_attachment_service_proto_rawDescGZIP(), []int{5}
}
func (x *GetAttachmentBinaryRequest) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *GetAttachmentBinaryRequest) GetFilename() string {
if x != nil {
return x.Filename
}
return ""
}
func (x *GetAttachmentBinaryRequest) GetThumbnail() bool {
if x != nil {
return x.Thumbnail
}
return false
}
type UpdateAttachmentRequest struct { type UpdateAttachmentRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
// Required. The attachment which replaces the attachment on the server. // Required. The attachment which replaces the attachment on the server.
@ -457,7 +392,7 @@ type UpdateAttachmentRequest struct {
func (x *UpdateAttachmentRequest) Reset() { func (x *UpdateAttachmentRequest) Reset() {
*x = UpdateAttachmentRequest{} *x = UpdateAttachmentRequest{}
mi := &file_api_v1_attachment_service_proto_msgTypes[6] mi := &file_api_v1_attachment_service_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -469,7 +404,7 @@ func (x *UpdateAttachmentRequest) String() string {
func (*UpdateAttachmentRequest) ProtoMessage() {} func (*UpdateAttachmentRequest) ProtoMessage() {}
func (x *UpdateAttachmentRequest) ProtoReflect() protoreflect.Message { func (x *UpdateAttachmentRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_v1_attachment_service_proto_msgTypes[6] mi := &file_api_v1_attachment_service_proto_msgTypes[5]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -482,7 +417,7 @@ func (x *UpdateAttachmentRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use UpdateAttachmentRequest.ProtoReflect.Descriptor instead. // Deprecated: Use UpdateAttachmentRequest.ProtoReflect.Descriptor instead.
func (*UpdateAttachmentRequest) Descriptor() ([]byte, []int) { func (*UpdateAttachmentRequest) Descriptor() ([]byte, []int) {
return file_api_v1_attachment_service_proto_rawDescGZIP(), []int{6} return file_api_v1_attachment_service_proto_rawDescGZIP(), []int{5}
} }
func (x *UpdateAttachmentRequest) GetAttachment() *Attachment { func (x *UpdateAttachmentRequest) GetAttachment() *Attachment {
@ -510,7 +445,7 @@ type DeleteAttachmentRequest struct {
func (x *DeleteAttachmentRequest) Reset() { func (x *DeleteAttachmentRequest) Reset() {
*x = DeleteAttachmentRequest{} *x = DeleteAttachmentRequest{}
mi := &file_api_v1_attachment_service_proto_msgTypes[7] mi := &file_api_v1_attachment_service_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -522,7 +457,7 @@ func (x *DeleteAttachmentRequest) String() string {
func (*DeleteAttachmentRequest) ProtoMessage() {} func (*DeleteAttachmentRequest) ProtoMessage() {}
func (x *DeleteAttachmentRequest) ProtoReflect() protoreflect.Message { func (x *DeleteAttachmentRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_v1_attachment_service_proto_msgTypes[7] mi := &file_api_v1_attachment_service_proto_msgTypes[6]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -535,7 +470,7 @@ func (x *DeleteAttachmentRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use DeleteAttachmentRequest.ProtoReflect.Descriptor instead. // Deprecated: Use DeleteAttachmentRequest.ProtoReflect.Descriptor instead.
func (*DeleteAttachmentRequest) Descriptor() ([]byte, []int) { func (*DeleteAttachmentRequest) Descriptor() ([]byte, []int) {
return file_api_v1_attachment_service_proto_rawDescGZIP(), []int{7} return file_api_v1_attachment_service_proto_rawDescGZIP(), []int{6}
} }
func (x *DeleteAttachmentRequest) GetName() string { func (x *DeleteAttachmentRequest) GetName() string {
@ -549,7 +484,7 @@ var File_api_v1_attachment_service_proto protoreflect.FileDescriptor
const file_api_v1_attachment_service_proto_rawDesc = "" + const file_api_v1_attachment_service_proto_rawDesc = "" +
"\n" + "\n" +
"\x1fapi/v1/attachment_service.proto\x12\fmemos.api.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/httpbody.proto\x1a\x19google/api/resource.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xfb\x02\n" + "\x1fapi/v1/attachment_service.proto\x12\fmemos.api.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xfb\x02\n" +
"\n" + "\n" +
"Attachment\x12\x17\n" + "Attachment\x12\x17\n" +
"\x04name\x18\x01 \x01(\tB\x03\xe0A\bR\x04name\x12@\n" + "\x04name\x18\x01 \x01(\tB\x03\xe0A\bR\x04name\x12@\n" +
@ -582,12 +517,7 @@ const file_api_v1_attachment_service_proto_rawDesc = "" +
"total_size\x18\x03 \x01(\x05R\ttotalSize\"K\n" + "total_size\x18\x03 \x01(\x05R\ttotalSize\"K\n" +
"\x14GetAttachmentRequest\x123\n" + "\x14GetAttachmentRequest\x123\n" +
"\x04name\x18\x01 \x01(\tB\x1f\xe0A\x02\xfaA\x19\n" + "\x04name\x18\x01 \x01(\tB\x1f\xe0A\x02\xfaA\x19\n" +
"\x17memos.api.v1/AttachmentR\x04name\"\x95\x01\n" + "\x17memos.api.v1/AttachmentR\x04name\"\x9a\x01\n" +
"\x1aGetAttachmentBinaryRequest\x123\n" +
"\x04name\x18\x01 \x01(\tB\x1f\xe0A\x02\xfaA\x19\n" +
"\x17memos.api.v1/AttachmentR\x04name\x12\x1f\n" +
"\bfilename\x18\x02 \x01(\tB\x03\xe0A\x02R\bfilename\x12!\n" +
"\tthumbnail\x18\x03 \x01(\bB\x03\xe0A\x01R\tthumbnail\"\x9a\x01\n" +
"\x17UpdateAttachmentRequest\x12=\n" + "\x17UpdateAttachmentRequest\x12=\n" +
"\n" + "\n" +
"attachment\x18\x01 \x01(\v2\x18.memos.api.v1.AttachmentB\x03\xe0A\x02R\n" + "attachment\x18\x01 \x01(\v2\x18.memos.api.v1.AttachmentB\x03\xe0A\x02R\n" +
@ -596,14 +526,13 @@ const file_api_v1_attachment_service_proto_rawDesc = "" +
"updateMask\"N\n" + "updateMask\"N\n" +
"\x17DeleteAttachmentRequest\x123\n" + "\x17DeleteAttachmentRequest\x123\n" +
"\x04name\x18\x01 \x01(\tB\x1f\xe0A\x02\xfaA\x19\n" + "\x04name\x18\x01 \x01(\tB\x1f\xe0A\x02\xfaA\x19\n" +
"\x17memos.api.v1/AttachmentR\x04name2\xe5\x06\n" + "\x17memos.api.v1/AttachmentR\x04name2\xc4\x05\n" +
"\x11AttachmentService\x12\x89\x01\n" + "\x11AttachmentService\x12\x89\x01\n" +
"\x10CreateAttachment\x12%.memos.api.v1.CreateAttachmentRequest\x1a\x18.memos.api.v1.Attachment\"4\xdaA\n" + "\x10CreateAttachment\x12%.memos.api.v1.CreateAttachmentRequest\x1a\x18.memos.api.v1.Attachment\"4\xdaA\n" +
"attachment\x82\xd3\xe4\x93\x02!:\n" + "attachment\x82\xd3\xe4\x93\x02!:\n" +
"attachment\"\x13/api/v1/attachments\x12{\n" + "attachment\"\x13/api/v1/attachments\x12{\n" +
"\x0fListAttachments\x12$.memos.api.v1.ListAttachmentsRequest\x1a%.memos.api.v1.ListAttachmentsResponse\"\x1b\x82\xd3\xe4\x93\x02\x15\x12\x13/api/v1/attachments\x12z\n" + "\x0fListAttachments\x12$.memos.api.v1.ListAttachmentsRequest\x1a%.memos.api.v1.ListAttachmentsResponse\"\x1b\x82\xd3\xe4\x93\x02\x15\x12\x13/api/v1/attachments\x12z\n" +
"\rGetAttachment\x12\".memos.api.v1.GetAttachmentRequest\x1a\x18.memos.api.v1.Attachment\"+\xdaA\x04name\x82\xd3\xe4\x93\x02\x1e\x12\x1c/api/v1/{name=attachments/*}\x12\x9e\x01\n" + "\rGetAttachment\x12\".memos.api.v1.GetAttachmentRequest\x1a\x18.memos.api.v1.Attachment\"+\xdaA\x04name\x82\xd3\xe4\x93\x02\x1e\x12\x1c/api/v1/{name=attachments/*}\x12\xa9\x01\n" +
"\x13GetAttachmentBinary\x12(.memos.api.v1.GetAttachmentBinaryRequest\x1a\x14.google.api.HttpBody\"G\xdaA\x17name,filename,thumbnail\x82\xd3\xe4\x93\x02'\x12%/file/{name=attachments/*}/{filename}\x12\xa9\x01\n" +
"\x10UpdateAttachment\x12%.memos.api.v1.UpdateAttachmentRequest\x1a\x18.memos.api.v1.Attachment\"T\xdaA\x16attachment,update_mask\x82\xd3\xe4\x93\x025:\n" + "\x10UpdateAttachment\x12%.memos.api.v1.UpdateAttachmentRequest\x1a\x18.memos.api.v1.Attachment\"T\xdaA\x16attachment,update_mask\x82\xd3\xe4\x93\x025:\n" +
"attachment2'/api/v1/{attachment.name=attachments/*}\x12~\n" + "attachment2'/api/v1/{attachment.name=attachments/*}\x12~\n" +
"\x10DeleteAttachment\x12%.memos.api.v1.DeleteAttachmentRequest\x1a\x16.google.protobuf.Empty\"+\xdaA\x04name\x82\xd3\xe4\x93\x02\x1e*\x1c/api/v1/{name=attachments/*}B\xae\x01\n" + "\x10DeleteAttachment\x12%.memos.api.v1.DeleteAttachmentRequest\x1a\x16.google.protobuf.Empty\"+\xdaA\x04name\x82\xd3\xe4\x93\x02\x1e*\x1c/api/v1/{name=attachments/*}B\xae\x01\n" +
@ -621,41 +550,37 @@ func file_api_v1_attachment_service_proto_rawDescGZIP() []byte {
return file_api_v1_attachment_service_proto_rawDescData return file_api_v1_attachment_service_proto_rawDescData
} }
var file_api_v1_attachment_service_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_api_v1_attachment_service_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_api_v1_attachment_service_proto_goTypes = []any{ var file_api_v1_attachment_service_proto_goTypes = []any{
(*Attachment)(nil), // 0: memos.api.v1.Attachment (*Attachment)(nil), // 0: memos.api.v1.Attachment
(*CreateAttachmentRequest)(nil), // 1: memos.api.v1.CreateAttachmentRequest (*CreateAttachmentRequest)(nil), // 1: memos.api.v1.CreateAttachmentRequest
(*ListAttachmentsRequest)(nil), // 2: memos.api.v1.ListAttachmentsRequest (*ListAttachmentsRequest)(nil), // 2: memos.api.v1.ListAttachmentsRequest
(*ListAttachmentsResponse)(nil), // 3: memos.api.v1.ListAttachmentsResponse (*ListAttachmentsResponse)(nil), // 3: memos.api.v1.ListAttachmentsResponse
(*GetAttachmentRequest)(nil), // 4: memos.api.v1.GetAttachmentRequest (*GetAttachmentRequest)(nil), // 4: memos.api.v1.GetAttachmentRequest
(*GetAttachmentBinaryRequest)(nil), // 5: memos.api.v1.GetAttachmentBinaryRequest (*UpdateAttachmentRequest)(nil), // 5: memos.api.v1.UpdateAttachmentRequest
(*UpdateAttachmentRequest)(nil), // 6: memos.api.v1.UpdateAttachmentRequest (*DeleteAttachmentRequest)(nil), // 6: memos.api.v1.DeleteAttachmentRequest
(*DeleteAttachmentRequest)(nil), // 7: memos.api.v1.DeleteAttachmentRequest (*timestamppb.Timestamp)(nil), // 7: google.protobuf.Timestamp
(*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp (*fieldmaskpb.FieldMask)(nil), // 8: google.protobuf.FieldMask
(*fieldmaskpb.FieldMask)(nil), // 9: google.protobuf.FieldMask (*emptypb.Empty)(nil), // 9: google.protobuf.Empty
(*httpbody.HttpBody)(nil), // 10: google.api.HttpBody
(*emptypb.Empty)(nil), // 11: google.protobuf.Empty
} }
var file_api_v1_attachment_service_proto_depIdxs = []int32{ var file_api_v1_attachment_service_proto_depIdxs = []int32{
8, // 0: memos.api.v1.Attachment.create_time:type_name -> google.protobuf.Timestamp 7, // 0: memos.api.v1.Attachment.create_time:type_name -> google.protobuf.Timestamp
0, // 1: memos.api.v1.CreateAttachmentRequest.attachment:type_name -> memos.api.v1.Attachment 0, // 1: memos.api.v1.CreateAttachmentRequest.attachment:type_name -> memos.api.v1.Attachment
0, // 2: memos.api.v1.ListAttachmentsResponse.attachments:type_name -> memos.api.v1.Attachment 0, // 2: memos.api.v1.ListAttachmentsResponse.attachments:type_name -> memos.api.v1.Attachment
0, // 3: memos.api.v1.UpdateAttachmentRequest.attachment:type_name -> memos.api.v1.Attachment 0, // 3: memos.api.v1.UpdateAttachmentRequest.attachment:type_name -> memos.api.v1.Attachment
9, // 4: memos.api.v1.UpdateAttachmentRequest.update_mask:type_name -> google.protobuf.FieldMask 8, // 4: memos.api.v1.UpdateAttachmentRequest.update_mask:type_name -> google.protobuf.FieldMask
1, // 5: memos.api.v1.AttachmentService.CreateAttachment:input_type -> memos.api.v1.CreateAttachmentRequest 1, // 5: memos.api.v1.AttachmentService.CreateAttachment:input_type -> memos.api.v1.CreateAttachmentRequest
2, // 6: memos.api.v1.AttachmentService.ListAttachments:input_type -> memos.api.v1.ListAttachmentsRequest 2, // 6: memos.api.v1.AttachmentService.ListAttachments:input_type -> memos.api.v1.ListAttachmentsRequest
4, // 7: memos.api.v1.AttachmentService.GetAttachment:input_type -> memos.api.v1.GetAttachmentRequest 4, // 7: memos.api.v1.AttachmentService.GetAttachment:input_type -> memos.api.v1.GetAttachmentRequest
5, // 8: memos.api.v1.AttachmentService.GetAttachmentBinary:input_type -> memos.api.v1.GetAttachmentBinaryRequest 5, // 8: memos.api.v1.AttachmentService.UpdateAttachment:input_type -> memos.api.v1.UpdateAttachmentRequest
6, // 9: memos.api.v1.AttachmentService.UpdateAttachment:input_type -> memos.api.v1.UpdateAttachmentRequest 6, // 9: memos.api.v1.AttachmentService.DeleteAttachment:input_type -> memos.api.v1.DeleteAttachmentRequest
7, // 10: memos.api.v1.AttachmentService.DeleteAttachment:input_type -> memos.api.v1.DeleteAttachmentRequest 0, // 10: memos.api.v1.AttachmentService.CreateAttachment:output_type -> memos.api.v1.Attachment
0, // 11: memos.api.v1.AttachmentService.CreateAttachment:output_type -> memos.api.v1.Attachment 3, // 11: memos.api.v1.AttachmentService.ListAttachments:output_type -> memos.api.v1.ListAttachmentsResponse
3, // 12: memos.api.v1.AttachmentService.ListAttachments:output_type -> memos.api.v1.ListAttachmentsResponse 0, // 12: memos.api.v1.AttachmentService.GetAttachment:output_type -> memos.api.v1.Attachment
0, // 13: memos.api.v1.AttachmentService.GetAttachment:output_type -> memos.api.v1.Attachment 0, // 13: memos.api.v1.AttachmentService.UpdateAttachment:output_type -> memos.api.v1.Attachment
10, // 14: memos.api.v1.AttachmentService.GetAttachmentBinary:output_type -> google.api.HttpBody 9, // 14: memos.api.v1.AttachmentService.DeleteAttachment:output_type -> google.protobuf.Empty
0, // 15: memos.api.v1.AttachmentService.UpdateAttachment:output_type -> memos.api.v1.Attachment 10, // [10:15] is the sub-list for method output_type
11, // 16: memos.api.v1.AttachmentService.DeleteAttachment:output_type -> google.protobuf.Empty 5, // [5:10] is the sub-list for method input_type
11, // [11:17] is the sub-list for method output_type
5, // [5:11] is the sub-list for method input_type
5, // [5:5] is the sub-list for extension type_name 5, // [5:5] is the sub-list for extension type_name
5, // [5:5] is the sub-list for extension extendee 5, // [5:5] is the sub-list for extension extendee
0, // [0:5] is the sub-list for field type_name 0, // [0:5] is the sub-list for field type_name
@ -673,7 +598,7 @@ func file_api_v1_attachment_service_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_v1_attachment_service_proto_rawDesc), len(file_api_v1_attachment_service_proto_rawDesc)), RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_v1_attachment_service_proto_rawDesc), len(file_api_v1_attachment_service_proto_rawDesc)),
NumEnums: 0, NumEnums: 0,
NumMessages: 8, NumMessages: 7,
NumExtensions: 0, NumExtensions: 0,
NumServices: 1, NumServices: 1,
}, },

View File

@ -150,75 +150,6 @@ func local_request_AttachmentService_GetAttachment_0(ctx context.Context, marsha
return msg, metadata, err return msg, metadata, err
} }
var filter_AttachmentService_GetAttachmentBinary_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0, "filename": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}}
func request_AttachmentService_GetAttachmentBinary_0(ctx context.Context, marshaler runtime.Marshaler, client AttachmentServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq GetAttachmentBinaryRequest
metadata runtime.ServerMetadata
err error
)
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
val, ok := pathParams["name"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name")
}
protoReq.Name, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
}
val, ok = pathParams["filename"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "filename")
}
protoReq.Filename, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "filename", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_AttachmentService_GetAttachmentBinary_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.GetAttachmentBinary(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_AttachmentService_GetAttachmentBinary_0(ctx context.Context, marshaler runtime.Marshaler, server AttachmentServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq GetAttachmentBinaryRequest
metadata runtime.ServerMetadata
err error
)
val, ok := pathParams["name"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name")
}
protoReq.Name, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
}
val, ok = pathParams["filename"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "filename")
}
protoReq.Filename, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "filename", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_AttachmentService_GetAttachmentBinary_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.GetAttachmentBinary(ctx, &protoReq)
return msg, metadata, err
}
var filter_AttachmentService_UpdateAttachment_0 = &utilities.DoubleArray{Encoding: map[string]int{"attachment": 0, "name": 1}, Base: []int{1, 2, 1, 0, 0}, Check: []int{0, 1, 2, 3, 2}} var filter_AttachmentService_UpdateAttachment_0 = &utilities.DoubleArray{Encoding: map[string]int{"attachment": 0, "name": 1}, Base: []int{1, 2, 1, 0, 0}, Check: []int{0, 1, 2, 3, 2}}
func request_AttachmentService_UpdateAttachment_0(ctx context.Context, marshaler runtime.Marshaler, client AttachmentServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { func request_AttachmentService_UpdateAttachment_0(ctx context.Context, marshaler runtime.Marshaler, client AttachmentServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
@ -405,26 +336,6 @@ func RegisterAttachmentServiceHandlerServer(ctx context.Context, mux *runtime.Se
} }
forward_AttachmentService_GetAttachment_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) forward_AttachmentService_GetAttachment_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
}) })
mux.Handle(http.MethodGet, pattern_AttachmentService_GetAttachmentBinary_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.AttachmentService/GetAttachmentBinary", runtime.WithHTTPPathPattern("/file/{name=attachments/*}/{filename}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_AttachmentService_GetAttachmentBinary_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_AttachmentService_GetAttachmentBinary_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPatch, pattern_AttachmentService_UpdateAttachment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { mux.Handle(http.MethodPatch, pattern_AttachmentService_UpdateAttachment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context()) ctx, cancel := context.WithCancel(req.Context())
defer cancel() defer cancel()
@ -556,23 +467,6 @@ func RegisterAttachmentServiceHandlerClient(ctx context.Context, mux *runtime.Se
} }
forward_AttachmentService_GetAttachment_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) forward_AttachmentService_GetAttachment_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
}) })
mux.Handle(http.MethodGet, pattern_AttachmentService_GetAttachmentBinary_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.AttachmentService/GetAttachmentBinary", runtime.WithHTTPPathPattern("/file/{name=attachments/*}/{filename}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_AttachmentService_GetAttachmentBinary_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_AttachmentService_GetAttachmentBinary_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPatch, pattern_AttachmentService_UpdateAttachment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { mux.Handle(http.MethodPatch, pattern_AttachmentService_UpdateAttachment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context()) ctx, cancel := context.WithCancel(req.Context())
defer cancel() defer cancel()
@ -611,19 +505,17 @@ func RegisterAttachmentServiceHandlerClient(ctx context.Context, mux *runtime.Se
} }
var ( var (
pattern_AttachmentService_CreateAttachment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "attachments"}, "")) pattern_AttachmentService_CreateAttachment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "attachments"}, ""))
pattern_AttachmentService_ListAttachments_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "attachments"}, "")) pattern_AttachmentService_ListAttachments_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "attachments"}, ""))
pattern_AttachmentService_GetAttachment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "attachments", "name"}, "")) pattern_AttachmentService_GetAttachment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "attachments", "name"}, ""))
pattern_AttachmentService_GetAttachmentBinary_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 2, 5, 2, 1, 0, 4, 1, 5, 3}, []string{"file", "attachments", "name", "filename"}, "")) pattern_AttachmentService_UpdateAttachment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "attachments", "attachment.name"}, ""))
pattern_AttachmentService_UpdateAttachment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "attachments", "attachment.name"}, "")) pattern_AttachmentService_DeleteAttachment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "attachments", "name"}, ""))
pattern_AttachmentService_DeleteAttachment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "attachments", "name"}, ""))
) )
var ( var (
forward_AttachmentService_CreateAttachment_0 = runtime.ForwardResponseMessage forward_AttachmentService_CreateAttachment_0 = runtime.ForwardResponseMessage
forward_AttachmentService_ListAttachments_0 = runtime.ForwardResponseMessage forward_AttachmentService_ListAttachments_0 = runtime.ForwardResponseMessage
forward_AttachmentService_GetAttachment_0 = runtime.ForwardResponseMessage forward_AttachmentService_GetAttachment_0 = runtime.ForwardResponseMessage
forward_AttachmentService_GetAttachmentBinary_0 = runtime.ForwardResponseMessage forward_AttachmentService_UpdateAttachment_0 = runtime.ForwardResponseMessage
forward_AttachmentService_UpdateAttachment_0 = runtime.ForwardResponseMessage forward_AttachmentService_DeleteAttachment_0 = runtime.ForwardResponseMessage
forward_AttachmentService_DeleteAttachment_0 = runtime.ForwardResponseMessage
) )

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions: // versions:
// - protoc-gen-go-grpc v1.5.1 // - protoc-gen-go-grpc v1.6.0
// - protoc (unknown) // - protoc (unknown)
// source: api/v1/attachment_service.proto // source: api/v1/attachment_service.proto
@ -8,7 +8,6 @@ package apiv1
import ( import (
context "context" context "context"
httpbody "google.golang.org/genproto/googleapis/api/httpbody"
grpc "google.golang.org/grpc" grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes" codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status" status "google.golang.org/grpc/status"
@ -21,12 +20,11 @@ import (
const _ = grpc.SupportPackageIsVersion9 const _ = grpc.SupportPackageIsVersion9
const ( const (
AttachmentService_CreateAttachment_FullMethodName = "/memos.api.v1.AttachmentService/CreateAttachment" AttachmentService_CreateAttachment_FullMethodName = "/memos.api.v1.AttachmentService/CreateAttachment"
AttachmentService_ListAttachments_FullMethodName = "/memos.api.v1.AttachmentService/ListAttachments" AttachmentService_ListAttachments_FullMethodName = "/memos.api.v1.AttachmentService/ListAttachments"
AttachmentService_GetAttachment_FullMethodName = "/memos.api.v1.AttachmentService/GetAttachment" AttachmentService_GetAttachment_FullMethodName = "/memos.api.v1.AttachmentService/GetAttachment"
AttachmentService_GetAttachmentBinary_FullMethodName = "/memos.api.v1.AttachmentService/GetAttachmentBinary" AttachmentService_UpdateAttachment_FullMethodName = "/memos.api.v1.AttachmentService/UpdateAttachment"
AttachmentService_UpdateAttachment_FullMethodName = "/memos.api.v1.AttachmentService/UpdateAttachment" AttachmentService_DeleteAttachment_FullMethodName = "/memos.api.v1.AttachmentService/DeleteAttachment"
AttachmentService_DeleteAttachment_FullMethodName = "/memos.api.v1.AttachmentService/DeleteAttachment"
) )
// AttachmentServiceClient is the client API for AttachmentService service. // AttachmentServiceClient is the client API for AttachmentService service.
@ -39,8 +37,6 @@ type AttachmentServiceClient interface {
ListAttachments(ctx context.Context, in *ListAttachmentsRequest, opts ...grpc.CallOption) (*ListAttachmentsResponse, error) ListAttachments(ctx context.Context, in *ListAttachmentsRequest, opts ...grpc.CallOption) (*ListAttachmentsResponse, error)
// GetAttachment returns a attachment by name. // GetAttachment returns a attachment by name.
GetAttachment(ctx context.Context, in *GetAttachmentRequest, opts ...grpc.CallOption) (*Attachment, error) GetAttachment(ctx context.Context, in *GetAttachmentRequest, opts ...grpc.CallOption) (*Attachment, error)
// GetAttachmentBinary returns a attachment binary by name.
GetAttachmentBinary(ctx context.Context, in *GetAttachmentBinaryRequest, opts ...grpc.CallOption) (*httpbody.HttpBody, error)
// UpdateAttachment updates a attachment. // UpdateAttachment updates a attachment.
UpdateAttachment(ctx context.Context, in *UpdateAttachmentRequest, opts ...grpc.CallOption) (*Attachment, error) UpdateAttachment(ctx context.Context, in *UpdateAttachmentRequest, opts ...grpc.CallOption) (*Attachment, error)
// DeleteAttachment deletes a attachment by name. // DeleteAttachment deletes a attachment by name.
@ -85,16 +81,6 @@ func (c *attachmentServiceClient) GetAttachment(ctx context.Context, in *GetAtta
return out, nil return out, nil
} }
func (c *attachmentServiceClient) GetAttachmentBinary(ctx context.Context, in *GetAttachmentBinaryRequest, opts ...grpc.CallOption) (*httpbody.HttpBody, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(httpbody.HttpBody)
err := c.cc.Invoke(ctx, AttachmentService_GetAttachmentBinary_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *attachmentServiceClient) UpdateAttachment(ctx context.Context, in *UpdateAttachmentRequest, opts ...grpc.CallOption) (*Attachment, error) { func (c *attachmentServiceClient) UpdateAttachment(ctx context.Context, in *UpdateAttachmentRequest, opts ...grpc.CallOption) (*Attachment, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(Attachment) out := new(Attachment)
@ -125,8 +111,6 @@ type AttachmentServiceServer interface {
ListAttachments(context.Context, *ListAttachmentsRequest) (*ListAttachmentsResponse, error) ListAttachments(context.Context, *ListAttachmentsRequest) (*ListAttachmentsResponse, error)
// GetAttachment returns a attachment by name. // GetAttachment returns a attachment by name.
GetAttachment(context.Context, *GetAttachmentRequest) (*Attachment, error) GetAttachment(context.Context, *GetAttachmentRequest) (*Attachment, error)
// GetAttachmentBinary returns a attachment binary by name.
GetAttachmentBinary(context.Context, *GetAttachmentBinaryRequest) (*httpbody.HttpBody, error)
// UpdateAttachment updates a attachment. // UpdateAttachment updates a attachment.
UpdateAttachment(context.Context, *UpdateAttachmentRequest) (*Attachment, error) UpdateAttachment(context.Context, *UpdateAttachmentRequest) (*Attachment, error)
// DeleteAttachment deletes a attachment by name. // DeleteAttachment deletes a attachment by name.
@ -142,22 +126,19 @@ type AttachmentServiceServer interface {
type UnimplementedAttachmentServiceServer struct{} type UnimplementedAttachmentServiceServer struct{}
func (UnimplementedAttachmentServiceServer) CreateAttachment(context.Context, *CreateAttachmentRequest) (*Attachment, error) { func (UnimplementedAttachmentServiceServer) CreateAttachment(context.Context, *CreateAttachmentRequest) (*Attachment, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateAttachment not implemented") return nil, status.Error(codes.Unimplemented, "method CreateAttachment not implemented")
} }
func (UnimplementedAttachmentServiceServer) ListAttachments(context.Context, *ListAttachmentsRequest) (*ListAttachmentsResponse, error) { func (UnimplementedAttachmentServiceServer) ListAttachments(context.Context, *ListAttachmentsRequest) (*ListAttachmentsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListAttachments not implemented") return nil, status.Error(codes.Unimplemented, "method ListAttachments not implemented")
} }
func (UnimplementedAttachmentServiceServer) GetAttachment(context.Context, *GetAttachmentRequest) (*Attachment, error) { func (UnimplementedAttachmentServiceServer) GetAttachment(context.Context, *GetAttachmentRequest) (*Attachment, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetAttachment not implemented") return nil, status.Error(codes.Unimplemented, "method GetAttachment not implemented")
}
func (UnimplementedAttachmentServiceServer) GetAttachmentBinary(context.Context, *GetAttachmentBinaryRequest) (*httpbody.HttpBody, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetAttachmentBinary not implemented")
} }
func (UnimplementedAttachmentServiceServer) UpdateAttachment(context.Context, *UpdateAttachmentRequest) (*Attachment, error) { func (UnimplementedAttachmentServiceServer) UpdateAttachment(context.Context, *UpdateAttachmentRequest) (*Attachment, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateAttachment not implemented") return nil, status.Error(codes.Unimplemented, "method UpdateAttachment not implemented")
} }
func (UnimplementedAttachmentServiceServer) DeleteAttachment(context.Context, *DeleteAttachmentRequest) (*emptypb.Empty, error) { func (UnimplementedAttachmentServiceServer) DeleteAttachment(context.Context, *DeleteAttachmentRequest) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteAttachment not implemented") return nil, status.Error(codes.Unimplemented, "method DeleteAttachment not implemented")
} }
func (UnimplementedAttachmentServiceServer) mustEmbedUnimplementedAttachmentServiceServer() {} func (UnimplementedAttachmentServiceServer) mustEmbedUnimplementedAttachmentServiceServer() {}
func (UnimplementedAttachmentServiceServer) testEmbeddedByValue() {} func (UnimplementedAttachmentServiceServer) testEmbeddedByValue() {}
@ -170,7 +151,7 @@ type UnsafeAttachmentServiceServer interface {
} }
func RegisterAttachmentServiceServer(s grpc.ServiceRegistrar, srv AttachmentServiceServer) { func RegisterAttachmentServiceServer(s grpc.ServiceRegistrar, srv AttachmentServiceServer) {
// If the following call pancis, it indicates UnimplementedAttachmentServiceServer was // If the following call panics, it indicates UnimplementedAttachmentServiceServer was
// embedded by pointer and is nil. This will cause panics if an // embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization // unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O. // time to prevent it from happening at runtime later due to I/O.
@ -234,24 +215,6 @@ func _AttachmentService_GetAttachment_Handler(srv interface{}, ctx context.Conte
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _AttachmentService_GetAttachmentBinary_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetAttachmentBinaryRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AttachmentServiceServer).GetAttachmentBinary(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: AttachmentService_GetAttachmentBinary_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AttachmentServiceServer).GetAttachmentBinary(ctx, req.(*GetAttachmentBinaryRequest))
}
return interceptor(ctx, in, info, handler)
}
func _AttachmentService_UpdateAttachment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { func _AttachmentService_UpdateAttachment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateAttachmentRequest) in := new(UpdateAttachmentRequest)
if err := dec(in); err != nil { if err := dec(in); err != nil {
@ -307,10 +270,6 @@ var AttachmentService_ServiceDesc = grpc.ServiceDesc{
MethodName: "GetAttachment", MethodName: "GetAttachment",
Handler: _AttachmentService_GetAttachment_Handler, Handler: _AttachmentService_GetAttachment_Handler,
}, },
{
MethodName: "GetAttachmentBinary",
Handler: _AttachmentService_GetAttachmentBinary_Handler,
},
{ {
MethodName: "UpdateAttachment", MethodName: "UpdateAttachment",
Handler: _AttachmentService_UpdateAttachment_Handler, Handler: _AttachmentService_UpdateAttachment_Handler,

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.10 // protoc-gen-go v1.36.11
// protoc (unknown) // protoc (unknown)
// source: api/v1/auth_service.proto // source: api/v1/auth_service.proto
@ -360,7 +360,10 @@ type CreateSessionRequest_SSOCredentials struct {
Code string `protobuf:"bytes,2,opt,name=code,proto3" json:"code,omitempty"` Code string `protobuf:"bytes,2,opt,name=code,proto3" json:"code,omitempty"`
// The redirect URI used in the SSO flow. // The redirect URI used in the SSO flow.
// Required field for security validation. // Required field for security validation.
RedirectUri string `protobuf:"bytes,3,opt,name=redirect_uri,json=redirectUri,proto3" json:"redirect_uri,omitempty"` RedirectUri string `protobuf:"bytes,3,opt,name=redirect_uri,json=redirectUri,proto3" json:"redirect_uri,omitempty"`
// The PKCE code verifier for enhanced security (RFC 7636).
// Optional field - if provided, enables PKCE flow protection against authorization code interception.
CodeVerifier string `protobuf:"bytes,4,opt,name=code_verifier,json=codeVerifier,proto3" json:"code_verifier,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@ -416,6 +419,13 @@ func (x *CreateSessionRequest_SSOCredentials) GetRedirectUri() string {
return "" return ""
} }
func (x *CreateSessionRequest_SSOCredentials) GetCodeVerifier() string {
if x != nil {
return x.CodeVerifier
}
return ""
}
var File_api_v1_auth_service_proto protoreflect.FileDescriptor var File_api_v1_auth_service_proto protoreflect.FileDescriptor
const file_api_v1_auth_service_proto_rawDesc = "" + const file_api_v1_auth_service_proto_rawDesc = "" +
@ -424,17 +434,18 @@ const file_api_v1_auth_service_proto_rawDesc = "" +
"\x18GetCurrentSessionRequest\"\x89\x01\n" + "\x18GetCurrentSessionRequest\"\x89\x01\n" +
"\x19GetCurrentSessionResponse\x12&\n" + "\x19GetCurrentSessionResponse\x12&\n" +
"\x04user\x18\x01 \x01(\v2\x12.memos.api.v1.UserR\x04user\x12D\n" + "\x04user\x18\x01 \x01(\v2\x12.memos.api.v1.UserR\x04user\x12D\n" +
"\x10last_accessed_at\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x0elastAccessedAt\"\xb8\x03\n" + "\x10last_accessed_at\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\x0elastAccessedAt\"\xe3\x03\n" +
"\x14CreateSessionRequest\x12k\n" + "\x14CreateSessionRequest\x12k\n" +
"\x14password_credentials\x18\x01 \x01(\v26.memos.api.v1.CreateSessionRequest.PasswordCredentialsH\x00R\x13passwordCredentials\x12\\\n" + "\x14password_credentials\x18\x01 \x01(\v26.memos.api.v1.CreateSessionRequest.PasswordCredentialsH\x00R\x13passwordCredentials\x12\\\n" +
"\x0fsso_credentials\x18\x02 \x01(\v21.memos.api.v1.CreateSessionRequest.SSOCredentialsH\x00R\x0essoCredentials\x1aW\n" + "\x0fsso_credentials\x18\x02 \x01(\v21.memos.api.v1.CreateSessionRequest.SSOCredentialsH\x00R\x0essoCredentials\x1aW\n" +
"\x13PasswordCredentials\x12\x1f\n" + "\x13PasswordCredentials\x12\x1f\n" +
"\busername\x18\x01 \x01(\tB\x03\xe0A\x02R\busername\x12\x1f\n" + "\busername\x18\x01 \x01(\tB\x03\xe0A\x02R\busername\x12\x1f\n" +
"\bpassword\x18\x02 \x01(\tB\x03\xe0A\x02R\bpassword\x1am\n" + "\bpassword\x18\x02 \x01(\tB\x03\xe0A\x02R\bpassword\x1a\x97\x01\n" +
"\x0eSSOCredentials\x12\x1a\n" + "\x0eSSOCredentials\x12\x1a\n" +
"\x06idp_id\x18\x01 \x01(\x05B\x03\xe0A\x02R\x05idpId\x12\x17\n" + "\x06idp_id\x18\x01 \x01(\x05B\x03\xe0A\x02R\x05idpId\x12\x17\n" +
"\x04code\x18\x02 \x01(\tB\x03\xe0A\x02R\x04code\x12&\n" + "\x04code\x18\x02 \x01(\tB\x03\xe0A\x02R\x04code\x12&\n" +
"\fredirect_uri\x18\x03 \x01(\tB\x03\xe0A\x02R\vredirectUriB\r\n" + "\fredirect_uri\x18\x03 \x01(\tB\x03\xe0A\x02R\vredirectUri\x12(\n" +
"\rcode_verifier\x18\x04 \x01(\tB\x03\xe0A\x01R\fcodeVerifierB\r\n" +
"\vcredentials\"\x85\x01\n" + "\vcredentials\"\x85\x01\n" +
"\x15CreateSessionResponse\x12&\n" + "\x15CreateSessionResponse\x12&\n" +
"\x04user\x18\x01 \x01(\v2\x12.memos.api.v1.UserR\x04user\x12D\n" + "\x04user\x18\x01 \x01(\v2\x12.memos.api.v1.UserR\x04user\x12D\n" +

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions: // versions:
// - protoc-gen-go-grpc v1.5.1 // - protoc-gen-go-grpc v1.6.0
// - protoc (unknown) // - protoc (unknown)
// source: api/v1/auth_service.proto // source: api/v1/auth_service.proto
@ -102,13 +102,13 @@ type AuthServiceServer interface {
type UnimplementedAuthServiceServer struct{} type UnimplementedAuthServiceServer struct{}
func (UnimplementedAuthServiceServer) GetCurrentSession(context.Context, *GetCurrentSessionRequest) (*GetCurrentSessionResponse, error) { func (UnimplementedAuthServiceServer) GetCurrentSession(context.Context, *GetCurrentSessionRequest) (*GetCurrentSessionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetCurrentSession not implemented") return nil, status.Error(codes.Unimplemented, "method GetCurrentSession not implemented")
} }
func (UnimplementedAuthServiceServer) CreateSession(context.Context, *CreateSessionRequest) (*CreateSessionResponse, error) { func (UnimplementedAuthServiceServer) CreateSession(context.Context, *CreateSessionRequest) (*CreateSessionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateSession not implemented") return nil, status.Error(codes.Unimplemented, "method CreateSession not implemented")
} }
func (UnimplementedAuthServiceServer) DeleteSession(context.Context, *DeleteSessionRequest) (*emptypb.Empty, error) { func (UnimplementedAuthServiceServer) DeleteSession(context.Context, *DeleteSessionRequest) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteSession not implemented") return nil, status.Error(codes.Unimplemented, "method DeleteSession not implemented")
} }
func (UnimplementedAuthServiceServer) mustEmbedUnimplementedAuthServiceServer() {} func (UnimplementedAuthServiceServer) mustEmbedUnimplementedAuthServiceServer() {}
func (UnimplementedAuthServiceServer) testEmbeddedByValue() {} func (UnimplementedAuthServiceServer) testEmbeddedByValue() {}
@ -121,7 +121,7 @@ type UnsafeAuthServiceServer interface {
} }
func RegisterAuthServiceServer(s grpc.ServiceRegistrar, srv AuthServiceServer) { func RegisterAuthServiceServer(s grpc.ServiceRegistrar, srv AuthServiceServer) {
// If the following call pancis, it indicates UnimplementedAuthServiceServer was // If the following call panics, it indicates UnimplementedAuthServiceServer was
// embedded by pointer and is nil. This will cause panics if an // embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization // unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O. // time to prevent it from happening at runtime later due to I/O.

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.10 // protoc-gen-go v1.36.11
// protoc (unknown) // protoc (unknown)
// source: api/v1/common.proto // source: api/v1/common.proto

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.10 // protoc-gen-go v1.36.11
// protoc (unknown) // protoc (unknown)
// source: api/v1/idp_service.proto // source: api/v1/idp_service.proto
@ -74,7 +74,7 @@ func (IdentityProvider_Type) EnumDescriptor() ([]byte, []int) {
type IdentityProvider struct { type IdentityProvider struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
// The resource name of the identity provider. // The resource name of the identity provider.
// Format: identityProviders/{idp} // Format: identity-providers/{idp}
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// Required. The type of the identity provider. // Required. The type of the identity provider.
Type IdentityProvider_Type `protobuf:"varint,2,opt,name=type,proto3,enum=memos.api.v1.IdentityProvider_Type" json:"type,omitempty"` Type IdentityProvider_Type `protobuf:"varint,2,opt,name=type,proto3,enum=memos.api.v1.IdentityProvider_Type" json:"type,omitempty"`
@ -463,7 +463,7 @@ func (x *ListIdentityProvidersResponse) GetIdentityProviders() []*IdentityProvid
type GetIdentityProviderRequest struct { type GetIdentityProviderRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
// Required. The resource name of the identity provider to get. // Required. The resource name of the identity provider to get.
// Format: identityProviders/{idp} // Format: identity-providers/{idp}
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -619,7 +619,7 @@ func (x *UpdateIdentityProviderRequest) GetUpdateMask() *fieldmaskpb.FieldMask {
type DeleteIdentityProviderRequest struct { type DeleteIdentityProviderRequest struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
// Required. The resource name of the identity provider to delete. // Required. The resource name of the identity provider to delete.
// Format: identityProviders/{idp} // Format: identity-providers/{idp}
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -666,7 +666,7 @@ var File_api_v1_idp_service_proto protoreflect.FileDescriptor
const file_api_v1_idp_service_proto_rawDesc = "" + const file_api_v1_idp_service_proto_rawDesc = "" +
"\n" + "\n" +
"\x18api/v1/idp_service.proto\x12\fmemos.api.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\"\x8b\x03\n" + "\x18api/v1/idp_service.proto\x12\fmemos.api.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\"\x8c\x03\n" +
"\x10IdentityProvider\x12\x17\n" + "\x10IdentityProvider\x12\x17\n" +
"\x04name\x18\x01 \x01(\tB\x03\xe0A\bR\x04name\x12<\n" + "\x04name\x18\x01 \x01(\tB\x03\xe0A\bR\x04name\x12<\n" +
"\x04type\x18\x02 \x01(\x0e2#.memos.api.v1.IdentityProvider.TypeB\x03\xe0A\x02R\x04type\x12\x19\n" + "\x04type\x18\x02 \x01(\x0e2#.memos.api.v1.IdentityProvider.TypeB\x03\xe0A\x02R\x04type\x12\x19\n" +
@ -676,8 +676,8 @@ const file_api_v1_idp_service_proto_rawDesc = "" +
"\x04Type\x12\x14\n" + "\x04Type\x12\x14\n" +
"\x10TYPE_UNSPECIFIED\x10\x00\x12\n" + "\x10TYPE_UNSPECIFIED\x10\x00\x12\n" +
"\n" + "\n" +
"\x06OAUTH2\x10\x01:f\xeaAc\n" + "\x06OAUTH2\x10\x01:g\xeaAd\n" +
"\x1dmemos.api.v1/IdentityProvider\x12\x17identityProviders/{idp}\x1a\x04name*\x11identityProviders2\x10identityProvider\"e\n" + "\x1dmemos.api.v1/IdentityProvider\x12\x18identity-providers/{idp}\x1a\x04name*\x11identityProviders2\x10identityProvider\"e\n" +
"\x16IdentityProviderConfig\x12A\n" + "\x16IdentityProviderConfig\x12A\n" +
"\roauth2_config\x18\x01 \x01(\v2\x1a.memos.api.v1.OAuth2ConfigH\x00R\foauth2ConfigB\b\n" + "\roauth2_config\x18\x01 \x01(\v2\x1a.memos.api.v1.OAuth2ConfigH\x00R\foauth2ConfigB\b\n" +
"\x06config\"\x86\x01\n" + "\x06config\"\x86\x01\n" +
@ -712,13 +712,13 @@ const file_api_v1_idp_service_proto_rawDesc = "" +
"updateMask\"Z\n" + "updateMask\"Z\n" +
"\x1dDeleteIdentityProviderRequest\x129\n" + "\x1dDeleteIdentityProviderRequest\x129\n" +
"\x04name\x18\x01 \x01(\tB%\xe0A\x02\xfaA\x1f\n" + "\x04name\x18\x01 \x01(\tB%\xe0A\x02\xfaA\x1f\n" +
"\x1dmemos.api.v1/IdentityProviderR\x04name2\xe2\x06\n" + "\x1dmemos.api.v1/IdentityProviderR\x04name2\xe7\x06\n" +
"\x17IdentityProviderService\x12\x93\x01\n" + "\x17IdentityProviderService\x12\x94\x01\n" +
"\x15ListIdentityProviders\x12*.memos.api.v1.ListIdentityProvidersRequest\x1a+.memos.api.v1.ListIdentityProvidersResponse\"!\x82\xd3\xe4\x93\x02\x1b\x12\x19/api/v1/identityProviders\x12\x92\x01\n" + "\x15ListIdentityProviders\x12*.memos.api.v1.ListIdentityProvidersRequest\x1a+.memos.api.v1.ListIdentityProvidersResponse\"\"\x82\xd3\xe4\x93\x02\x1c\x12\x1a/api/v1/identity-providers\x12\x93\x01\n" +
"\x13GetIdentityProvider\x12(.memos.api.v1.GetIdentityProviderRequest\x1a\x1e.memos.api.v1.IdentityProvider\"1\xdaA\x04name\x82\xd3\xe4\x93\x02$\x12\"/api/v1/{name=identityProviders/*}\x12\xaf\x01\n" + "\x13GetIdentityProvider\x12(.memos.api.v1.GetIdentityProviderRequest\x1a\x1e.memos.api.v1.IdentityProvider\"2\xdaA\x04name\x82\xd3\xe4\x93\x02%\x12#/api/v1/{name=identity-providers/*}\x12\xb0\x01\n" +
"\x16CreateIdentityProvider\x12+.memos.api.v1.CreateIdentityProviderRequest\x1a\x1e.memos.api.v1.IdentityProvider\"H\xdaA\x11identity_provider\x82\xd3\xe4\x93\x02.:\x11identity_provider\"\x19/api/v1/identityProviders\x12\xd6\x01\n" + "\x16CreateIdentityProvider\x12+.memos.api.v1.CreateIdentityProviderRequest\x1a\x1e.memos.api.v1.IdentityProvider\"I\xdaA\x11identity_provider\x82\xd3\xe4\x93\x02/:\x11identity_provider\"\x1a/api/v1/identity-providers\x12\xd7\x01\n" +
"\x16UpdateIdentityProvider\x12+.memos.api.v1.UpdateIdentityProviderRequest\x1a\x1e.memos.api.v1.IdentityProvider\"o\xdaA\x1didentity_provider,update_mask\x82\xd3\xe4\x93\x02I:\x11identity_provider24/api/v1/{identity_provider.name=identityProviders/*}\x12\x90\x01\n" + "\x16UpdateIdentityProvider\x12+.memos.api.v1.UpdateIdentityProviderRequest\x1a\x1e.memos.api.v1.IdentityProvider\"p\xdaA\x1didentity_provider,update_mask\x82\xd3\xe4\x93\x02J:\x11identity_provider25/api/v1/{identity_provider.name=identity-providers/*}\x12\x91\x01\n" +
"\x16DeleteIdentityProvider\x12+.memos.api.v1.DeleteIdentityProviderRequest\x1a\x16.google.protobuf.Empty\"1\xdaA\x04name\x82\xd3\xe4\x93\x02$*\"/api/v1/{name=identityProviders/*}B\xa7\x01\n" + "\x16DeleteIdentityProvider\x12+.memos.api.v1.DeleteIdentityProviderRequest\x1a\x16.google.protobuf.Empty\"2\xdaA\x04name\x82\xd3\xe4\x93\x02%*#/api/v1/{name=identity-providers/*}B\xa7\x01\n" +
"\x10com.memos.api.v1B\x0fIdpServiceProtoP\x01Z0github.com/usememos/memos/proto/gen/api/v1;apiv1\xa2\x02\x03MAX\xaa\x02\fMemos.Api.V1\xca\x02\fMemos\\Api\\V1\xe2\x02\x18Memos\\Api\\V1\\GPBMetadata\xea\x02\x0eMemos::Api::V1b\x06proto3" "\x10com.memos.api.v1B\x0fIdpServiceProtoP\x01Z0github.com/usememos/memos/proto/gen/api/v1;apiv1\xa2\x02\x03MAX\xaa\x02\fMemos.Api.V1\xca\x02\fMemos\\Api\\V1\xe2\x02\x18Memos\\Api\\V1\\GPBMetadata\xea\x02\x0eMemos::Api::V1b\x06proto3"
var ( var (

View File

@ -268,7 +268,7 @@ func RegisterIdentityProviderServiceHandlerServer(ctx context.Context, mux *runt
var stream runtime.ServerTransportStream var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.IdentityProviderService/ListIdentityProviders", runtime.WithHTTPPathPattern("/api/v1/identityProviders")) annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.IdentityProviderService/ListIdentityProviders", runtime.WithHTTPPathPattern("/api/v1/identity-providers"))
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
@ -288,7 +288,7 @@ func RegisterIdentityProviderServiceHandlerServer(ctx context.Context, mux *runt
var stream runtime.ServerTransportStream var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.IdentityProviderService/GetIdentityProvider", runtime.WithHTTPPathPattern("/api/v1/{name=identityProviders/*}")) annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.IdentityProviderService/GetIdentityProvider", runtime.WithHTTPPathPattern("/api/v1/{name=identity-providers/*}"))
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
@ -308,7 +308,7 @@ func RegisterIdentityProviderServiceHandlerServer(ctx context.Context, mux *runt
var stream runtime.ServerTransportStream var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.IdentityProviderService/CreateIdentityProvider", runtime.WithHTTPPathPattern("/api/v1/identityProviders")) annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.IdentityProviderService/CreateIdentityProvider", runtime.WithHTTPPathPattern("/api/v1/identity-providers"))
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
@ -328,7 +328,7 @@ func RegisterIdentityProviderServiceHandlerServer(ctx context.Context, mux *runt
var stream runtime.ServerTransportStream var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.IdentityProviderService/UpdateIdentityProvider", runtime.WithHTTPPathPattern("/api/v1/{identity_provider.name=identityProviders/*}")) annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.IdentityProviderService/UpdateIdentityProvider", runtime.WithHTTPPathPattern("/api/v1/{identity_provider.name=identity-providers/*}"))
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
@ -348,7 +348,7 @@ func RegisterIdentityProviderServiceHandlerServer(ctx context.Context, mux *runt
var stream runtime.ServerTransportStream var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.IdentityProviderService/DeleteIdentityProvider", runtime.WithHTTPPathPattern("/api/v1/{name=identityProviders/*}")) annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.IdentityProviderService/DeleteIdentityProvider", runtime.WithHTTPPathPattern("/api/v1/{name=identity-providers/*}"))
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
@ -406,7 +406,7 @@ func RegisterIdentityProviderServiceHandlerClient(ctx context.Context, mux *runt
ctx, cancel := context.WithCancel(req.Context()) ctx, cancel := context.WithCancel(req.Context())
defer cancel() defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.IdentityProviderService/ListIdentityProviders", runtime.WithHTTPPathPattern("/api/v1/identityProviders")) annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.IdentityProviderService/ListIdentityProviders", runtime.WithHTTPPathPattern("/api/v1/identity-providers"))
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
@ -423,7 +423,7 @@ func RegisterIdentityProviderServiceHandlerClient(ctx context.Context, mux *runt
ctx, cancel := context.WithCancel(req.Context()) ctx, cancel := context.WithCancel(req.Context())
defer cancel() defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.IdentityProviderService/GetIdentityProvider", runtime.WithHTTPPathPattern("/api/v1/{name=identityProviders/*}")) annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.IdentityProviderService/GetIdentityProvider", runtime.WithHTTPPathPattern("/api/v1/{name=identity-providers/*}"))
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
@ -440,7 +440,7 @@ func RegisterIdentityProviderServiceHandlerClient(ctx context.Context, mux *runt
ctx, cancel := context.WithCancel(req.Context()) ctx, cancel := context.WithCancel(req.Context())
defer cancel() defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.IdentityProviderService/CreateIdentityProvider", runtime.WithHTTPPathPattern("/api/v1/identityProviders")) annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.IdentityProviderService/CreateIdentityProvider", runtime.WithHTTPPathPattern("/api/v1/identity-providers"))
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
@ -457,7 +457,7 @@ func RegisterIdentityProviderServiceHandlerClient(ctx context.Context, mux *runt
ctx, cancel := context.WithCancel(req.Context()) ctx, cancel := context.WithCancel(req.Context())
defer cancel() defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.IdentityProviderService/UpdateIdentityProvider", runtime.WithHTTPPathPattern("/api/v1/{identity_provider.name=identityProviders/*}")) annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.IdentityProviderService/UpdateIdentityProvider", runtime.WithHTTPPathPattern("/api/v1/{identity_provider.name=identity-providers/*}"))
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
@ -474,7 +474,7 @@ func RegisterIdentityProviderServiceHandlerClient(ctx context.Context, mux *runt
ctx, cancel := context.WithCancel(req.Context()) ctx, cancel := context.WithCancel(req.Context())
defer cancel() defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.IdentityProviderService/DeleteIdentityProvider", runtime.WithHTTPPathPattern("/api/v1/{name=identityProviders/*}")) annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.IdentityProviderService/DeleteIdentityProvider", runtime.WithHTTPPathPattern("/api/v1/{name=identity-providers/*}"))
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
@ -491,11 +491,11 @@ func RegisterIdentityProviderServiceHandlerClient(ctx context.Context, mux *runt
} }
var ( var (
pattern_IdentityProviderService_ListIdentityProviders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "identityProviders"}, "")) pattern_IdentityProviderService_ListIdentityProviders_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "identity-providers"}, ""))
pattern_IdentityProviderService_GetIdentityProvider_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "identityProviders", "name"}, "")) pattern_IdentityProviderService_GetIdentityProvider_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "identity-providers", "name"}, ""))
pattern_IdentityProviderService_CreateIdentityProvider_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "identityProviders"}, "")) pattern_IdentityProviderService_CreateIdentityProvider_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "identity-providers"}, ""))
pattern_IdentityProviderService_UpdateIdentityProvider_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "identityProviders", "identity_provider.name"}, "")) pattern_IdentityProviderService_UpdateIdentityProvider_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "identity-providers", "identity_provider.name"}, ""))
pattern_IdentityProviderService_DeleteIdentityProvider_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "identityProviders", "name"}, "")) pattern_IdentityProviderService_DeleteIdentityProvider_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "identity-providers", "name"}, ""))
) )
var ( var (

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions: // versions:
// - protoc-gen-go-grpc v1.5.1 // - protoc-gen-go-grpc v1.6.0
// - protoc (unknown) // - protoc (unknown)
// source: api/v1/idp_service.proto // source: api/v1/idp_service.proto
@ -126,19 +126,19 @@ type IdentityProviderServiceServer interface {
type UnimplementedIdentityProviderServiceServer struct{} type UnimplementedIdentityProviderServiceServer struct{}
func (UnimplementedIdentityProviderServiceServer) ListIdentityProviders(context.Context, *ListIdentityProvidersRequest) (*ListIdentityProvidersResponse, error) { func (UnimplementedIdentityProviderServiceServer) ListIdentityProviders(context.Context, *ListIdentityProvidersRequest) (*ListIdentityProvidersResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListIdentityProviders not implemented") return nil, status.Error(codes.Unimplemented, "method ListIdentityProviders not implemented")
} }
func (UnimplementedIdentityProviderServiceServer) GetIdentityProvider(context.Context, *GetIdentityProviderRequest) (*IdentityProvider, error) { func (UnimplementedIdentityProviderServiceServer) GetIdentityProvider(context.Context, *GetIdentityProviderRequest) (*IdentityProvider, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetIdentityProvider not implemented") return nil, status.Error(codes.Unimplemented, "method GetIdentityProvider not implemented")
} }
func (UnimplementedIdentityProviderServiceServer) CreateIdentityProvider(context.Context, *CreateIdentityProviderRequest) (*IdentityProvider, error) { func (UnimplementedIdentityProviderServiceServer) CreateIdentityProvider(context.Context, *CreateIdentityProviderRequest) (*IdentityProvider, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateIdentityProvider not implemented") return nil, status.Error(codes.Unimplemented, "method CreateIdentityProvider not implemented")
} }
func (UnimplementedIdentityProviderServiceServer) UpdateIdentityProvider(context.Context, *UpdateIdentityProviderRequest) (*IdentityProvider, error) { func (UnimplementedIdentityProviderServiceServer) UpdateIdentityProvider(context.Context, *UpdateIdentityProviderRequest) (*IdentityProvider, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateIdentityProvider not implemented") return nil, status.Error(codes.Unimplemented, "method UpdateIdentityProvider not implemented")
} }
func (UnimplementedIdentityProviderServiceServer) DeleteIdentityProvider(context.Context, *DeleteIdentityProviderRequest) (*emptypb.Empty, error) { func (UnimplementedIdentityProviderServiceServer) DeleteIdentityProvider(context.Context, *DeleteIdentityProviderRequest) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteIdentityProvider not implemented") return nil, status.Error(codes.Unimplemented, "method DeleteIdentityProvider not implemented")
} }
func (UnimplementedIdentityProviderServiceServer) mustEmbedUnimplementedIdentityProviderServiceServer() { func (UnimplementedIdentityProviderServiceServer) mustEmbedUnimplementedIdentityProviderServiceServer() {
} }
@ -152,7 +152,7 @@ type UnsafeIdentityProviderServiceServer interface {
} }
func RegisterIdentityProviderServiceServer(s grpc.ServiceRegistrar, srv IdentityProviderServiceServer) { func RegisterIdentityProviderServiceServer(s grpc.ServiceRegistrar, srv IdentityProviderServiceServer) {
// If the following call pancis, it indicates UnimplementedIdentityProviderServiceServer was // If the following call panics, it indicates UnimplementedIdentityProviderServiceServer was
// embedded by pointer and is nil. This will cause panics if an // embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization // unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O. // time to prevent it from happening at runtime later due to I/O.

View File

@ -1,622 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.10
// protoc (unknown)
// source: api/v1/inbox_service.proto
package apiv1
import (
_ "google.golang.org/genproto/googleapis/api/annotations"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
emptypb "google.golang.org/protobuf/types/known/emptypb"
fieldmaskpb "google.golang.org/protobuf/types/known/fieldmaskpb"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// Status enumeration for inbox notifications.
type Inbox_Status int32
const (
// Unspecified status.
Inbox_STATUS_UNSPECIFIED Inbox_Status = 0
// The notification is unread.
Inbox_UNREAD Inbox_Status = 1
// The notification is archived.
Inbox_ARCHIVED Inbox_Status = 2
)
// Enum value maps for Inbox_Status.
var (
Inbox_Status_name = map[int32]string{
0: "STATUS_UNSPECIFIED",
1: "UNREAD",
2: "ARCHIVED",
}
Inbox_Status_value = map[string]int32{
"STATUS_UNSPECIFIED": 0,
"UNREAD": 1,
"ARCHIVED": 2,
}
)
func (x Inbox_Status) Enum() *Inbox_Status {
p := new(Inbox_Status)
*p = x
return p
}
func (x Inbox_Status) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Inbox_Status) Descriptor() protoreflect.EnumDescriptor {
return file_api_v1_inbox_service_proto_enumTypes[0].Descriptor()
}
func (Inbox_Status) Type() protoreflect.EnumType {
return &file_api_v1_inbox_service_proto_enumTypes[0]
}
func (x Inbox_Status) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Inbox_Status.Descriptor instead.
func (Inbox_Status) EnumDescriptor() ([]byte, []int) {
return file_api_v1_inbox_service_proto_rawDescGZIP(), []int{0, 0}
}
// Type enumeration for inbox notifications.
type Inbox_Type int32
const (
// Unspecified type.
Inbox_TYPE_UNSPECIFIED Inbox_Type = 0
// Memo comment notification.
Inbox_MEMO_COMMENT Inbox_Type = 1
// Version update notification.
Inbox_VERSION_UPDATE Inbox_Type = 2
)
// Enum value maps for Inbox_Type.
var (
Inbox_Type_name = map[int32]string{
0: "TYPE_UNSPECIFIED",
1: "MEMO_COMMENT",
2: "VERSION_UPDATE",
}
Inbox_Type_value = map[string]int32{
"TYPE_UNSPECIFIED": 0,
"MEMO_COMMENT": 1,
"VERSION_UPDATE": 2,
}
)
func (x Inbox_Type) Enum() *Inbox_Type {
p := new(Inbox_Type)
*p = x
return p
}
func (x Inbox_Type) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Inbox_Type) Descriptor() protoreflect.EnumDescriptor {
return file_api_v1_inbox_service_proto_enumTypes[1].Descriptor()
}
func (Inbox_Type) Type() protoreflect.EnumType {
return &file_api_v1_inbox_service_proto_enumTypes[1]
}
func (x Inbox_Type) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Inbox_Type.Descriptor instead.
func (Inbox_Type) EnumDescriptor() ([]byte, []int) {
return file_api_v1_inbox_service_proto_rawDescGZIP(), []int{0, 1}
}
type Inbox struct {
state protoimpl.MessageState `protogen:"open.v1"`
// The resource name of the inbox.
// Format: inboxes/{inbox}
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// The sender of the inbox notification.
// Format: users/{user}
Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty"`
// The receiver of the inbox notification.
// Format: users/{user}
Receiver string `protobuf:"bytes,3,opt,name=receiver,proto3" json:"receiver,omitempty"`
// The status of the inbox notification.
Status Inbox_Status `protobuf:"varint,4,opt,name=status,proto3,enum=memos.api.v1.Inbox_Status" json:"status,omitempty"`
// Output only. The creation timestamp.
CreateTime *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=create_time,json=createTime,proto3" json:"create_time,omitempty"`
// The type of the inbox notification.
Type Inbox_Type `protobuf:"varint,6,opt,name=type,proto3,enum=memos.api.v1.Inbox_Type" json:"type,omitempty"`
// Optional. The activity ID associated with this inbox notification.
ActivityId *int32 `protobuf:"varint,7,opt,name=activity_id,json=activityId,proto3,oneof" json:"activity_id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Inbox) Reset() {
*x = Inbox{}
mi := &file_api_v1_inbox_service_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Inbox) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Inbox) ProtoMessage() {}
func (x *Inbox) ProtoReflect() protoreflect.Message {
mi := &file_api_v1_inbox_service_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Inbox.ProtoReflect.Descriptor instead.
func (*Inbox) Descriptor() ([]byte, []int) {
return file_api_v1_inbox_service_proto_rawDescGZIP(), []int{0}
}
func (x *Inbox) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *Inbox) GetSender() string {
if x != nil {
return x.Sender
}
return ""
}
func (x *Inbox) GetReceiver() string {
if x != nil {
return x.Receiver
}
return ""
}
func (x *Inbox) GetStatus() Inbox_Status {
if x != nil {
return x.Status
}
return Inbox_STATUS_UNSPECIFIED
}
func (x *Inbox) GetCreateTime() *timestamppb.Timestamp {
if x != nil {
return x.CreateTime
}
return nil
}
func (x *Inbox) GetType() Inbox_Type {
if x != nil {
return x.Type
}
return Inbox_TYPE_UNSPECIFIED
}
func (x *Inbox) GetActivityId() int32 {
if x != nil && x.ActivityId != nil {
return *x.ActivityId
}
return 0
}
type ListInboxesRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Required. The parent resource whose inboxes will be listed.
// Format: users/{user}
Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"`
// Optional. The maximum number of inboxes to return.
// The service may return fewer than this value.
// If unspecified, at most 50 inboxes will be returned.
// The maximum value is 1000; values above 1000 will be coerced to 1000.
PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize,proto3" json:"page_size,omitempty"`
// Optional. A page token, received from a previous `ListInboxes` call.
// Provide this to retrieve the subsequent page.
PageToken string `protobuf:"bytes,3,opt,name=page_token,json=pageToken,proto3" json:"page_token,omitempty"`
// Optional. Filter to apply to the list results.
// Example: "status=UNREAD" or "type=MEMO_COMMENT"
// Supported operators: =, !=
// Supported fields: status, type, sender, create_time
Filter string `protobuf:"bytes,4,opt,name=filter,proto3" json:"filter,omitempty"`
// Optional. The order to sort results by.
// Example: "create_time desc" or "status asc"
OrderBy string `protobuf:"bytes,5,opt,name=order_by,json=orderBy,proto3" json:"order_by,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListInboxesRequest) Reset() {
*x = ListInboxesRequest{}
mi := &file_api_v1_inbox_service_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListInboxesRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListInboxesRequest) ProtoMessage() {}
func (x *ListInboxesRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_v1_inbox_service_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListInboxesRequest.ProtoReflect.Descriptor instead.
func (*ListInboxesRequest) Descriptor() ([]byte, []int) {
return file_api_v1_inbox_service_proto_rawDescGZIP(), []int{1}
}
func (x *ListInboxesRequest) GetParent() string {
if x != nil {
return x.Parent
}
return ""
}
func (x *ListInboxesRequest) GetPageSize() int32 {
if x != nil {
return x.PageSize
}
return 0
}
func (x *ListInboxesRequest) GetPageToken() string {
if x != nil {
return x.PageToken
}
return ""
}
func (x *ListInboxesRequest) GetFilter() string {
if x != nil {
return x.Filter
}
return ""
}
func (x *ListInboxesRequest) GetOrderBy() string {
if x != nil {
return x.OrderBy
}
return ""
}
type ListInboxesResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
// The list of inboxes.
Inboxes []*Inbox `protobuf:"bytes,1,rep,name=inboxes,proto3" json:"inboxes,omitempty"`
// A token that can be sent as `page_token` to retrieve the next page.
// If this field is omitted, there are no subsequent pages.
NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"`
// The total count of inboxes (may be approximate).
TotalSize int32 `protobuf:"varint,3,opt,name=total_size,json=totalSize,proto3" json:"total_size,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ListInboxesResponse) Reset() {
*x = ListInboxesResponse{}
mi := &file_api_v1_inbox_service_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ListInboxesResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListInboxesResponse) ProtoMessage() {}
func (x *ListInboxesResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_v1_inbox_service_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListInboxesResponse.ProtoReflect.Descriptor instead.
func (*ListInboxesResponse) Descriptor() ([]byte, []int) {
return file_api_v1_inbox_service_proto_rawDescGZIP(), []int{2}
}
func (x *ListInboxesResponse) GetInboxes() []*Inbox {
if x != nil {
return x.Inboxes
}
return nil
}
func (x *ListInboxesResponse) GetNextPageToken() string {
if x != nil {
return x.NextPageToken
}
return ""
}
func (x *ListInboxesResponse) GetTotalSize() int32 {
if x != nil {
return x.TotalSize
}
return 0
}
type UpdateInboxRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Required. The inbox to update.
Inbox *Inbox `protobuf:"bytes,1,opt,name=inbox,proto3" json:"inbox,omitempty"`
// Required. The list of fields to update.
UpdateMask *fieldmaskpb.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask,proto3" json:"update_mask,omitempty"`
// Optional. If set to true, allows updating missing fields.
AllowMissing bool `protobuf:"varint,3,opt,name=allow_missing,json=allowMissing,proto3" json:"allow_missing,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *UpdateInboxRequest) Reset() {
*x = UpdateInboxRequest{}
mi := &file_api_v1_inbox_service_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *UpdateInboxRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*UpdateInboxRequest) ProtoMessage() {}
func (x *UpdateInboxRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_v1_inbox_service_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use UpdateInboxRequest.ProtoReflect.Descriptor instead.
func (*UpdateInboxRequest) Descriptor() ([]byte, []int) {
return file_api_v1_inbox_service_proto_rawDescGZIP(), []int{3}
}
func (x *UpdateInboxRequest) GetInbox() *Inbox {
if x != nil {
return x.Inbox
}
return nil
}
func (x *UpdateInboxRequest) GetUpdateMask() *fieldmaskpb.FieldMask {
if x != nil {
return x.UpdateMask
}
return nil
}
func (x *UpdateInboxRequest) GetAllowMissing() bool {
if x != nil {
return x.AllowMissing
}
return false
}
type DeleteInboxRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
// Required. The resource name of the inbox to delete.
// Format: inboxes/{inbox}
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *DeleteInboxRequest) Reset() {
*x = DeleteInboxRequest{}
mi := &file_api_v1_inbox_service_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *DeleteInboxRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DeleteInboxRequest) ProtoMessage() {}
func (x *DeleteInboxRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_v1_inbox_service_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DeleteInboxRequest.ProtoReflect.Descriptor instead.
func (*DeleteInboxRequest) Descriptor() ([]byte, []int) {
return file_api_v1_inbox_service_proto_rawDescGZIP(), []int{4}
}
func (x *DeleteInboxRequest) GetName() string {
if x != nil {
return x.Name
}
return ""
}
var File_api_v1_inbox_service_proto protoreflect.FileDescriptor
const file_api_v1_inbox_service_proto_rawDesc = "" +
"\n" +
"\x1aapi/v1/inbox_service.proto\x12\fmemos.api.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/api/client.proto\x1a\x1fgoogle/api/field_behavior.proto\x1a\x19google/api/resource.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a google/protobuf/field_mask.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x87\x04\n" +
"\x05Inbox\x12\x17\n" +
"\x04name\x18\x01 \x01(\tB\x03\xe0A\bR\x04name\x12\x1b\n" +
"\x06sender\x18\x02 \x01(\tB\x03\xe0A\x03R\x06sender\x12\x1f\n" +
"\breceiver\x18\x03 \x01(\tB\x03\xe0A\x03R\breceiver\x127\n" +
"\x06status\x18\x04 \x01(\x0e2\x1a.memos.api.v1.Inbox.StatusB\x03\xe0A\x01R\x06status\x12@\n" +
"\vcreate_time\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampB\x03\xe0A\x03R\n" +
"createTime\x121\n" +
"\x04type\x18\x06 \x01(\x0e2\x18.memos.api.v1.Inbox.TypeB\x03\xe0A\x03R\x04type\x12)\n" +
"\vactivity_id\x18\a \x01(\x05B\x03\xe0A\x01H\x00R\n" +
"activityId\x88\x01\x01\":\n" +
"\x06Status\x12\x16\n" +
"\x12STATUS_UNSPECIFIED\x10\x00\x12\n" +
"\n" +
"\x06UNREAD\x10\x01\x12\f\n" +
"\bARCHIVED\x10\x02\"B\n" +
"\x04Type\x12\x14\n" +
"\x10TYPE_UNSPECIFIED\x10\x00\x12\x10\n" +
"\fMEMO_COMMENT\x10\x01\x12\x12\n" +
"\x0eVERSION_UPDATE\x10\x02:>\xeaA;\n" +
"\x12memos.api.v1/Inbox\x12\x0finboxes/{inbox}\x1a\x04name*\ainboxes2\x05inboxB\x0e\n" +
"\f_activity_id\"\xca\x01\n" +
"\x12ListInboxesRequest\x121\n" +
"\x06parent\x18\x01 \x01(\tB\x19\xe0A\x02\xfaA\x13\n" +
"\x11memos.api.v1/UserR\x06parent\x12 \n" +
"\tpage_size\x18\x02 \x01(\x05B\x03\xe0A\x01R\bpageSize\x12\"\n" +
"\n" +
"page_token\x18\x03 \x01(\tB\x03\xe0A\x01R\tpageToken\x12\x1b\n" +
"\x06filter\x18\x04 \x01(\tB\x03\xe0A\x01R\x06filter\x12\x1e\n" +
"\border_by\x18\x05 \x01(\tB\x03\xe0A\x01R\aorderBy\"\x8b\x01\n" +
"\x13ListInboxesResponse\x12-\n" +
"\ainboxes\x18\x01 \x03(\v2\x13.memos.api.v1.InboxR\ainboxes\x12&\n" +
"\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\x12\x1d\n" +
"\n" +
"total_size\x18\x03 \x01(\x05R\ttotalSize\"\xb0\x01\n" +
"\x12UpdateInboxRequest\x12.\n" +
"\x05inbox\x18\x01 \x01(\v2\x13.memos.api.v1.InboxB\x03\xe0A\x02R\x05inbox\x12@\n" +
"\vupdate_mask\x18\x02 \x01(\v2\x1a.google.protobuf.FieldMaskB\x03\xe0A\x02R\n" +
"updateMask\x12(\n" +
"\rallow_missing\x18\x03 \x01(\bB\x03\xe0A\x01R\fallowMissing\"D\n" +
"\x12DeleteInboxRequest\x12.\n" +
"\x04name\x18\x01 \x01(\tB\x1a\xe0A\x02\xfaA\x14\n" +
"\x12memos.api.v1/InboxR\x04name2\x92\x03\n" +
"\fInboxService\x12\x85\x01\n" +
"\vListInboxes\x12 .memos.api.v1.ListInboxesRequest\x1a!.memos.api.v1.ListInboxesResponse\"1\xdaA\x06parent\x82\xd3\xe4\x93\x02\"\x12 /api/v1/{parent=users/*}/inboxes\x12\x87\x01\n" +
"\vUpdateInbox\x12 .memos.api.v1.UpdateInboxRequest\x1a\x13.memos.api.v1.Inbox\"A\xdaA\x11inbox,update_mask\x82\xd3\xe4\x93\x02':\x05inbox2\x1e/api/v1/{inbox.name=inboxes/*}\x12p\n" +
"\vDeleteInbox\x12 .memos.api.v1.DeleteInboxRequest\x1a\x16.google.protobuf.Empty\"'\xdaA\x04name\x82\xd3\xe4\x93\x02\x1a*\x18/api/v1/{name=inboxes/*}B\xa9\x01\n" +
"\x10com.memos.api.v1B\x11InboxServiceProtoP\x01Z0github.com/usememos/memos/proto/gen/api/v1;apiv1\xa2\x02\x03MAX\xaa\x02\fMemos.Api.V1\xca\x02\fMemos\\Api\\V1\xe2\x02\x18Memos\\Api\\V1\\GPBMetadata\xea\x02\x0eMemos::Api::V1b\x06proto3"
var (
file_api_v1_inbox_service_proto_rawDescOnce sync.Once
file_api_v1_inbox_service_proto_rawDescData []byte
)
func file_api_v1_inbox_service_proto_rawDescGZIP() []byte {
file_api_v1_inbox_service_proto_rawDescOnce.Do(func() {
file_api_v1_inbox_service_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_api_v1_inbox_service_proto_rawDesc), len(file_api_v1_inbox_service_proto_rawDesc)))
})
return file_api_v1_inbox_service_proto_rawDescData
}
var file_api_v1_inbox_service_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_api_v1_inbox_service_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_api_v1_inbox_service_proto_goTypes = []any{
(Inbox_Status)(0), // 0: memos.api.v1.Inbox.Status
(Inbox_Type)(0), // 1: memos.api.v1.Inbox.Type
(*Inbox)(nil), // 2: memos.api.v1.Inbox
(*ListInboxesRequest)(nil), // 3: memos.api.v1.ListInboxesRequest
(*ListInboxesResponse)(nil), // 4: memos.api.v1.ListInboxesResponse
(*UpdateInboxRequest)(nil), // 5: memos.api.v1.UpdateInboxRequest
(*DeleteInboxRequest)(nil), // 6: memos.api.v1.DeleteInboxRequest
(*timestamppb.Timestamp)(nil), // 7: google.protobuf.Timestamp
(*fieldmaskpb.FieldMask)(nil), // 8: google.protobuf.FieldMask
(*emptypb.Empty)(nil), // 9: google.protobuf.Empty
}
var file_api_v1_inbox_service_proto_depIdxs = []int32{
0, // 0: memos.api.v1.Inbox.status:type_name -> memos.api.v1.Inbox.Status
7, // 1: memos.api.v1.Inbox.create_time:type_name -> google.protobuf.Timestamp
1, // 2: memos.api.v1.Inbox.type:type_name -> memos.api.v1.Inbox.Type
2, // 3: memos.api.v1.ListInboxesResponse.inboxes:type_name -> memos.api.v1.Inbox
2, // 4: memos.api.v1.UpdateInboxRequest.inbox:type_name -> memos.api.v1.Inbox
8, // 5: memos.api.v1.UpdateInboxRequest.update_mask:type_name -> google.protobuf.FieldMask
3, // 6: memos.api.v1.InboxService.ListInboxes:input_type -> memos.api.v1.ListInboxesRequest
5, // 7: memos.api.v1.InboxService.UpdateInbox:input_type -> memos.api.v1.UpdateInboxRequest
6, // 8: memos.api.v1.InboxService.DeleteInbox:input_type -> memos.api.v1.DeleteInboxRequest
4, // 9: memos.api.v1.InboxService.ListInboxes:output_type -> memos.api.v1.ListInboxesResponse
2, // 10: memos.api.v1.InboxService.UpdateInbox:output_type -> memos.api.v1.Inbox
9, // 11: memos.api.v1.InboxService.DeleteInbox:output_type -> google.protobuf.Empty
9, // [9:12] is the sub-list for method output_type
6, // [6:9] is the sub-list for method input_type
6, // [6:6] is the sub-list for extension type_name
6, // [6:6] is the sub-list for extension extendee
0, // [0:6] is the sub-list for field type_name
}
func init() { file_api_v1_inbox_service_proto_init() }
func file_api_v1_inbox_service_proto_init() {
if File_api_v1_inbox_service_proto != nil {
return
}
file_api_v1_inbox_service_proto_msgTypes[0].OneofWrappers = []any{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_v1_inbox_service_proto_rawDesc), len(file_api_v1_inbox_service_proto_rawDesc)),
NumEnums: 2,
NumMessages: 5,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_api_v1_inbox_service_proto_goTypes,
DependencyIndexes: file_api_v1_inbox_service_proto_depIdxs,
EnumInfos: file_api_v1_inbox_service_proto_enumTypes,
MessageInfos: file_api_v1_inbox_service_proto_msgTypes,
}.Build()
File_api_v1_inbox_service_proto = out.File
file_api_v1_inbox_service_proto_goTypes = nil
file_api_v1_inbox_service_proto_depIdxs = nil
}

View File

@ -1,381 +0,0 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: api/v1/inbox_service.proto
/*
Package apiv1 is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package apiv1
import (
"context"
"errors"
"io"
"net/http"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
)
// Suppress "imported and not used" errors
var (
_ codes.Code
_ io.Reader
_ status.Status
_ = errors.New
_ = runtime.String
_ = utilities.NewDoubleArray
_ = metadata.Join
)
var filter_InboxService_ListInboxes_0 = &utilities.DoubleArray{Encoding: map[string]int{"parent": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
func request_InboxService_ListInboxes_0(ctx context.Context, marshaler runtime.Marshaler, client InboxServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq ListInboxesRequest
metadata runtime.ServerMetadata
err error
)
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
val, ok := pathParams["parent"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "parent")
}
protoReq.Parent, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "parent", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_InboxService_ListInboxes_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.ListInboxes(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_InboxService_ListInboxes_0(ctx context.Context, marshaler runtime.Marshaler, server InboxServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq ListInboxesRequest
metadata runtime.ServerMetadata
err error
)
val, ok := pathParams["parent"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "parent")
}
protoReq.Parent, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "parent", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_InboxService_ListInboxes_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.ListInboxes(ctx, &protoReq)
return msg, metadata, err
}
var filter_InboxService_UpdateInbox_0 = &utilities.DoubleArray{Encoding: map[string]int{"inbox": 0, "name": 1}, Base: []int{1, 2, 1, 0, 0}, Check: []int{0, 1, 2, 3, 2}}
func request_InboxService_UpdateInbox_0(ctx context.Context, marshaler runtime.Marshaler, client InboxServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq UpdateInboxRequest
metadata runtime.ServerMetadata
err error
)
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Inbox); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
if protoReq.UpdateMask == nil || len(protoReq.UpdateMask.GetPaths()) == 0 {
if fieldMask, err := runtime.FieldMaskFromRequestBody(newReader(), protoReq.Inbox); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
} else {
protoReq.UpdateMask = fieldMask
}
}
val, ok := pathParams["inbox.name"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "inbox.name")
}
err = runtime.PopulateFieldFromPath(&protoReq, "inbox.name", val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "inbox.name", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_InboxService_UpdateInbox_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.UpdateInbox(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_InboxService_UpdateInbox_0(ctx context.Context, marshaler runtime.Marshaler, server InboxServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq UpdateInboxRequest
metadata runtime.ServerMetadata
err error
)
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Inbox); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if protoReq.UpdateMask == nil || len(protoReq.UpdateMask.GetPaths()) == 0 {
if fieldMask, err := runtime.FieldMaskFromRequestBody(newReader(), protoReq.Inbox); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
} else {
protoReq.UpdateMask = fieldMask
}
}
val, ok := pathParams["inbox.name"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "inbox.name")
}
err = runtime.PopulateFieldFromPath(&protoReq, "inbox.name", val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "inbox.name", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_InboxService_UpdateInbox_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.UpdateInbox(ctx, &protoReq)
return msg, metadata, err
}
func request_InboxService_DeleteInbox_0(ctx context.Context, marshaler runtime.Marshaler, client InboxServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq DeleteInboxRequest
metadata runtime.ServerMetadata
err error
)
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
val, ok := pathParams["name"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name")
}
protoReq.Name, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
}
msg, err := client.DeleteInbox(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_InboxService_DeleteInbox_0(ctx context.Context, marshaler runtime.Marshaler, server InboxServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq DeleteInboxRequest
metadata runtime.ServerMetadata
err error
)
val, ok := pathParams["name"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name")
}
protoReq.Name, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
}
msg, err := server.DeleteInbox(ctx, &protoReq)
return msg, metadata, err
}
// RegisterInboxServiceHandlerServer registers the http handlers for service InboxService to "mux".
// UnaryRPC :call InboxServiceServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterInboxServiceHandlerFromEndpoint instead.
// GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call.
func RegisterInboxServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server InboxServiceServer) error {
mux.Handle(http.MethodGet, pattern_InboxService_ListInboxes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.InboxService/ListInboxes", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/inboxes"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_InboxService_ListInboxes_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_InboxService_ListInboxes_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPatch, pattern_InboxService_UpdateInbox_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.InboxService/UpdateInbox", runtime.WithHTTPPathPattern("/api/v1/{inbox.name=inboxes/*}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_InboxService_UpdateInbox_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_InboxService_UpdateInbox_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodDelete, pattern_InboxService_DeleteInbox_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.InboxService/DeleteInbox", runtime.WithHTTPPathPattern("/api/v1/{name=inboxes/*}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_InboxService_DeleteInbox_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_InboxService_DeleteInbox_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// RegisterInboxServiceHandlerFromEndpoint is same as RegisterInboxServiceHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterInboxServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.NewClient(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterInboxServiceHandler(ctx, mux, conn)
}
// RegisterInboxServiceHandler registers the http handlers for service InboxService to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterInboxServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterInboxServiceHandlerClient(ctx, mux, NewInboxServiceClient(conn))
}
// RegisterInboxServiceHandlerClient registers the http handlers for service InboxService
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "InboxServiceClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "InboxServiceClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "InboxServiceClient" to call the correct interceptors. This client ignores the HTTP middlewares.
func RegisterInboxServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client InboxServiceClient) error {
mux.Handle(http.MethodGet, pattern_InboxService_ListInboxes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.InboxService/ListInboxes", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/inboxes"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_InboxService_ListInboxes_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_InboxService_ListInboxes_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPatch, pattern_InboxService_UpdateInbox_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.InboxService/UpdateInbox", runtime.WithHTTPPathPattern("/api/v1/{inbox.name=inboxes/*}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_InboxService_UpdateInbox_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_InboxService_UpdateInbox_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodDelete, pattern_InboxService_DeleteInbox_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.InboxService/DeleteInbox", runtime.WithHTTPPathPattern("/api/v1/{name=inboxes/*}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_InboxService_DeleteInbox_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_InboxService_DeleteInbox_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_InboxService_ListInboxes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "inboxes"}, ""))
pattern_InboxService_UpdateInbox_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "inboxes", "inbox.name"}, ""))
pattern_InboxService_DeleteInbox_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "inboxes", "name"}, ""))
)
var (
forward_InboxService_ListInboxes_0 = runtime.ForwardResponseMessage
forward_InboxService_UpdateInbox_0 = runtime.ForwardResponseMessage
forward_InboxService_DeleteInbox_0 = runtime.ForwardResponseMessage
)

View File

@ -1,204 +0,0 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc (unknown)
// source: api/v1/inbox_service.proto
package apiv1
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
emptypb "google.golang.org/protobuf/types/known/emptypb"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
InboxService_ListInboxes_FullMethodName = "/memos.api.v1.InboxService/ListInboxes"
InboxService_UpdateInbox_FullMethodName = "/memos.api.v1.InboxService/UpdateInbox"
InboxService_DeleteInbox_FullMethodName = "/memos.api.v1.InboxService/DeleteInbox"
)
// InboxServiceClient is the client API for InboxService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type InboxServiceClient interface {
// ListInboxes lists inboxes for a user.
ListInboxes(ctx context.Context, in *ListInboxesRequest, opts ...grpc.CallOption) (*ListInboxesResponse, error)
// UpdateInbox updates an inbox.
UpdateInbox(ctx context.Context, in *UpdateInboxRequest, opts ...grpc.CallOption) (*Inbox, error)
// DeleteInbox deletes an inbox.
DeleteInbox(ctx context.Context, in *DeleteInboxRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
}
type inboxServiceClient struct {
cc grpc.ClientConnInterface
}
func NewInboxServiceClient(cc grpc.ClientConnInterface) InboxServiceClient {
return &inboxServiceClient{cc}
}
func (c *inboxServiceClient) ListInboxes(ctx context.Context, in *ListInboxesRequest, opts ...grpc.CallOption) (*ListInboxesResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListInboxesResponse)
err := c.cc.Invoke(ctx, InboxService_ListInboxes_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *inboxServiceClient) UpdateInbox(ctx context.Context, in *UpdateInboxRequest, opts ...grpc.CallOption) (*Inbox, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(Inbox)
err := c.cc.Invoke(ctx, InboxService_UpdateInbox_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *inboxServiceClient) DeleteInbox(ctx context.Context, in *DeleteInboxRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(emptypb.Empty)
err := c.cc.Invoke(ctx, InboxService_DeleteInbox_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// InboxServiceServer is the server API for InboxService service.
// All implementations must embed UnimplementedInboxServiceServer
// for forward compatibility.
type InboxServiceServer interface {
// ListInboxes lists inboxes for a user.
ListInboxes(context.Context, *ListInboxesRequest) (*ListInboxesResponse, error)
// UpdateInbox updates an inbox.
UpdateInbox(context.Context, *UpdateInboxRequest) (*Inbox, error)
// DeleteInbox deletes an inbox.
DeleteInbox(context.Context, *DeleteInboxRequest) (*emptypb.Empty, error)
mustEmbedUnimplementedInboxServiceServer()
}
// UnimplementedInboxServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedInboxServiceServer struct{}
func (UnimplementedInboxServiceServer) ListInboxes(context.Context, *ListInboxesRequest) (*ListInboxesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListInboxes not implemented")
}
func (UnimplementedInboxServiceServer) UpdateInbox(context.Context, *UpdateInboxRequest) (*Inbox, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateInbox not implemented")
}
func (UnimplementedInboxServiceServer) DeleteInbox(context.Context, *DeleteInboxRequest) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteInbox not implemented")
}
func (UnimplementedInboxServiceServer) mustEmbedUnimplementedInboxServiceServer() {}
func (UnimplementedInboxServiceServer) testEmbeddedByValue() {}
// UnsafeInboxServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to InboxServiceServer will
// result in compilation errors.
type UnsafeInboxServiceServer interface {
mustEmbedUnimplementedInboxServiceServer()
}
func RegisterInboxServiceServer(s grpc.ServiceRegistrar, srv InboxServiceServer) {
// If the following call pancis, it indicates UnimplementedInboxServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&InboxService_ServiceDesc, srv)
}
func _InboxService_ListInboxes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListInboxesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(InboxServiceServer).ListInboxes(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: InboxService_ListInboxes_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(InboxServiceServer).ListInboxes(ctx, req.(*ListInboxesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _InboxService_UpdateInbox_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateInboxRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(InboxServiceServer).UpdateInbox(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: InboxService_UpdateInbox_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(InboxServiceServer).UpdateInbox(ctx, req.(*UpdateInboxRequest))
}
return interceptor(ctx, in, info, handler)
}
func _InboxService_DeleteInbox_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteInboxRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(InboxServiceServer).DeleteInbox(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: InboxService_DeleteInbox_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(InboxServiceServer).DeleteInbox(ctx, req.(*DeleteInboxRequest))
}
return interceptor(ctx, in, info, handler)
}
// InboxService_ServiceDesc is the grpc.ServiceDesc for InboxService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var InboxService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "memos.api.v1.InboxService",
HandlerType: (*InboxServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "ListInboxes",
Handler: _InboxService_ListInboxes_Handler,
},
{
MethodName: "UpdateInbox",
Handler: _InboxService_UpdateInbox_Handler,
},
{
MethodName: "DeleteInbox",
Handler: _InboxService_DeleteInbox_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "api/v1/inbox_service.proto",
}

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. // Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: api/v1/workspace_service.proto // source: api/v1/instance_service.proto
/* /*
Package apiv1 is a reverse proxy. Package apiv1 is a reverse proxy.
@ -35,30 +35,30 @@ var (
_ = metadata.Join _ = metadata.Join
) )
func request_WorkspaceService_GetWorkspaceProfile_0(ctx context.Context, marshaler runtime.Marshaler, client WorkspaceServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { func request_InstanceService_GetInstanceProfile_0(ctx context.Context, marshaler runtime.Marshaler, client InstanceServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var ( var (
protoReq GetWorkspaceProfileRequest protoReq GetInstanceProfileRequest
metadata runtime.ServerMetadata metadata runtime.ServerMetadata
) )
if req.Body != nil { if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body) _, _ = io.Copy(io.Discard, req.Body)
} }
msg, err := client.GetWorkspaceProfile(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) msg, err := client.GetInstanceProfile(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err return msg, metadata, err
} }
func local_request_WorkspaceService_GetWorkspaceProfile_0(ctx context.Context, marshaler runtime.Marshaler, server WorkspaceServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { func local_request_InstanceService_GetInstanceProfile_0(ctx context.Context, marshaler runtime.Marshaler, server InstanceServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var ( var (
protoReq GetWorkspaceProfileRequest protoReq GetInstanceProfileRequest
metadata runtime.ServerMetadata metadata runtime.ServerMetadata
) )
msg, err := server.GetWorkspaceProfile(ctx, &protoReq) msg, err := server.GetInstanceProfile(ctx, &protoReq)
return msg, metadata, err return msg, metadata, err
} }
func request_WorkspaceService_GetWorkspaceSetting_0(ctx context.Context, marshaler runtime.Marshaler, client WorkspaceServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { func request_InstanceService_GetInstanceSetting_0(ctx context.Context, marshaler runtime.Marshaler, client InstanceServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var ( var (
protoReq GetWorkspaceSettingRequest protoReq GetInstanceSettingRequest
metadata runtime.ServerMetadata metadata runtime.ServerMetadata
err error err error
) )
@ -73,13 +73,13 @@ func request_WorkspaceService_GetWorkspaceSetting_0(ctx context.Context, marshal
if err != nil { if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
} }
msg, err := client.GetWorkspaceSetting(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) msg, err := client.GetInstanceSetting(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err return msg, metadata, err
} }
func local_request_WorkspaceService_GetWorkspaceSetting_0(ctx context.Context, marshaler runtime.Marshaler, server WorkspaceServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { func local_request_InstanceService_GetInstanceSetting_0(ctx context.Context, marshaler runtime.Marshaler, server InstanceServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var ( var (
protoReq GetWorkspaceSettingRequest protoReq GetInstanceSettingRequest
metadata runtime.ServerMetadata metadata runtime.ServerMetadata
err error err error
) )
@ -91,15 +91,15 @@ func local_request_WorkspaceService_GetWorkspaceSetting_0(ctx context.Context, m
if err != nil { if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
} }
msg, err := server.GetWorkspaceSetting(ctx, &protoReq) msg, err := server.GetInstanceSetting(ctx, &protoReq)
return msg, metadata, err return msg, metadata, err
} }
var filter_WorkspaceService_UpdateWorkspaceSetting_0 = &utilities.DoubleArray{Encoding: map[string]int{"setting": 0, "name": 1}, Base: []int{1, 2, 1, 0, 0}, Check: []int{0, 1, 2, 3, 2}} var filter_InstanceService_UpdateInstanceSetting_0 = &utilities.DoubleArray{Encoding: map[string]int{"setting": 0, "name": 1}, Base: []int{1, 2, 1, 0, 0}, Check: []int{0, 1, 2, 3, 2}}
func request_WorkspaceService_UpdateWorkspaceSetting_0(ctx context.Context, marshaler runtime.Marshaler, client WorkspaceServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { func request_InstanceService_UpdateInstanceSetting_0(ctx context.Context, marshaler runtime.Marshaler, client InstanceServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var ( var (
protoReq UpdateWorkspaceSettingRequest protoReq UpdateInstanceSettingRequest
metadata runtime.ServerMetadata metadata runtime.ServerMetadata
err error err error
) )
@ -131,16 +131,16 @@ func request_WorkspaceService_UpdateWorkspaceSetting_0(ctx context.Context, mars
if err := req.ParseForm(); err != nil { if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
} }
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_WorkspaceService_UpdateWorkspaceSetting_0); err != nil { if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_InstanceService_UpdateInstanceSetting_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
} }
msg, err := client.UpdateWorkspaceSetting(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) msg, err := client.UpdateInstanceSetting(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err return msg, metadata, err
} }
func local_request_WorkspaceService_UpdateWorkspaceSetting_0(ctx context.Context, marshaler runtime.Marshaler, server WorkspaceServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { func local_request_InstanceService_UpdateInstanceSetting_0(ctx context.Context, marshaler runtime.Marshaler, server InstanceServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var ( var (
protoReq UpdateWorkspaceSettingRequest protoReq UpdateInstanceSettingRequest
metadata runtime.ServerMetadata metadata runtime.ServerMetadata
err error err error
) )
@ -169,86 +169,86 @@ func local_request_WorkspaceService_UpdateWorkspaceSetting_0(ctx context.Context
if err := req.ParseForm(); err != nil { if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
} }
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_WorkspaceService_UpdateWorkspaceSetting_0); err != nil { if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_InstanceService_UpdateInstanceSetting_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
} }
msg, err := server.UpdateWorkspaceSetting(ctx, &protoReq) msg, err := server.UpdateInstanceSetting(ctx, &protoReq)
return msg, metadata, err return msg, metadata, err
} }
// RegisterWorkspaceServiceHandlerServer registers the http handlers for service WorkspaceService to "mux". // RegisterInstanceServiceHandlerServer registers the http handlers for service InstanceService to "mux".
// UnaryRPC :call WorkspaceServiceServer directly. // UnaryRPC :call InstanceServiceServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterWorkspaceServiceHandlerFromEndpoint instead. // Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterInstanceServiceHandlerFromEndpoint instead.
// GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call. // GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call.
func RegisterWorkspaceServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server WorkspaceServiceServer) error { func RegisterInstanceServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server InstanceServiceServer) error {
mux.Handle(http.MethodGet, pattern_WorkspaceService_GetWorkspaceProfile_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { mux.Handle(http.MethodGet, pattern_InstanceService_GetInstanceProfile_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context()) ctx, cancel := context.WithCancel(req.Context())
defer cancel() defer cancel()
var stream runtime.ServerTransportStream var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.WorkspaceService/GetWorkspaceProfile", runtime.WithHTTPPathPattern("/api/v1/workspace/profile")) annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.InstanceService/GetInstanceProfile", runtime.WithHTTPPathPattern("/api/v1/instance/profile"))
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
} }
resp, md, err := local_request_WorkspaceService_GetWorkspaceProfile_0(annotatedContext, inboundMarshaler, server, req, pathParams) resp, md, err := local_request_InstanceService_GetInstanceProfile_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil { if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return return
} }
forward_WorkspaceService_GetWorkspaceProfile_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) forward_InstanceService_GetInstanceProfile_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
}) })
mux.Handle(http.MethodGet, pattern_WorkspaceService_GetWorkspaceSetting_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { mux.Handle(http.MethodGet, pattern_InstanceService_GetInstanceSetting_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context()) ctx, cancel := context.WithCancel(req.Context())
defer cancel() defer cancel()
var stream runtime.ServerTransportStream var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.WorkspaceService/GetWorkspaceSetting", runtime.WithHTTPPathPattern("/api/v1/{name=workspace/settings/*}")) annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.InstanceService/GetInstanceSetting", runtime.WithHTTPPathPattern("/api/v1/{name=instance/settings/*}"))
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
} }
resp, md, err := local_request_WorkspaceService_GetWorkspaceSetting_0(annotatedContext, inboundMarshaler, server, req, pathParams) resp, md, err := local_request_InstanceService_GetInstanceSetting_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil { if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return return
} }
forward_WorkspaceService_GetWorkspaceSetting_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) forward_InstanceService_GetInstanceSetting_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
}) })
mux.Handle(http.MethodPatch, pattern_WorkspaceService_UpdateWorkspaceSetting_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { mux.Handle(http.MethodPatch, pattern_InstanceService_UpdateInstanceSetting_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context()) ctx, cancel := context.WithCancel(req.Context())
defer cancel() defer cancel()
var stream runtime.ServerTransportStream var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.WorkspaceService/UpdateWorkspaceSetting", runtime.WithHTTPPathPattern("/api/v1/{setting.name=workspace/settings/*}")) annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.InstanceService/UpdateInstanceSetting", runtime.WithHTTPPathPattern("/api/v1/{setting.name=instance/settings/*}"))
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
} }
resp, md, err := local_request_WorkspaceService_UpdateWorkspaceSetting_0(annotatedContext, inboundMarshaler, server, req, pathParams) resp, md, err := local_request_InstanceService_UpdateInstanceSetting_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil { if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return return
} }
forward_WorkspaceService_UpdateWorkspaceSetting_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) forward_InstanceService_UpdateInstanceSetting_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
}) })
return nil return nil
} }
// RegisterWorkspaceServiceHandlerFromEndpoint is same as RegisterWorkspaceServiceHandler but // RegisterInstanceServiceHandlerFromEndpoint is same as RegisterInstanceServiceHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done. // automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterWorkspaceServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { func RegisterInstanceServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.NewClient(endpoint, opts...) conn, err := grpc.NewClient(endpoint, opts...)
if err != nil { if err != nil {
return err return err
@ -267,83 +267,83 @@ func RegisterWorkspaceServiceHandlerFromEndpoint(ctx context.Context, mux *runti
} }
}() }()
}() }()
return RegisterWorkspaceServiceHandler(ctx, mux, conn) return RegisterInstanceServiceHandler(ctx, mux, conn)
} }
// RegisterWorkspaceServiceHandler registers the http handlers for service WorkspaceService to "mux". // RegisterInstanceServiceHandler registers the http handlers for service InstanceService to "mux".
// The handlers forward requests to the grpc endpoint over "conn". // The handlers forward requests to the grpc endpoint over "conn".
func RegisterWorkspaceServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { func RegisterInstanceServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterWorkspaceServiceHandlerClient(ctx, mux, NewWorkspaceServiceClient(conn)) return RegisterInstanceServiceHandlerClient(ctx, mux, NewInstanceServiceClient(conn))
} }
// RegisterWorkspaceServiceHandlerClient registers the http handlers for service WorkspaceService // RegisterInstanceServiceHandlerClient registers the http handlers for service InstanceService
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "WorkspaceServiceClient". // to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "InstanceServiceClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "WorkspaceServiceClient" // Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "InstanceServiceClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in // doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "WorkspaceServiceClient" to call the correct interceptors. This client ignores the HTTP middlewares. // "InstanceServiceClient" to call the correct interceptors. This client ignores the HTTP middlewares.
func RegisterWorkspaceServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client WorkspaceServiceClient) error { func RegisterInstanceServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client InstanceServiceClient) error {
mux.Handle(http.MethodGet, pattern_WorkspaceService_GetWorkspaceProfile_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { mux.Handle(http.MethodGet, pattern_InstanceService_GetInstanceProfile_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context()) ctx, cancel := context.WithCancel(req.Context())
defer cancel() defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.WorkspaceService/GetWorkspaceProfile", runtime.WithHTTPPathPattern("/api/v1/workspace/profile")) annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.InstanceService/GetInstanceProfile", runtime.WithHTTPPathPattern("/api/v1/instance/profile"))
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
} }
resp, md, err := request_WorkspaceService_GetWorkspaceProfile_0(annotatedContext, inboundMarshaler, client, req, pathParams) resp, md, err := request_InstanceService_GetInstanceProfile_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil { if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return return
} }
forward_WorkspaceService_GetWorkspaceProfile_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) forward_InstanceService_GetInstanceProfile_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
}) })
mux.Handle(http.MethodGet, pattern_WorkspaceService_GetWorkspaceSetting_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { mux.Handle(http.MethodGet, pattern_InstanceService_GetInstanceSetting_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context()) ctx, cancel := context.WithCancel(req.Context())
defer cancel() defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.WorkspaceService/GetWorkspaceSetting", runtime.WithHTTPPathPattern("/api/v1/{name=workspace/settings/*}")) annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.InstanceService/GetInstanceSetting", runtime.WithHTTPPathPattern("/api/v1/{name=instance/settings/*}"))
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
} }
resp, md, err := request_WorkspaceService_GetWorkspaceSetting_0(annotatedContext, inboundMarshaler, client, req, pathParams) resp, md, err := request_InstanceService_GetInstanceSetting_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil { if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return return
} }
forward_WorkspaceService_GetWorkspaceSetting_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) forward_InstanceService_GetInstanceSetting_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
}) })
mux.Handle(http.MethodPatch, pattern_WorkspaceService_UpdateWorkspaceSetting_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { mux.Handle(http.MethodPatch, pattern_InstanceService_UpdateInstanceSetting_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context()) ctx, cancel := context.WithCancel(req.Context())
defer cancel() defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.WorkspaceService/UpdateWorkspaceSetting", runtime.WithHTTPPathPattern("/api/v1/{setting.name=workspace/settings/*}")) annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.InstanceService/UpdateInstanceSetting", runtime.WithHTTPPathPattern("/api/v1/{setting.name=instance/settings/*}"))
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
} }
resp, md, err := request_WorkspaceService_UpdateWorkspaceSetting_0(annotatedContext, inboundMarshaler, client, req, pathParams) resp, md, err := request_InstanceService_UpdateInstanceSetting_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil { if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return return
} }
forward_WorkspaceService_UpdateWorkspaceSetting_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) forward_InstanceService_UpdateInstanceSetting_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
}) })
return nil return nil
} }
var ( var (
pattern_WorkspaceService_GetWorkspaceProfile_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "workspace", "profile"}, "")) pattern_InstanceService_GetInstanceProfile_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "instance", "profile"}, ""))
pattern_WorkspaceService_GetWorkspaceSetting_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 3, 5, 4}, []string{"api", "v1", "workspace", "settings", "name"}, "")) pattern_InstanceService_GetInstanceSetting_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 3, 5, 4}, []string{"api", "v1", "instance", "settings", "name"}, ""))
pattern_WorkspaceService_UpdateWorkspaceSetting_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 3, 5, 4}, []string{"api", "v1", "workspace", "settings", "setting.name"}, "")) pattern_InstanceService_UpdateInstanceSetting_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 3, 5, 4}, []string{"api", "v1", "instance", "settings", "setting.name"}, ""))
) )
var ( var (
forward_WorkspaceService_GetWorkspaceProfile_0 = runtime.ForwardResponseMessage forward_InstanceService_GetInstanceProfile_0 = runtime.ForwardResponseMessage
forward_WorkspaceService_GetWorkspaceSetting_0 = runtime.ForwardResponseMessage forward_InstanceService_GetInstanceSetting_0 = runtime.ForwardResponseMessage
forward_WorkspaceService_UpdateWorkspaceSetting_0 = runtime.ForwardResponseMessage forward_InstanceService_UpdateInstanceSetting_0 = runtime.ForwardResponseMessage
) )

View File

@ -0,0 +1,203 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.0
// - protoc (unknown)
// source: api/v1/instance_service.proto
package apiv1
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
InstanceService_GetInstanceProfile_FullMethodName = "/memos.api.v1.InstanceService/GetInstanceProfile"
InstanceService_GetInstanceSetting_FullMethodName = "/memos.api.v1.InstanceService/GetInstanceSetting"
InstanceService_UpdateInstanceSetting_FullMethodName = "/memos.api.v1.InstanceService/UpdateInstanceSetting"
)
// InstanceServiceClient is the client API for InstanceService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type InstanceServiceClient interface {
// Gets the instance profile.
GetInstanceProfile(ctx context.Context, in *GetInstanceProfileRequest, opts ...grpc.CallOption) (*InstanceProfile, error)
// Gets an instance setting.
GetInstanceSetting(ctx context.Context, in *GetInstanceSettingRequest, opts ...grpc.CallOption) (*InstanceSetting, error)
// Updates an instance setting.
UpdateInstanceSetting(ctx context.Context, in *UpdateInstanceSettingRequest, opts ...grpc.CallOption) (*InstanceSetting, error)
}
type instanceServiceClient struct {
cc grpc.ClientConnInterface
}
func NewInstanceServiceClient(cc grpc.ClientConnInterface) InstanceServiceClient {
return &instanceServiceClient{cc}
}
func (c *instanceServiceClient) GetInstanceProfile(ctx context.Context, in *GetInstanceProfileRequest, opts ...grpc.CallOption) (*InstanceProfile, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(InstanceProfile)
err := c.cc.Invoke(ctx, InstanceService_GetInstanceProfile_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *instanceServiceClient) GetInstanceSetting(ctx context.Context, in *GetInstanceSettingRequest, opts ...grpc.CallOption) (*InstanceSetting, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(InstanceSetting)
err := c.cc.Invoke(ctx, InstanceService_GetInstanceSetting_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *instanceServiceClient) UpdateInstanceSetting(ctx context.Context, in *UpdateInstanceSettingRequest, opts ...grpc.CallOption) (*InstanceSetting, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(InstanceSetting)
err := c.cc.Invoke(ctx, InstanceService_UpdateInstanceSetting_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// InstanceServiceServer is the server API for InstanceService service.
// All implementations must embed UnimplementedInstanceServiceServer
// for forward compatibility.
type InstanceServiceServer interface {
// Gets the instance profile.
GetInstanceProfile(context.Context, *GetInstanceProfileRequest) (*InstanceProfile, error)
// Gets an instance setting.
GetInstanceSetting(context.Context, *GetInstanceSettingRequest) (*InstanceSetting, error)
// Updates an instance setting.
UpdateInstanceSetting(context.Context, *UpdateInstanceSettingRequest) (*InstanceSetting, error)
mustEmbedUnimplementedInstanceServiceServer()
}
// UnimplementedInstanceServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedInstanceServiceServer struct{}
func (UnimplementedInstanceServiceServer) GetInstanceProfile(context.Context, *GetInstanceProfileRequest) (*InstanceProfile, error) {
return nil, status.Error(codes.Unimplemented, "method GetInstanceProfile not implemented")
}
func (UnimplementedInstanceServiceServer) GetInstanceSetting(context.Context, *GetInstanceSettingRequest) (*InstanceSetting, error) {
return nil, status.Error(codes.Unimplemented, "method GetInstanceSetting not implemented")
}
func (UnimplementedInstanceServiceServer) UpdateInstanceSetting(context.Context, *UpdateInstanceSettingRequest) (*InstanceSetting, error) {
return nil, status.Error(codes.Unimplemented, "method UpdateInstanceSetting not implemented")
}
func (UnimplementedInstanceServiceServer) mustEmbedUnimplementedInstanceServiceServer() {}
func (UnimplementedInstanceServiceServer) testEmbeddedByValue() {}
// UnsafeInstanceServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to InstanceServiceServer will
// result in compilation errors.
type UnsafeInstanceServiceServer interface {
mustEmbedUnimplementedInstanceServiceServer()
}
func RegisterInstanceServiceServer(s grpc.ServiceRegistrar, srv InstanceServiceServer) {
// If the following call panics, it indicates UnimplementedInstanceServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&InstanceService_ServiceDesc, srv)
}
func _InstanceService_GetInstanceProfile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetInstanceProfileRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(InstanceServiceServer).GetInstanceProfile(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: InstanceService_GetInstanceProfile_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(InstanceServiceServer).GetInstanceProfile(ctx, req.(*GetInstanceProfileRequest))
}
return interceptor(ctx, in, info, handler)
}
func _InstanceService_GetInstanceSetting_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetInstanceSettingRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(InstanceServiceServer).GetInstanceSetting(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: InstanceService_GetInstanceSetting_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(InstanceServiceServer).GetInstanceSetting(ctx, req.(*GetInstanceSettingRequest))
}
return interceptor(ctx, in, info, handler)
}
func _InstanceService_UpdateInstanceSetting_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateInstanceSettingRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(InstanceServiceServer).UpdateInstanceSetting(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: InstanceService_UpdateInstanceSetting_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(InstanceServiceServer).UpdateInstanceSetting(ctx, req.(*UpdateInstanceSettingRequest))
}
return interceptor(ctx, in, info, handler)
}
// InstanceService_ServiceDesc is the grpc.ServiceDesc for InstanceService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var InstanceService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "memos.api.v1.InstanceService",
HandlerType: (*InstanceServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetInstanceProfile",
Handler: _InstanceService_GetInstanceProfile_Handler,
},
{
MethodName: "GetInstanceSetting",
Handler: _InstanceService_GetInstanceSetting_Handler,
},
{
MethodName: "UpdateInstanceSetting",
Handler: _InstanceService_UpdateInstanceSetting_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "api/v1/instance_service.proto",
}

File diff suppressed because it is too large Load Diff

View File

@ -1,363 +0,0 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: api/v1/markdown_service.proto
/*
Package apiv1 is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package apiv1
import (
"context"
"errors"
"io"
"net/http"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
)
// Suppress "imported and not used" errors
var (
_ codes.Code
_ io.Reader
_ status.Status
_ = errors.New
_ = runtime.String
_ = utilities.NewDoubleArray
_ = metadata.Join
)
func request_MarkdownService_ParseMarkdown_0(ctx context.Context, marshaler runtime.Marshaler, client MarkdownServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq ParseMarkdownRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.ParseMarkdown(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_MarkdownService_ParseMarkdown_0(ctx context.Context, marshaler runtime.Marshaler, server MarkdownServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq ParseMarkdownRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.ParseMarkdown(ctx, &protoReq)
return msg, metadata, err
}
func request_MarkdownService_RestoreMarkdownNodes_0(ctx context.Context, marshaler runtime.Marshaler, client MarkdownServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq RestoreMarkdownNodesRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.RestoreMarkdownNodes(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_MarkdownService_RestoreMarkdownNodes_0(ctx context.Context, marshaler runtime.Marshaler, server MarkdownServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq RestoreMarkdownNodesRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.RestoreMarkdownNodes(ctx, &protoReq)
return msg, metadata, err
}
func request_MarkdownService_StringifyMarkdownNodes_0(ctx context.Context, marshaler runtime.Marshaler, client MarkdownServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq StringifyMarkdownNodesRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
msg, err := client.StringifyMarkdownNodes(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_MarkdownService_StringifyMarkdownNodes_0(ctx context.Context, marshaler runtime.Marshaler, server MarkdownServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq StringifyMarkdownNodesRequest
metadata runtime.ServerMetadata
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.StringifyMarkdownNodes(ctx, &protoReq)
return msg, metadata, err
}
var filter_MarkdownService_GetLinkMetadata_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
func request_MarkdownService_GetLinkMetadata_0(ctx context.Context, marshaler runtime.Marshaler, client MarkdownServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq GetLinkMetadataRequest
metadata runtime.ServerMetadata
)
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_MarkdownService_GetLinkMetadata_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.GetLinkMetadata(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_MarkdownService_GetLinkMetadata_0(ctx context.Context, marshaler runtime.Marshaler, server MarkdownServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq GetLinkMetadataRequest
metadata runtime.ServerMetadata
)
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_MarkdownService_GetLinkMetadata_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.GetLinkMetadata(ctx, &protoReq)
return msg, metadata, err
}
// RegisterMarkdownServiceHandlerServer registers the http handlers for service MarkdownService to "mux".
// UnaryRPC :call MarkdownServiceServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterMarkdownServiceHandlerFromEndpoint instead.
// GRPC interceptors will not work for this type of registration. To use interceptors, you must use the "runtime.WithMiddlewares" option in the "runtime.NewServeMux" call.
func RegisterMarkdownServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server MarkdownServiceServer) error {
mux.Handle(http.MethodPost, pattern_MarkdownService_ParseMarkdown_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.MarkdownService/ParseMarkdown", runtime.WithHTTPPathPattern("/api/v1/markdown:parse"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_MarkdownService_ParseMarkdown_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_MarkdownService_ParseMarkdown_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_MarkdownService_RestoreMarkdownNodes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.MarkdownService/RestoreMarkdownNodes", runtime.WithHTTPPathPattern("/api/v1/markdown:restore"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_MarkdownService_RestoreMarkdownNodes_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_MarkdownService_RestoreMarkdownNodes_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_MarkdownService_StringifyMarkdownNodes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.MarkdownService/StringifyMarkdownNodes", runtime.WithHTTPPathPattern("/api/v1/markdown:stringify"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_MarkdownService_StringifyMarkdownNodes_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_MarkdownService_StringifyMarkdownNodes_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodGet, pattern_MarkdownService_GetLinkMetadata_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.MarkdownService/GetLinkMetadata", runtime.WithHTTPPathPattern("/api/v1/markdown/links:getMetadata"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_MarkdownService_GetLinkMetadata_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_MarkdownService_GetLinkMetadata_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// RegisterMarkdownServiceHandlerFromEndpoint is same as RegisterMarkdownServiceHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterMarkdownServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.NewClient(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterMarkdownServiceHandler(ctx, mux, conn)
}
// RegisterMarkdownServiceHandler registers the http handlers for service MarkdownService to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterMarkdownServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterMarkdownServiceHandlerClient(ctx, mux, NewMarkdownServiceClient(conn))
}
// RegisterMarkdownServiceHandlerClient registers the http handlers for service MarkdownService
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "MarkdownServiceClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "MarkdownServiceClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "MarkdownServiceClient" to call the correct interceptors. This client ignores the HTTP middlewares.
func RegisterMarkdownServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client MarkdownServiceClient) error {
mux.Handle(http.MethodPost, pattern_MarkdownService_ParseMarkdown_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.MarkdownService/ParseMarkdown", runtime.WithHTTPPathPattern("/api/v1/markdown:parse"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_MarkdownService_ParseMarkdown_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_MarkdownService_ParseMarkdown_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_MarkdownService_RestoreMarkdownNodes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.MarkdownService/RestoreMarkdownNodes", runtime.WithHTTPPathPattern("/api/v1/markdown:restore"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_MarkdownService_RestoreMarkdownNodes_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_MarkdownService_RestoreMarkdownNodes_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_MarkdownService_StringifyMarkdownNodes_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.MarkdownService/StringifyMarkdownNodes", runtime.WithHTTPPathPattern("/api/v1/markdown:stringify"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_MarkdownService_StringifyMarkdownNodes_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_MarkdownService_StringifyMarkdownNodes_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodGet, pattern_MarkdownService_GetLinkMetadata_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.MarkdownService/GetLinkMetadata", runtime.WithHTTPPathPattern("/api/v1/markdown/links:getMetadata"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_MarkdownService_GetLinkMetadata_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_MarkdownService_GetLinkMetadata_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_MarkdownService_ParseMarkdown_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "markdown"}, "parse"))
pattern_MarkdownService_RestoreMarkdownNodes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "markdown"}, "restore"))
pattern_MarkdownService_StringifyMarkdownNodes_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "markdown"}, "stringify"))
pattern_MarkdownService_GetLinkMetadata_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "markdown", "links"}, "getMetadata"))
)
var (
forward_MarkdownService_ParseMarkdown_0 = runtime.ForwardResponseMessage
forward_MarkdownService_RestoreMarkdownNodes_0 = runtime.ForwardResponseMessage
forward_MarkdownService_StringifyMarkdownNodes_0 = runtime.ForwardResponseMessage
forward_MarkdownService_GetLinkMetadata_0 = runtime.ForwardResponseMessage
)

View File

@ -1,251 +0,0 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc (unknown)
// source: api/v1/markdown_service.proto
package apiv1
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
MarkdownService_ParseMarkdown_FullMethodName = "/memos.api.v1.MarkdownService/ParseMarkdown"
MarkdownService_RestoreMarkdownNodes_FullMethodName = "/memos.api.v1.MarkdownService/RestoreMarkdownNodes"
MarkdownService_StringifyMarkdownNodes_FullMethodName = "/memos.api.v1.MarkdownService/StringifyMarkdownNodes"
MarkdownService_GetLinkMetadata_FullMethodName = "/memos.api.v1.MarkdownService/GetLinkMetadata"
)
// MarkdownServiceClient is the client API for MarkdownService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type MarkdownServiceClient interface {
// ParseMarkdown parses the given markdown content and returns a list of nodes.
// This is a utility method that transforms markdown text into structured nodes.
ParseMarkdown(ctx context.Context, in *ParseMarkdownRequest, opts ...grpc.CallOption) (*ParseMarkdownResponse, error)
// RestoreMarkdownNodes restores the given nodes to markdown content.
// This is the inverse operation of ParseMarkdown.
RestoreMarkdownNodes(ctx context.Context, in *RestoreMarkdownNodesRequest, opts ...grpc.CallOption) (*RestoreMarkdownNodesResponse, error)
// StringifyMarkdownNodes stringify the given nodes to plain text content.
// This removes all markdown formatting and returns plain text.
StringifyMarkdownNodes(ctx context.Context, in *StringifyMarkdownNodesRequest, opts ...grpc.CallOption) (*StringifyMarkdownNodesResponse, error)
// GetLinkMetadata returns metadata for a given link.
// This is useful for generating link previews.
GetLinkMetadata(ctx context.Context, in *GetLinkMetadataRequest, opts ...grpc.CallOption) (*LinkMetadata, error)
}
type markdownServiceClient struct {
cc grpc.ClientConnInterface
}
func NewMarkdownServiceClient(cc grpc.ClientConnInterface) MarkdownServiceClient {
return &markdownServiceClient{cc}
}
func (c *markdownServiceClient) ParseMarkdown(ctx context.Context, in *ParseMarkdownRequest, opts ...grpc.CallOption) (*ParseMarkdownResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ParseMarkdownResponse)
err := c.cc.Invoke(ctx, MarkdownService_ParseMarkdown_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *markdownServiceClient) RestoreMarkdownNodes(ctx context.Context, in *RestoreMarkdownNodesRequest, opts ...grpc.CallOption) (*RestoreMarkdownNodesResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RestoreMarkdownNodesResponse)
err := c.cc.Invoke(ctx, MarkdownService_RestoreMarkdownNodes_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *markdownServiceClient) StringifyMarkdownNodes(ctx context.Context, in *StringifyMarkdownNodesRequest, opts ...grpc.CallOption) (*StringifyMarkdownNodesResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(StringifyMarkdownNodesResponse)
err := c.cc.Invoke(ctx, MarkdownService_StringifyMarkdownNodes_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *markdownServiceClient) GetLinkMetadata(ctx context.Context, in *GetLinkMetadataRequest, opts ...grpc.CallOption) (*LinkMetadata, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(LinkMetadata)
err := c.cc.Invoke(ctx, MarkdownService_GetLinkMetadata_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// MarkdownServiceServer is the server API for MarkdownService service.
// All implementations must embed UnimplementedMarkdownServiceServer
// for forward compatibility.
type MarkdownServiceServer interface {
// ParseMarkdown parses the given markdown content and returns a list of nodes.
// This is a utility method that transforms markdown text into structured nodes.
ParseMarkdown(context.Context, *ParseMarkdownRequest) (*ParseMarkdownResponse, error)
// RestoreMarkdownNodes restores the given nodes to markdown content.
// This is the inverse operation of ParseMarkdown.
RestoreMarkdownNodes(context.Context, *RestoreMarkdownNodesRequest) (*RestoreMarkdownNodesResponse, error)
// StringifyMarkdownNodes stringify the given nodes to plain text content.
// This removes all markdown formatting and returns plain text.
StringifyMarkdownNodes(context.Context, *StringifyMarkdownNodesRequest) (*StringifyMarkdownNodesResponse, error)
// GetLinkMetadata returns metadata for a given link.
// This is useful for generating link previews.
GetLinkMetadata(context.Context, *GetLinkMetadataRequest) (*LinkMetadata, error)
mustEmbedUnimplementedMarkdownServiceServer()
}
// UnimplementedMarkdownServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedMarkdownServiceServer struct{}
func (UnimplementedMarkdownServiceServer) ParseMarkdown(context.Context, *ParseMarkdownRequest) (*ParseMarkdownResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ParseMarkdown not implemented")
}
func (UnimplementedMarkdownServiceServer) RestoreMarkdownNodes(context.Context, *RestoreMarkdownNodesRequest) (*RestoreMarkdownNodesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method RestoreMarkdownNodes not implemented")
}
func (UnimplementedMarkdownServiceServer) StringifyMarkdownNodes(context.Context, *StringifyMarkdownNodesRequest) (*StringifyMarkdownNodesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method StringifyMarkdownNodes not implemented")
}
func (UnimplementedMarkdownServiceServer) GetLinkMetadata(context.Context, *GetLinkMetadataRequest) (*LinkMetadata, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetLinkMetadata not implemented")
}
func (UnimplementedMarkdownServiceServer) mustEmbedUnimplementedMarkdownServiceServer() {}
func (UnimplementedMarkdownServiceServer) testEmbeddedByValue() {}
// UnsafeMarkdownServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to MarkdownServiceServer will
// result in compilation errors.
type UnsafeMarkdownServiceServer interface {
mustEmbedUnimplementedMarkdownServiceServer()
}
func RegisterMarkdownServiceServer(s grpc.ServiceRegistrar, srv MarkdownServiceServer) {
// If the following call pancis, it indicates UnimplementedMarkdownServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&MarkdownService_ServiceDesc, srv)
}
func _MarkdownService_ParseMarkdown_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ParseMarkdownRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MarkdownServiceServer).ParseMarkdown(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: MarkdownService_ParseMarkdown_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MarkdownServiceServer).ParseMarkdown(ctx, req.(*ParseMarkdownRequest))
}
return interceptor(ctx, in, info, handler)
}
func _MarkdownService_RestoreMarkdownNodes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RestoreMarkdownNodesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MarkdownServiceServer).RestoreMarkdownNodes(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: MarkdownService_RestoreMarkdownNodes_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MarkdownServiceServer).RestoreMarkdownNodes(ctx, req.(*RestoreMarkdownNodesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _MarkdownService_StringifyMarkdownNodes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(StringifyMarkdownNodesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MarkdownServiceServer).StringifyMarkdownNodes(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: MarkdownService_StringifyMarkdownNodes_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MarkdownServiceServer).StringifyMarkdownNodes(ctx, req.(*StringifyMarkdownNodesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _MarkdownService_GetLinkMetadata_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetLinkMetadataRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MarkdownServiceServer).GetLinkMetadata(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: MarkdownService_GetLinkMetadata_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MarkdownServiceServer).GetLinkMetadata(ctx, req.(*GetLinkMetadataRequest))
}
return interceptor(ctx, in, info, handler)
}
// MarkdownService_ServiceDesc is the grpc.ServiceDesc for MarkdownService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var MarkdownService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "memos.api.v1.MarkdownService",
HandlerType: (*MarkdownServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "ParseMarkdown",
Handler: _MarkdownService_ParseMarkdown_Handler,
},
{
MethodName: "RestoreMarkdownNodes",
Handler: _MarkdownService_RestoreMarkdownNodes_Handler,
},
{
MethodName: "StringifyMarkdownNodes",
Handler: _MarkdownService_StringifyMarkdownNodes_Handler,
},
{
MethodName: "GetLinkMetadata",
Handler: _MarkdownService_GetLinkMetadata_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "api/v1/markdown_service.proto",
}

File diff suppressed because it is too large Load Diff

View File

@ -111,8 +111,6 @@ func local_request_MemoService_ListMemos_0(ctx context.Context, marshaler runtim
return msg, metadata, err return msg, metadata, err
} }
var filter_MemoService_GetMemo_0 = &utilities.DoubleArray{Encoding: map[string]int{"name": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
func request_MemoService_GetMemo_0(ctx context.Context, marshaler runtime.Marshaler, client MemoServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { func request_MemoService_GetMemo_0(ctx context.Context, marshaler runtime.Marshaler, client MemoServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var ( var (
protoReq GetMemoRequest protoReq GetMemoRequest
@ -130,12 +128,6 @@ func request_MemoService_GetMemo_0(ctx context.Context, marshaler runtime.Marsha
if err != nil { if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
} }
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_MemoService_GetMemo_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.GetMemo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) msg, err := client.GetMemo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err return msg, metadata, err
} }
@ -154,12 +146,6 @@ func local_request_MemoService_GetMemo_0(ctx context.Context, marshaler runtime.
if err != nil { if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
} }
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_MemoService_GetMemo_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.GetMemo(ctx, &protoReq) msg, err := server.GetMemo(ctx, &protoReq)
return msg, metadata, err return msg, metadata, err
} }
@ -298,96 +284,6 @@ func local_request_MemoService_DeleteMemo_0(ctx context.Context, marshaler runti
return msg, metadata, err return msg, metadata, err
} }
func request_MemoService_RenameMemoTag_0(ctx context.Context, marshaler runtime.Marshaler, client MemoServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq RenameMemoTagRequest
metadata runtime.ServerMetadata
err error
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
val, ok := pathParams["parent"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "parent")
}
protoReq.Parent, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "parent", err)
}
msg, err := client.RenameMemoTag(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_MemoService_RenameMemoTag_0(ctx context.Context, marshaler runtime.Marshaler, server MemoServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq RenameMemoTagRequest
metadata runtime.ServerMetadata
err error
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
val, ok := pathParams["parent"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "parent")
}
protoReq.Parent, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "parent", err)
}
msg, err := server.RenameMemoTag(ctx, &protoReq)
return msg, metadata, err
}
func request_MemoService_DeleteMemoTag_0(ctx context.Context, marshaler runtime.Marshaler, client MemoServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq DeleteMemoTagRequest
metadata runtime.ServerMetadata
err error
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
val, ok := pathParams["parent"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "parent")
}
protoReq.Parent, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "parent", err)
}
msg, err := client.DeleteMemoTag(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_MemoService_DeleteMemoTag_0(ctx context.Context, marshaler runtime.Marshaler, server MemoServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq DeleteMemoTagRequest
metadata runtime.ServerMetadata
err error
)
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
val, ok := pathParams["parent"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "parent")
}
protoReq.Parent, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "parent", err)
}
msg, err := server.DeleteMemoTag(ctx, &protoReq)
return msg, metadata, err
}
func request_MemoService_SetMemoAttachments_0(ctx context.Context, marshaler runtime.Marshaler, client MemoServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { func request_MemoService_SetMemoAttachments_0(ctx context.Context, marshaler runtime.Marshaler, client MemoServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var ( var (
protoReq SetMemoAttachmentsRequest protoReq SetMemoAttachmentsRequest
@ -939,46 +835,6 @@ func RegisterMemoServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
} }
forward_MemoService_DeleteMemo_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) forward_MemoService_DeleteMemo_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
}) })
mux.Handle(http.MethodPatch, pattern_MemoService_RenameMemoTag_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.MemoService/RenameMemoTag", runtime.WithHTTPPathPattern("/api/v1/{parent=memos/*}/tags:rename"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_MemoService_RenameMemoTag_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_MemoService_RenameMemoTag_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_MemoService_DeleteMemoTag_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.MemoService/DeleteMemoTag", runtime.WithHTTPPathPattern("/api/v1/{parent=memos/*}/tags:delete"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_MemoService_DeleteMemoTag_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_MemoService_DeleteMemoTag_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPatch, pattern_MemoService_SetMemoAttachments_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { mux.Handle(http.MethodPatch, pattern_MemoService_SetMemoAttachments_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context()) ctx, cancel := context.WithCancel(req.Context())
defer cancel() defer cancel()
@ -1284,40 +1140,6 @@ func RegisterMemoServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux
} }
forward_MemoService_DeleteMemo_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) forward_MemoService_DeleteMemo_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
}) })
mux.Handle(http.MethodPatch, pattern_MemoService_RenameMemoTag_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.MemoService/RenameMemoTag", runtime.WithHTTPPathPattern("/api/v1/{parent=memos/*}/tags:rename"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_MemoService_RenameMemoTag_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_MemoService_RenameMemoTag_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPost, pattern_MemoService_DeleteMemoTag_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.MemoService/DeleteMemoTag", runtime.WithHTTPPathPattern("/api/v1/{parent=memos/*}/tags:delete"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_MemoService_DeleteMemoTag_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_MemoService_DeleteMemoTag_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPatch, pattern_MemoService_SetMemoAttachments_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { mux.Handle(http.MethodPatch, pattern_MemoService_SetMemoAttachments_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context()) ctx, cancel := context.WithCancel(req.Context())
defer cancel() defer cancel()
@ -1480,8 +1302,6 @@ var (
pattern_MemoService_GetMemo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "memos", "name"}, "")) pattern_MemoService_GetMemo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "memos", "name"}, ""))
pattern_MemoService_UpdateMemo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "memos", "memo.name"}, "")) pattern_MemoService_UpdateMemo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "memos", "memo.name"}, ""))
pattern_MemoService_DeleteMemo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "memos", "name"}, "")) pattern_MemoService_DeleteMemo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "memos", "name"}, ""))
pattern_MemoService_RenameMemoTag_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "parent", "tags"}, "rename"))
pattern_MemoService_DeleteMemoTag_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "parent", "tags"}, "delete"))
pattern_MemoService_SetMemoAttachments_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "name", "attachments"}, "")) pattern_MemoService_SetMemoAttachments_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "name", "attachments"}, ""))
pattern_MemoService_ListMemoAttachments_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "name", "attachments"}, "")) pattern_MemoService_ListMemoAttachments_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "name", "attachments"}, ""))
pattern_MemoService_SetMemoRelations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "name", "relations"}, "")) pattern_MemoService_SetMemoRelations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "memos", "name", "relations"}, ""))
@ -1499,8 +1319,6 @@ var (
forward_MemoService_GetMemo_0 = runtime.ForwardResponseMessage forward_MemoService_GetMemo_0 = runtime.ForwardResponseMessage
forward_MemoService_UpdateMemo_0 = runtime.ForwardResponseMessage forward_MemoService_UpdateMemo_0 = runtime.ForwardResponseMessage
forward_MemoService_DeleteMemo_0 = runtime.ForwardResponseMessage forward_MemoService_DeleteMemo_0 = runtime.ForwardResponseMessage
forward_MemoService_RenameMemoTag_0 = runtime.ForwardResponseMessage
forward_MemoService_DeleteMemoTag_0 = runtime.ForwardResponseMessage
forward_MemoService_SetMemoAttachments_0 = runtime.ForwardResponseMessage forward_MemoService_SetMemoAttachments_0 = runtime.ForwardResponseMessage
forward_MemoService_ListMemoAttachments_0 = runtime.ForwardResponseMessage forward_MemoService_ListMemoAttachments_0 = runtime.ForwardResponseMessage
forward_MemoService_SetMemoRelations_0 = runtime.ForwardResponseMessage forward_MemoService_SetMemoRelations_0 = runtime.ForwardResponseMessage

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions: // versions:
// - protoc-gen-go-grpc v1.5.1 // - protoc-gen-go-grpc v1.6.0
// - protoc (unknown) // - protoc (unknown)
// source: api/v1/memo_service.proto // source: api/v1/memo_service.proto
@ -25,8 +25,6 @@ const (
MemoService_GetMemo_FullMethodName = "/memos.api.v1.MemoService/GetMemo" MemoService_GetMemo_FullMethodName = "/memos.api.v1.MemoService/GetMemo"
MemoService_UpdateMemo_FullMethodName = "/memos.api.v1.MemoService/UpdateMemo" MemoService_UpdateMemo_FullMethodName = "/memos.api.v1.MemoService/UpdateMemo"
MemoService_DeleteMemo_FullMethodName = "/memos.api.v1.MemoService/DeleteMemo" MemoService_DeleteMemo_FullMethodName = "/memos.api.v1.MemoService/DeleteMemo"
MemoService_RenameMemoTag_FullMethodName = "/memos.api.v1.MemoService/RenameMemoTag"
MemoService_DeleteMemoTag_FullMethodName = "/memos.api.v1.MemoService/DeleteMemoTag"
MemoService_SetMemoAttachments_FullMethodName = "/memos.api.v1.MemoService/SetMemoAttachments" MemoService_SetMemoAttachments_FullMethodName = "/memos.api.v1.MemoService/SetMemoAttachments"
MemoService_ListMemoAttachments_FullMethodName = "/memos.api.v1.MemoService/ListMemoAttachments" MemoService_ListMemoAttachments_FullMethodName = "/memos.api.v1.MemoService/ListMemoAttachments"
MemoService_SetMemoRelations_FullMethodName = "/memos.api.v1.MemoService/SetMemoRelations" MemoService_SetMemoRelations_FullMethodName = "/memos.api.v1.MemoService/SetMemoRelations"
@ -52,10 +50,6 @@ type MemoServiceClient interface {
UpdateMemo(ctx context.Context, in *UpdateMemoRequest, opts ...grpc.CallOption) (*Memo, error) UpdateMemo(ctx context.Context, in *UpdateMemoRequest, opts ...grpc.CallOption) (*Memo, error)
// DeleteMemo deletes a memo. // DeleteMemo deletes a memo.
DeleteMemo(ctx context.Context, in *DeleteMemoRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) DeleteMemo(ctx context.Context, in *DeleteMemoRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
// RenameMemoTag renames a tag for a memo.
RenameMemoTag(ctx context.Context, in *RenameMemoTagRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
// DeleteMemoTag deletes a tag for a memo.
DeleteMemoTag(ctx context.Context, in *DeleteMemoTagRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
// SetMemoAttachments sets attachments for a memo. // SetMemoAttachments sets attachments for a memo.
SetMemoAttachments(ctx context.Context, in *SetMemoAttachmentsRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) SetMemoAttachments(ctx context.Context, in *SetMemoAttachmentsRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
// ListMemoAttachments lists attachments for a memo. // ListMemoAttachments lists attachments for a memo.
@ -134,26 +128,6 @@ func (c *memoServiceClient) DeleteMemo(ctx context.Context, in *DeleteMemoReques
return out, nil return out, nil
} }
func (c *memoServiceClient) RenameMemoTag(ctx context.Context, in *RenameMemoTagRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(emptypb.Empty)
err := c.cc.Invoke(ctx, MemoService_RenameMemoTag_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *memoServiceClient) DeleteMemoTag(ctx context.Context, in *DeleteMemoTagRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(emptypb.Empty)
err := c.cc.Invoke(ctx, MemoService_DeleteMemoTag_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *memoServiceClient) SetMemoAttachments(ctx context.Context, in *SetMemoAttachmentsRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { func (c *memoServiceClient) SetMemoAttachments(ctx context.Context, in *SetMemoAttachmentsRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(emptypb.Empty) out := new(emptypb.Empty)
@ -258,10 +232,6 @@ type MemoServiceServer interface {
UpdateMemo(context.Context, *UpdateMemoRequest) (*Memo, error) UpdateMemo(context.Context, *UpdateMemoRequest) (*Memo, error)
// DeleteMemo deletes a memo. // DeleteMemo deletes a memo.
DeleteMemo(context.Context, *DeleteMemoRequest) (*emptypb.Empty, error) DeleteMemo(context.Context, *DeleteMemoRequest) (*emptypb.Empty, error)
// RenameMemoTag renames a tag for a memo.
RenameMemoTag(context.Context, *RenameMemoTagRequest) (*emptypb.Empty, error)
// DeleteMemoTag deletes a tag for a memo.
DeleteMemoTag(context.Context, *DeleteMemoTagRequest) (*emptypb.Empty, error)
// SetMemoAttachments sets attachments for a memo. // SetMemoAttachments sets attachments for a memo.
SetMemoAttachments(context.Context, *SetMemoAttachmentsRequest) (*emptypb.Empty, error) SetMemoAttachments(context.Context, *SetMemoAttachmentsRequest) (*emptypb.Empty, error)
// ListMemoAttachments lists attachments for a memo. // ListMemoAttachments lists attachments for a memo.
@ -291,52 +261,46 @@ type MemoServiceServer interface {
type UnimplementedMemoServiceServer struct{} type UnimplementedMemoServiceServer struct{}
func (UnimplementedMemoServiceServer) CreateMemo(context.Context, *CreateMemoRequest) (*Memo, error) { func (UnimplementedMemoServiceServer) CreateMemo(context.Context, *CreateMemoRequest) (*Memo, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateMemo not implemented") return nil, status.Error(codes.Unimplemented, "method CreateMemo not implemented")
} }
func (UnimplementedMemoServiceServer) ListMemos(context.Context, *ListMemosRequest) (*ListMemosResponse, error) { func (UnimplementedMemoServiceServer) ListMemos(context.Context, *ListMemosRequest) (*ListMemosResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListMemos not implemented") return nil, status.Error(codes.Unimplemented, "method ListMemos not implemented")
} }
func (UnimplementedMemoServiceServer) GetMemo(context.Context, *GetMemoRequest) (*Memo, error) { func (UnimplementedMemoServiceServer) GetMemo(context.Context, *GetMemoRequest) (*Memo, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetMemo not implemented") return nil, status.Error(codes.Unimplemented, "method GetMemo not implemented")
} }
func (UnimplementedMemoServiceServer) UpdateMemo(context.Context, *UpdateMemoRequest) (*Memo, error) { func (UnimplementedMemoServiceServer) UpdateMemo(context.Context, *UpdateMemoRequest) (*Memo, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateMemo not implemented") return nil, status.Error(codes.Unimplemented, "method UpdateMemo not implemented")
} }
func (UnimplementedMemoServiceServer) DeleteMemo(context.Context, *DeleteMemoRequest) (*emptypb.Empty, error) { func (UnimplementedMemoServiceServer) DeleteMemo(context.Context, *DeleteMemoRequest) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteMemo not implemented") return nil, status.Error(codes.Unimplemented, "method DeleteMemo not implemented")
}
func (UnimplementedMemoServiceServer) RenameMemoTag(context.Context, *RenameMemoTagRequest) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method RenameMemoTag not implemented")
}
func (UnimplementedMemoServiceServer) DeleteMemoTag(context.Context, *DeleteMemoTagRequest) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteMemoTag not implemented")
} }
func (UnimplementedMemoServiceServer) SetMemoAttachments(context.Context, *SetMemoAttachmentsRequest) (*emptypb.Empty, error) { func (UnimplementedMemoServiceServer) SetMemoAttachments(context.Context, *SetMemoAttachmentsRequest) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetMemoAttachments not implemented") return nil, status.Error(codes.Unimplemented, "method SetMemoAttachments not implemented")
} }
func (UnimplementedMemoServiceServer) ListMemoAttachments(context.Context, *ListMemoAttachmentsRequest) (*ListMemoAttachmentsResponse, error) { func (UnimplementedMemoServiceServer) ListMemoAttachments(context.Context, *ListMemoAttachmentsRequest) (*ListMemoAttachmentsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListMemoAttachments not implemented") return nil, status.Error(codes.Unimplemented, "method ListMemoAttachments not implemented")
} }
func (UnimplementedMemoServiceServer) SetMemoRelations(context.Context, *SetMemoRelationsRequest) (*emptypb.Empty, error) { func (UnimplementedMemoServiceServer) SetMemoRelations(context.Context, *SetMemoRelationsRequest) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method SetMemoRelations not implemented") return nil, status.Error(codes.Unimplemented, "method SetMemoRelations not implemented")
} }
func (UnimplementedMemoServiceServer) ListMemoRelations(context.Context, *ListMemoRelationsRequest) (*ListMemoRelationsResponse, error) { func (UnimplementedMemoServiceServer) ListMemoRelations(context.Context, *ListMemoRelationsRequest) (*ListMemoRelationsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListMemoRelations not implemented") return nil, status.Error(codes.Unimplemented, "method ListMemoRelations not implemented")
} }
func (UnimplementedMemoServiceServer) CreateMemoComment(context.Context, *CreateMemoCommentRequest) (*Memo, error) { func (UnimplementedMemoServiceServer) CreateMemoComment(context.Context, *CreateMemoCommentRequest) (*Memo, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateMemoComment not implemented") return nil, status.Error(codes.Unimplemented, "method CreateMemoComment not implemented")
} }
func (UnimplementedMemoServiceServer) ListMemoComments(context.Context, *ListMemoCommentsRequest) (*ListMemoCommentsResponse, error) { func (UnimplementedMemoServiceServer) ListMemoComments(context.Context, *ListMemoCommentsRequest) (*ListMemoCommentsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListMemoComments not implemented") return nil, status.Error(codes.Unimplemented, "method ListMemoComments not implemented")
} }
func (UnimplementedMemoServiceServer) ListMemoReactions(context.Context, *ListMemoReactionsRequest) (*ListMemoReactionsResponse, error) { func (UnimplementedMemoServiceServer) ListMemoReactions(context.Context, *ListMemoReactionsRequest) (*ListMemoReactionsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListMemoReactions not implemented") return nil, status.Error(codes.Unimplemented, "method ListMemoReactions not implemented")
} }
func (UnimplementedMemoServiceServer) UpsertMemoReaction(context.Context, *UpsertMemoReactionRequest) (*Reaction, error) { func (UnimplementedMemoServiceServer) UpsertMemoReaction(context.Context, *UpsertMemoReactionRequest) (*Reaction, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpsertMemoReaction not implemented") return nil, status.Error(codes.Unimplemented, "method UpsertMemoReaction not implemented")
} }
func (UnimplementedMemoServiceServer) DeleteMemoReaction(context.Context, *DeleteMemoReactionRequest) (*emptypb.Empty, error) { func (UnimplementedMemoServiceServer) DeleteMemoReaction(context.Context, *DeleteMemoReactionRequest) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteMemoReaction not implemented") return nil, status.Error(codes.Unimplemented, "method DeleteMemoReaction not implemented")
} }
func (UnimplementedMemoServiceServer) mustEmbedUnimplementedMemoServiceServer() {} func (UnimplementedMemoServiceServer) mustEmbedUnimplementedMemoServiceServer() {}
func (UnimplementedMemoServiceServer) testEmbeddedByValue() {} func (UnimplementedMemoServiceServer) testEmbeddedByValue() {}
@ -349,7 +313,7 @@ type UnsafeMemoServiceServer interface {
} }
func RegisterMemoServiceServer(s grpc.ServiceRegistrar, srv MemoServiceServer) { func RegisterMemoServiceServer(s grpc.ServiceRegistrar, srv MemoServiceServer) {
// If the following call pancis, it indicates UnimplementedMemoServiceServer was // If the following call panics, it indicates UnimplementedMemoServiceServer was
// embedded by pointer and is nil. This will cause panics if an // embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization // unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O. // time to prevent it from happening at runtime later due to I/O.
@ -449,42 +413,6 @@ func _MemoService_DeleteMemo_Handler(srv interface{}, ctx context.Context, dec f
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _MemoService_RenameMemoTag_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RenameMemoTagRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MemoServiceServer).RenameMemoTag(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: MemoService_RenameMemoTag_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MemoServiceServer).RenameMemoTag(ctx, req.(*RenameMemoTagRequest))
}
return interceptor(ctx, in, info, handler)
}
func _MemoService_DeleteMemoTag_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteMemoTagRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(MemoServiceServer).DeleteMemoTag(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: MemoService_DeleteMemoTag_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(MemoServiceServer).DeleteMemoTag(ctx, req.(*DeleteMemoTagRequest))
}
return interceptor(ctx, in, info, handler)
}
func _MemoService_SetMemoAttachments_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { func _MemoService_SetMemoAttachments_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SetMemoAttachmentsRequest) in := new(SetMemoAttachmentsRequest)
if err := dec(in); err != nil { if err := dec(in); err != nil {
@ -674,14 +602,6 @@ var MemoService_ServiceDesc = grpc.ServiceDesc{
MethodName: "DeleteMemo", MethodName: "DeleteMemo",
Handler: _MemoService_DeleteMemo_Handler, Handler: _MemoService_DeleteMemo_Handler,
}, },
{
MethodName: "RenameMemoTag",
Handler: _MemoService_RenameMemoTag_Handler,
},
{
MethodName: "DeleteMemoTag",
Handler: _MemoService_DeleteMemoTag_Handler,
},
{ {
MethodName: "SetMemoAttachments", MethodName: "SetMemoAttachments",
Handler: _MemoService_SetMemoAttachments_Handler, Handler: _MemoService_SetMemoAttachments_Handler,

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.10 // protoc-gen-go v1.36.11
// protoc (unknown) // protoc (unknown)
// source: api/v1/shortcut_service.proto // source: api/v1/shortcut_service.proto

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions: // versions:
// - protoc-gen-go-grpc v1.5.1 // - protoc-gen-go-grpc v1.6.0
// - protoc (unknown) // - protoc (unknown)
// source: api/v1/shortcut_service.proto // source: api/v1/shortcut_service.proto
@ -126,19 +126,19 @@ type ShortcutServiceServer interface {
type UnimplementedShortcutServiceServer struct{} type UnimplementedShortcutServiceServer struct{}
func (UnimplementedShortcutServiceServer) ListShortcuts(context.Context, *ListShortcutsRequest) (*ListShortcutsResponse, error) { func (UnimplementedShortcutServiceServer) ListShortcuts(context.Context, *ListShortcutsRequest) (*ListShortcutsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListShortcuts not implemented") return nil, status.Error(codes.Unimplemented, "method ListShortcuts not implemented")
} }
func (UnimplementedShortcutServiceServer) GetShortcut(context.Context, *GetShortcutRequest) (*Shortcut, error) { func (UnimplementedShortcutServiceServer) GetShortcut(context.Context, *GetShortcutRequest) (*Shortcut, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetShortcut not implemented") return nil, status.Error(codes.Unimplemented, "method GetShortcut not implemented")
} }
func (UnimplementedShortcutServiceServer) CreateShortcut(context.Context, *CreateShortcutRequest) (*Shortcut, error) { func (UnimplementedShortcutServiceServer) CreateShortcut(context.Context, *CreateShortcutRequest) (*Shortcut, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateShortcut not implemented") return nil, status.Error(codes.Unimplemented, "method CreateShortcut not implemented")
} }
func (UnimplementedShortcutServiceServer) UpdateShortcut(context.Context, *UpdateShortcutRequest) (*Shortcut, error) { func (UnimplementedShortcutServiceServer) UpdateShortcut(context.Context, *UpdateShortcutRequest) (*Shortcut, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateShortcut not implemented") return nil, status.Error(codes.Unimplemented, "method UpdateShortcut not implemented")
} }
func (UnimplementedShortcutServiceServer) DeleteShortcut(context.Context, *DeleteShortcutRequest) (*emptypb.Empty, error) { func (UnimplementedShortcutServiceServer) DeleteShortcut(context.Context, *DeleteShortcutRequest) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteShortcut not implemented") return nil, status.Error(codes.Unimplemented, "method DeleteShortcut not implemented")
} }
func (UnimplementedShortcutServiceServer) mustEmbedUnimplementedShortcutServiceServer() {} func (UnimplementedShortcutServiceServer) mustEmbedUnimplementedShortcutServiceServer() {}
func (UnimplementedShortcutServiceServer) testEmbeddedByValue() {} func (UnimplementedShortcutServiceServer) testEmbeddedByValue() {}
@ -151,7 +151,7 @@ type UnsafeShortcutServiceServer interface {
} }
func RegisterShortcutServiceServer(s grpc.ServiceRegistrar, srv ShortcutServiceServer) { func RegisterShortcutServiceServer(s grpc.ServiceRegistrar, srv ShortcutServiceServer) {
// If the following call pancis, it indicates UnimplementedShortcutServiceServer was // If the following call panics, it indicates UnimplementedShortcutServiceServer was
// embedded by pointer and is nil. This will cause panics if an // embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization // unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O. // time to prevent it from happening at runtime later due to I/O.

File diff suppressed because it is too large Load Diff

View File

@ -298,45 +298,6 @@ func local_request_UserService_DeleteUser_0(ctx context.Context, marshaler runti
return msg, metadata, err return msg, metadata, err
} }
func request_UserService_GetUserAvatar_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq GetUserAvatarRequest
metadata runtime.ServerMetadata
err error
)
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
val, ok := pathParams["name"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name")
}
protoReq.Name, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
}
msg, err := client.GetUserAvatar(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_UserService_GetUserAvatar_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq GetUserAvatarRequest
metadata runtime.ServerMetadata
err error
)
val, ok := pathParams["name"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name")
}
protoReq.Name, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
}
msg, err := server.GetUserAvatar(ctx, &protoReq)
return msg, metadata, err
}
func request_UserService_ListAllUserStats_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { func request_UserService_ListAllUserStats_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var ( var (
protoReq ListAllUserStatsRequest protoReq ListAllUserStatsRequest
@ -1003,6 +964,179 @@ func local_request_UserService_DeleteUserWebhook_0(ctx context.Context, marshale
return msg, metadata, err return msg, metadata, err
} }
var filter_UserService_ListUserNotifications_0 = &utilities.DoubleArray{Encoding: map[string]int{"parent": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
func request_UserService_ListUserNotifications_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq ListUserNotificationsRequest
metadata runtime.ServerMetadata
err error
)
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
val, ok := pathParams["parent"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "parent")
}
protoReq.Parent, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "parent", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_UserService_ListUserNotifications_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.ListUserNotifications(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_UserService_ListUserNotifications_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq ListUserNotificationsRequest
metadata runtime.ServerMetadata
err error
)
val, ok := pathParams["parent"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "parent")
}
protoReq.Parent, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "parent", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_UserService_ListUserNotifications_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.ListUserNotifications(ctx, &protoReq)
return msg, metadata, err
}
var filter_UserService_UpdateUserNotification_0 = &utilities.DoubleArray{Encoding: map[string]int{"notification": 0, "name": 1}, Base: []int{1, 2, 1, 0, 0}, Check: []int{0, 1, 2, 3, 2}}
func request_UserService_UpdateUserNotification_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq UpdateUserNotificationRequest
metadata runtime.ServerMetadata
err error
)
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Notification); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
if protoReq.UpdateMask == nil || len(protoReq.UpdateMask.GetPaths()) == 0 {
if fieldMask, err := runtime.FieldMaskFromRequestBody(newReader(), protoReq.Notification); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
} else {
protoReq.UpdateMask = fieldMask
}
}
val, ok := pathParams["notification.name"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "notification.name")
}
err = runtime.PopulateFieldFromPath(&protoReq, "notification.name", val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "notification.name", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_UserService_UpdateUserNotification_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.UpdateUserNotification(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_UserService_UpdateUserNotification_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq UpdateUserNotificationRequest
metadata runtime.ServerMetadata
err error
)
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Notification); err != nil && !errors.Is(err, io.EOF) {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if protoReq.UpdateMask == nil || len(protoReq.UpdateMask.GetPaths()) == 0 {
if fieldMask, err := runtime.FieldMaskFromRequestBody(newReader(), protoReq.Notification); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
} else {
protoReq.UpdateMask = fieldMask
}
}
val, ok := pathParams["notification.name"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "notification.name")
}
err = runtime.PopulateFieldFromPath(&protoReq, "notification.name", val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "notification.name", err)
}
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_UserService_UpdateUserNotification_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.UpdateUserNotification(ctx, &protoReq)
return msg, metadata, err
}
func request_UserService_DeleteUserNotification_0(ctx context.Context, marshaler runtime.Marshaler, client UserServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq DeleteUserNotificationRequest
metadata runtime.ServerMetadata
err error
)
if req.Body != nil {
_, _ = io.Copy(io.Discard, req.Body)
}
val, ok := pathParams["name"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name")
}
protoReq.Name, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
}
msg, err := client.DeleteUserNotification(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_UserService_DeleteUserNotification_0(ctx context.Context, marshaler runtime.Marshaler, server UserServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var (
protoReq DeleteUserNotificationRequest
metadata runtime.ServerMetadata
err error
)
val, ok := pathParams["name"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name")
}
protoReq.Name, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err)
}
msg, err := server.DeleteUserNotification(ctx, &protoReq)
return msg, metadata, err
}
// RegisterUserServiceHandlerServer registers the http handlers for service UserService to "mux". // RegisterUserServiceHandlerServer registers the http handlers for service UserService to "mux".
// UnaryRPC :call UserServiceServer directly. // UnaryRPC :call UserServiceServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
@ -1109,26 +1243,6 @@ func RegisterUserServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
} }
forward_UserService_DeleteUser_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) forward_UserService_DeleteUser_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
}) })
mux.Handle(http.MethodGet, pattern_UserService_GetUserAvatar_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.UserService/GetUserAvatar", runtime.WithHTTPPathPattern("/api/v1/{name=users/*}/avatar"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_UserService_GetUserAvatar_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_UserService_GetUserAvatar_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodGet, pattern_UserService_ListAllUserStats_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { mux.Handle(http.MethodGet, pattern_UserService_ListAllUserStats_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context()) ctx, cancel := context.WithCancel(req.Context())
defer cancel() defer cancel()
@ -1409,6 +1523,66 @@ func RegisterUserServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux
} }
forward_UserService_DeleteUserWebhook_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) forward_UserService_DeleteUserWebhook_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
}) })
mux.Handle(http.MethodGet, pattern_UserService_ListUserNotifications_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.UserService/ListUserNotifications", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/notifications"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_UserService_ListUserNotifications_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_UserService_ListUserNotifications_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPatch, pattern_UserService_UpdateUserNotification_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.UserService/UpdateUserNotification", runtime.WithHTTPPathPattern("/api/v1/{notification.name=users/*/notifications/*}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_UserService_UpdateUserNotification_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_UserService_UpdateUserNotification_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodDelete, pattern_UserService_DeleteUserNotification_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/memos.api.v1.UserService/DeleteUserNotification", runtime.WithHTTPPathPattern("/api/v1/{name=users/*/notifications/*}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_UserService_DeleteUserNotification_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_UserService_DeleteUserNotification_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil return nil
} }
@ -1534,23 +1708,6 @@ func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux
} }
forward_UserService_DeleteUser_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) forward_UserService_DeleteUser_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
}) })
mux.Handle(http.MethodGet, pattern_UserService_GetUserAvatar_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.UserService/GetUserAvatar", runtime.WithHTTPPathPattern("/api/v1/{name=users/*}/avatar"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_UserService_GetUserAvatar_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_UserService_GetUserAvatar_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodGet, pattern_UserService_ListAllUserStats_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { mux.Handle(http.MethodGet, pattern_UserService_ListAllUserStats_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context()) ctx, cancel := context.WithCancel(req.Context())
defer cancel() defer cancel()
@ -1789,51 +1946,106 @@ func RegisterUserServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux
} }
forward_UserService_DeleteUserWebhook_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) forward_UserService_DeleteUserWebhook_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
}) })
mux.Handle(http.MethodGet, pattern_UserService_ListUserNotifications_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.UserService/ListUserNotifications", runtime.WithHTTPPathPattern("/api/v1/{parent=users/*}/notifications"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_UserService_ListUserNotifications_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_UserService_ListUserNotifications_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodPatch, pattern_UserService_UpdateUserNotification_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.UserService/UpdateUserNotification", runtime.WithHTTPPathPattern("/api/v1/{notification.name=users/*/notifications/*}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_UserService_UpdateUserNotification_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_UserService_UpdateUserNotification_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle(http.MethodDelete, pattern_UserService_DeleteUserNotification_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
annotatedContext, err := runtime.AnnotateContext(ctx, mux, req, "/memos.api.v1.UserService/DeleteUserNotification", runtime.WithHTTPPathPattern("/api/v1/{name=users/*/notifications/*}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_UserService_DeleteUserNotification_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_UserService_DeleteUserNotification_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil return nil
} }
var ( var (
pattern_UserService_ListUsers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "")) pattern_UserService_ListUsers_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, ""))
pattern_UserService_GetUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, "")) pattern_UserService_GetUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, ""))
pattern_UserService_CreateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "")) pattern_UserService_CreateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, ""))
pattern_UserService_UpdateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "user.name"}, "")) pattern_UserService_UpdateUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "user.name"}, ""))
pattern_UserService_DeleteUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, "")) pattern_UserService_DeleteUser_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, ""))
pattern_UserService_GetUserAvatar_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "name", "avatar"}, "")) pattern_UserService_ListAllUserStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "stats"))
pattern_UserService_ListAllUserStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "users"}, "stats")) pattern_UserService_GetUserStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, "getStats"))
pattern_UserService_GetUserStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3}, []string{"api", "v1", "users", "name"}, "getStats")) pattern_UserService_GetUserSetting_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "settings", "name"}, ""))
pattern_UserService_GetUserSetting_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "settings", "name"}, "")) pattern_UserService_UpdateUserSetting_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "settings", "setting.name"}, ""))
pattern_UserService_UpdateUserSetting_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "settings", "setting.name"}, "")) pattern_UserService_ListUserSettings_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "settings"}, ""))
pattern_UserService_ListUserSettings_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "settings"}, "")) pattern_UserService_ListUserAccessTokens_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "accessTokens"}, ""))
pattern_UserService_ListUserAccessTokens_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "accessTokens"}, "")) pattern_UserService_CreateUserAccessToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "accessTokens"}, ""))
pattern_UserService_CreateUserAccessToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "accessTokens"}, "")) pattern_UserService_DeleteUserAccessToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "accessTokens", "name"}, ""))
pattern_UserService_DeleteUserAccessToken_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "accessTokens", "name"}, "")) pattern_UserService_ListUserSessions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "sessions"}, ""))
pattern_UserService_ListUserSessions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "sessions"}, "")) pattern_UserService_RevokeUserSession_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "sessions", "name"}, ""))
pattern_UserService_RevokeUserSession_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "sessions", "name"}, "")) pattern_UserService_ListUserWebhooks_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "webhooks"}, ""))
pattern_UserService_ListUserWebhooks_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "webhooks"}, "")) pattern_UserService_CreateUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "webhooks"}, ""))
pattern_UserService_CreateUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "webhooks"}, "")) pattern_UserService_UpdateUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "webhooks", "webhook.name"}, ""))
pattern_UserService_UpdateUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "webhooks", "webhook.name"}, "")) pattern_UserService_DeleteUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "webhooks", "name"}, ""))
pattern_UserService_DeleteUserWebhook_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "webhooks", "name"}, "")) pattern_UserService_ListUserNotifications_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 2, 5, 3, 2, 4}, []string{"api", "v1", "users", "parent", "notifications"}, ""))
pattern_UserService_UpdateUserNotification_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "notifications", "notification.name"}, ""))
pattern_UserService_DeleteUserNotification_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 2, 3, 1, 0, 4, 4, 5, 4}, []string{"api", "v1", "users", "notifications", "name"}, ""))
) )
var ( var (
forward_UserService_ListUsers_0 = runtime.ForwardResponseMessage forward_UserService_ListUsers_0 = runtime.ForwardResponseMessage
forward_UserService_GetUser_0 = runtime.ForwardResponseMessage forward_UserService_GetUser_0 = runtime.ForwardResponseMessage
forward_UserService_CreateUser_0 = runtime.ForwardResponseMessage forward_UserService_CreateUser_0 = runtime.ForwardResponseMessage
forward_UserService_UpdateUser_0 = runtime.ForwardResponseMessage forward_UserService_UpdateUser_0 = runtime.ForwardResponseMessage
forward_UserService_DeleteUser_0 = runtime.ForwardResponseMessage forward_UserService_DeleteUser_0 = runtime.ForwardResponseMessage
forward_UserService_GetUserAvatar_0 = runtime.ForwardResponseMessage forward_UserService_ListAllUserStats_0 = runtime.ForwardResponseMessage
forward_UserService_ListAllUserStats_0 = runtime.ForwardResponseMessage forward_UserService_GetUserStats_0 = runtime.ForwardResponseMessage
forward_UserService_GetUserStats_0 = runtime.ForwardResponseMessage forward_UserService_GetUserSetting_0 = runtime.ForwardResponseMessage
forward_UserService_GetUserSetting_0 = runtime.ForwardResponseMessage forward_UserService_UpdateUserSetting_0 = runtime.ForwardResponseMessage
forward_UserService_UpdateUserSetting_0 = runtime.ForwardResponseMessage forward_UserService_ListUserSettings_0 = runtime.ForwardResponseMessage
forward_UserService_ListUserSettings_0 = runtime.ForwardResponseMessage forward_UserService_ListUserAccessTokens_0 = runtime.ForwardResponseMessage
forward_UserService_ListUserAccessTokens_0 = runtime.ForwardResponseMessage forward_UserService_CreateUserAccessToken_0 = runtime.ForwardResponseMessage
forward_UserService_CreateUserAccessToken_0 = runtime.ForwardResponseMessage forward_UserService_DeleteUserAccessToken_0 = runtime.ForwardResponseMessage
forward_UserService_DeleteUserAccessToken_0 = runtime.ForwardResponseMessage forward_UserService_ListUserSessions_0 = runtime.ForwardResponseMessage
forward_UserService_ListUserSessions_0 = runtime.ForwardResponseMessage forward_UserService_RevokeUserSession_0 = runtime.ForwardResponseMessage
forward_UserService_RevokeUserSession_0 = runtime.ForwardResponseMessage forward_UserService_ListUserWebhooks_0 = runtime.ForwardResponseMessage
forward_UserService_ListUserWebhooks_0 = runtime.ForwardResponseMessage forward_UserService_CreateUserWebhook_0 = runtime.ForwardResponseMessage
forward_UserService_CreateUserWebhook_0 = runtime.ForwardResponseMessage forward_UserService_UpdateUserWebhook_0 = runtime.ForwardResponseMessage
forward_UserService_UpdateUserWebhook_0 = runtime.ForwardResponseMessage forward_UserService_DeleteUserWebhook_0 = runtime.ForwardResponseMessage
forward_UserService_DeleteUserWebhook_0 = runtime.ForwardResponseMessage forward_UserService_ListUserNotifications_0 = runtime.ForwardResponseMessage
forward_UserService_UpdateUserNotification_0 = runtime.ForwardResponseMessage
forward_UserService_DeleteUserNotification_0 = runtime.ForwardResponseMessage
) )

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT. // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions: // versions:
// - protoc-gen-go-grpc v1.5.1 // - protoc-gen-go-grpc v1.6.0
// - protoc (unknown) // - protoc (unknown)
// source: api/v1/user_service.proto // source: api/v1/user_service.proto
@ -8,7 +8,6 @@ package apiv1
import ( import (
context "context" context "context"
httpbody "google.golang.org/genproto/googleapis/api/httpbody"
grpc "google.golang.org/grpc" grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes" codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status" status "google.golang.org/grpc/status"
@ -21,26 +20,28 @@ import (
const _ = grpc.SupportPackageIsVersion9 const _ = grpc.SupportPackageIsVersion9
const ( const (
UserService_ListUsers_FullMethodName = "/memos.api.v1.UserService/ListUsers" UserService_ListUsers_FullMethodName = "/memos.api.v1.UserService/ListUsers"
UserService_GetUser_FullMethodName = "/memos.api.v1.UserService/GetUser" UserService_GetUser_FullMethodName = "/memos.api.v1.UserService/GetUser"
UserService_CreateUser_FullMethodName = "/memos.api.v1.UserService/CreateUser" UserService_CreateUser_FullMethodName = "/memos.api.v1.UserService/CreateUser"
UserService_UpdateUser_FullMethodName = "/memos.api.v1.UserService/UpdateUser" UserService_UpdateUser_FullMethodName = "/memos.api.v1.UserService/UpdateUser"
UserService_DeleteUser_FullMethodName = "/memos.api.v1.UserService/DeleteUser" UserService_DeleteUser_FullMethodName = "/memos.api.v1.UserService/DeleteUser"
UserService_GetUserAvatar_FullMethodName = "/memos.api.v1.UserService/GetUserAvatar" UserService_ListAllUserStats_FullMethodName = "/memos.api.v1.UserService/ListAllUserStats"
UserService_ListAllUserStats_FullMethodName = "/memos.api.v1.UserService/ListAllUserStats" UserService_GetUserStats_FullMethodName = "/memos.api.v1.UserService/GetUserStats"
UserService_GetUserStats_FullMethodName = "/memos.api.v1.UserService/GetUserStats" UserService_GetUserSetting_FullMethodName = "/memos.api.v1.UserService/GetUserSetting"
UserService_GetUserSetting_FullMethodName = "/memos.api.v1.UserService/GetUserSetting" UserService_UpdateUserSetting_FullMethodName = "/memos.api.v1.UserService/UpdateUserSetting"
UserService_UpdateUserSetting_FullMethodName = "/memos.api.v1.UserService/UpdateUserSetting" UserService_ListUserSettings_FullMethodName = "/memos.api.v1.UserService/ListUserSettings"
UserService_ListUserSettings_FullMethodName = "/memos.api.v1.UserService/ListUserSettings" UserService_ListUserAccessTokens_FullMethodName = "/memos.api.v1.UserService/ListUserAccessTokens"
UserService_ListUserAccessTokens_FullMethodName = "/memos.api.v1.UserService/ListUserAccessTokens" UserService_CreateUserAccessToken_FullMethodName = "/memos.api.v1.UserService/CreateUserAccessToken"
UserService_CreateUserAccessToken_FullMethodName = "/memos.api.v1.UserService/CreateUserAccessToken" UserService_DeleteUserAccessToken_FullMethodName = "/memos.api.v1.UserService/DeleteUserAccessToken"
UserService_DeleteUserAccessToken_FullMethodName = "/memos.api.v1.UserService/DeleteUserAccessToken" UserService_ListUserSessions_FullMethodName = "/memos.api.v1.UserService/ListUserSessions"
UserService_ListUserSessions_FullMethodName = "/memos.api.v1.UserService/ListUserSessions" UserService_RevokeUserSession_FullMethodName = "/memos.api.v1.UserService/RevokeUserSession"
UserService_RevokeUserSession_FullMethodName = "/memos.api.v1.UserService/RevokeUserSession" UserService_ListUserWebhooks_FullMethodName = "/memos.api.v1.UserService/ListUserWebhooks"
UserService_ListUserWebhooks_FullMethodName = "/memos.api.v1.UserService/ListUserWebhooks" UserService_CreateUserWebhook_FullMethodName = "/memos.api.v1.UserService/CreateUserWebhook"
UserService_CreateUserWebhook_FullMethodName = "/memos.api.v1.UserService/CreateUserWebhook" UserService_UpdateUserWebhook_FullMethodName = "/memos.api.v1.UserService/UpdateUserWebhook"
UserService_UpdateUserWebhook_FullMethodName = "/memos.api.v1.UserService/UpdateUserWebhook" UserService_DeleteUserWebhook_FullMethodName = "/memos.api.v1.UserService/DeleteUserWebhook"
UserService_DeleteUserWebhook_FullMethodName = "/memos.api.v1.UserService/DeleteUserWebhook" UserService_ListUserNotifications_FullMethodName = "/memos.api.v1.UserService/ListUserNotifications"
UserService_UpdateUserNotification_FullMethodName = "/memos.api.v1.UserService/UpdateUserNotification"
UserService_DeleteUserNotification_FullMethodName = "/memos.api.v1.UserService/DeleteUserNotification"
) )
// UserServiceClient is the client API for UserService service. // UserServiceClient is the client API for UserService service.
@ -60,8 +61,6 @@ type UserServiceClient interface {
UpdateUser(ctx context.Context, in *UpdateUserRequest, opts ...grpc.CallOption) (*User, error) UpdateUser(ctx context.Context, in *UpdateUserRequest, opts ...grpc.CallOption) (*User, error)
// DeleteUser deletes a user. // DeleteUser deletes a user.
DeleteUser(ctx context.Context, in *DeleteUserRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) DeleteUser(ctx context.Context, in *DeleteUserRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
// GetUserAvatar gets the avatar of a user.
GetUserAvatar(ctx context.Context, in *GetUserAvatarRequest, opts ...grpc.CallOption) (*httpbody.HttpBody, error)
// ListAllUserStats returns statistics for all users. // ListAllUserStats returns statistics for all users.
ListAllUserStats(ctx context.Context, in *ListAllUserStatsRequest, opts ...grpc.CallOption) (*ListAllUserStatsResponse, error) ListAllUserStats(ctx context.Context, in *ListAllUserStatsRequest, opts ...grpc.CallOption) (*ListAllUserStatsResponse, error)
// GetUserStats returns statistics for a specific user. // GetUserStats returns statistics for a specific user.
@ -90,6 +89,12 @@ type UserServiceClient interface {
UpdateUserWebhook(ctx context.Context, in *UpdateUserWebhookRequest, opts ...grpc.CallOption) (*UserWebhook, error) UpdateUserWebhook(ctx context.Context, in *UpdateUserWebhookRequest, opts ...grpc.CallOption) (*UserWebhook, error)
// DeleteUserWebhook deletes a webhook for a user. // DeleteUserWebhook deletes a webhook for a user.
DeleteUserWebhook(ctx context.Context, in *DeleteUserWebhookRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) DeleteUserWebhook(ctx context.Context, in *DeleteUserWebhookRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
// ListUserNotifications lists notifications for a user.
ListUserNotifications(ctx context.Context, in *ListUserNotificationsRequest, opts ...grpc.CallOption) (*ListUserNotificationsResponse, error)
// UpdateUserNotification updates a notification.
UpdateUserNotification(ctx context.Context, in *UpdateUserNotificationRequest, opts ...grpc.CallOption) (*UserNotification, error)
// DeleteUserNotification deletes a notification.
DeleteUserNotification(ctx context.Context, in *DeleteUserNotificationRequest, opts ...grpc.CallOption) (*emptypb.Empty, error)
} }
type userServiceClient struct { type userServiceClient struct {
@ -150,16 +155,6 @@ func (c *userServiceClient) DeleteUser(ctx context.Context, in *DeleteUserReques
return out, nil return out, nil
} }
func (c *userServiceClient) GetUserAvatar(ctx context.Context, in *GetUserAvatarRequest, opts ...grpc.CallOption) (*httpbody.HttpBody, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(httpbody.HttpBody)
err := c.cc.Invoke(ctx, UserService_GetUserAvatar_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *userServiceClient) ListAllUserStats(ctx context.Context, in *ListAllUserStatsRequest, opts ...grpc.CallOption) (*ListAllUserStatsResponse, error) { func (c *userServiceClient) ListAllUserStats(ctx context.Context, in *ListAllUserStatsRequest, opts ...grpc.CallOption) (*ListAllUserStatsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListAllUserStatsResponse) out := new(ListAllUserStatsResponse)
@ -300,6 +295,36 @@ func (c *userServiceClient) DeleteUserWebhook(ctx context.Context, in *DeleteUse
return out, nil return out, nil
} }
func (c *userServiceClient) ListUserNotifications(ctx context.Context, in *ListUserNotificationsRequest, opts ...grpc.CallOption) (*ListUserNotificationsResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListUserNotificationsResponse)
err := c.cc.Invoke(ctx, UserService_ListUserNotifications_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *userServiceClient) UpdateUserNotification(ctx context.Context, in *UpdateUserNotificationRequest, opts ...grpc.CallOption) (*UserNotification, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(UserNotification)
err := c.cc.Invoke(ctx, UserService_UpdateUserNotification_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *userServiceClient) DeleteUserNotification(ctx context.Context, in *DeleteUserNotificationRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(emptypb.Empty)
err := c.cc.Invoke(ctx, UserService_DeleteUserNotification_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// UserServiceServer is the server API for UserService service. // UserServiceServer is the server API for UserService service.
// All implementations must embed UnimplementedUserServiceServer // All implementations must embed UnimplementedUserServiceServer
// for forward compatibility. // for forward compatibility.
@ -317,8 +342,6 @@ type UserServiceServer interface {
UpdateUser(context.Context, *UpdateUserRequest) (*User, error) UpdateUser(context.Context, *UpdateUserRequest) (*User, error)
// DeleteUser deletes a user. // DeleteUser deletes a user.
DeleteUser(context.Context, *DeleteUserRequest) (*emptypb.Empty, error) DeleteUser(context.Context, *DeleteUserRequest) (*emptypb.Empty, error)
// GetUserAvatar gets the avatar of a user.
GetUserAvatar(context.Context, *GetUserAvatarRequest) (*httpbody.HttpBody, error)
// ListAllUserStats returns statistics for all users. // ListAllUserStats returns statistics for all users.
ListAllUserStats(context.Context, *ListAllUserStatsRequest) (*ListAllUserStatsResponse, error) ListAllUserStats(context.Context, *ListAllUserStatsRequest) (*ListAllUserStatsResponse, error)
// GetUserStats returns statistics for a specific user. // GetUserStats returns statistics for a specific user.
@ -347,6 +370,12 @@ type UserServiceServer interface {
UpdateUserWebhook(context.Context, *UpdateUserWebhookRequest) (*UserWebhook, error) UpdateUserWebhook(context.Context, *UpdateUserWebhookRequest) (*UserWebhook, error)
// DeleteUserWebhook deletes a webhook for a user. // DeleteUserWebhook deletes a webhook for a user.
DeleteUserWebhook(context.Context, *DeleteUserWebhookRequest) (*emptypb.Empty, error) DeleteUserWebhook(context.Context, *DeleteUserWebhookRequest) (*emptypb.Empty, error)
// ListUserNotifications lists notifications for a user.
ListUserNotifications(context.Context, *ListUserNotificationsRequest) (*ListUserNotificationsResponse, error)
// UpdateUserNotification updates a notification.
UpdateUserNotification(context.Context, *UpdateUserNotificationRequest) (*UserNotification, error)
// DeleteUserNotification deletes a notification.
DeleteUserNotification(context.Context, *DeleteUserNotificationRequest) (*emptypb.Empty, error)
mustEmbedUnimplementedUserServiceServer() mustEmbedUnimplementedUserServiceServer()
} }
@ -358,64 +387,70 @@ type UserServiceServer interface {
type UnimplementedUserServiceServer struct{} type UnimplementedUserServiceServer struct{}
func (UnimplementedUserServiceServer) ListUsers(context.Context, *ListUsersRequest) (*ListUsersResponse, error) { func (UnimplementedUserServiceServer) ListUsers(context.Context, *ListUsersRequest) (*ListUsersResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListUsers not implemented") return nil, status.Error(codes.Unimplemented, "method ListUsers not implemented")
} }
func (UnimplementedUserServiceServer) GetUser(context.Context, *GetUserRequest) (*User, error) { func (UnimplementedUserServiceServer) GetUser(context.Context, *GetUserRequest) (*User, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetUser not implemented") return nil, status.Error(codes.Unimplemented, "method GetUser not implemented")
} }
func (UnimplementedUserServiceServer) CreateUser(context.Context, *CreateUserRequest) (*User, error) { func (UnimplementedUserServiceServer) CreateUser(context.Context, *CreateUserRequest) (*User, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateUser not implemented") return nil, status.Error(codes.Unimplemented, "method CreateUser not implemented")
} }
func (UnimplementedUserServiceServer) UpdateUser(context.Context, *UpdateUserRequest) (*User, error) { func (UnimplementedUserServiceServer) UpdateUser(context.Context, *UpdateUserRequest) (*User, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateUser not implemented") return nil, status.Error(codes.Unimplemented, "method UpdateUser not implemented")
} }
func (UnimplementedUserServiceServer) DeleteUser(context.Context, *DeleteUserRequest) (*emptypb.Empty, error) { func (UnimplementedUserServiceServer) DeleteUser(context.Context, *DeleteUserRequest) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteUser not implemented") return nil, status.Error(codes.Unimplemented, "method DeleteUser not implemented")
}
func (UnimplementedUserServiceServer) GetUserAvatar(context.Context, *GetUserAvatarRequest) (*httpbody.HttpBody, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetUserAvatar not implemented")
} }
func (UnimplementedUserServiceServer) ListAllUserStats(context.Context, *ListAllUserStatsRequest) (*ListAllUserStatsResponse, error) { func (UnimplementedUserServiceServer) ListAllUserStats(context.Context, *ListAllUserStatsRequest) (*ListAllUserStatsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListAllUserStats not implemented") return nil, status.Error(codes.Unimplemented, "method ListAllUserStats not implemented")
} }
func (UnimplementedUserServiceServer) GetUserStats(context.Context, *GetUserStatsRequest) (*UserStats, error) { func (UnimplementedUserServiceServer) GetUserStats(context.Context, *GetUserStatsRequest) (*UserStats, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetUserStats not implemented") return nil, status.Error(codes.Unimplemented, "method GetUserStats not implemented")
} }
func (UnimplementedUserServiceServer) GetUserSetting(context.Context, *GetUserSettingRequest) (*UserSetting, error) { func (UnimplementedUserServiceServer) GetUserSetting(context.Context, *GetUserSettingRequest) (*UserSetting, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetUserSetting not implemented") return nil, status.Error(codes.Unimplemented, "method GetUserSetting not implemented")
} }
func (UnimplementedUserServiceServer) UpdateUserSetting(context.Context, *UpdateUserSettingRequest) (*UserSetting, error) { func (UnimplementedUserServiceServer) UpdateUserSetting(context.Context, *UpdateUserSettingRequest) (*UserSetting, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateUserSetting not implemented") return nil, status.Error(codes.Unimplemented, "method UpdateUserSetting not implemented")
} }
func (UnimplementedUserServiceServer) ListUserSettings(context.Context, *ListUserSettingsRequest) (*ListUserSettingsResponse, error) { func (UnimplementedUserServiceServer) ListUserSettings(context.Context, *ListUserSettingsRequest) (*ListUserSettingsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListUserSettings not implemented") return nil, status.Error(codes.Unimplemented, "method ListUserSettings not implemented")
} }
func (UnimplementedUserServiceServer) ListUserAccessTokens(context.Context, *ListUserAccessTokensRequest) (*ListUserAccessTokensResponse, error) { func (UnimplementedUserServiceServer) ListUserAccessTokens(context.Context, *ListUserAccessTokensRequest) (*ListUserAccessTokensResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListUserAccessTokens not implemented") return nil, status.Error(codes.Unimplemented, "method ListUserAccessTokens not implemented")
} }
func (UnimplementedUserServiceServer) CreateUserAccessToken(context.Context, *CreateUserAccessTokenRequest) (*UserAccessToken, error) { func (UnimplementedUserServiceServer) CreateUserAccessToken(context.Context, *CreateUserAccessTokenRequest) (*UserAccessToken, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateUserAccessToken not implemented") return nil, status.Error(codes.Unimplemented, "method CreateUserAccessToken not implemented")
} }
func (UnimplementedUserServiceServer) DeleteUserAccessToken(context.Context, *DeleteUserAccessTokenRequest) (*emptypb.Empty, error) { func (UnimplementedUserServiceServer) DeleteUserAccessToken(context.Context, *DeleteUserAccessTokenRequest) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteUserAccessToken not implemented") return nil, status.Error(codes.Unimplemented, "method DeleteUserAccessToken not implemented")
} }
func (UnimplementedUserServiceServer) ListUserSessions(context.Context, *ListUserSessionsRequest) (*ListUserSessionsResponse, error) { func (UnimplementedUserServiceServer) ListUserSessions(context.Context, *ListUserSessionsRequest) (*ListUserSessionsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListUserSessions not implemented") return nil, status.Error(codes.Unimplemented, "method ListUserSessions not implemented")
} }
func (UnimplementedUserServiceServer) RevokeUserSession(context.Context, *RevokeUserSessionRequest) (*emptypb.Empty, error) { func (UnimplementedUserServiceServer) RevokeUserSession(context.Context, *RevokeUserSessionRequest) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method RevokeUserSession not implemented") return nil, status.Error(codes.Unimplemented, "method RevokeUserSession not implemented")
} }
func (UnimplementedUserServiceServer) ListUserWebhooks(context.Context, *ListUserWebhooksRequest) (*ListUserWebhooksResponse, error) { func (UnimplementedUserServiceServer) ListUserWebhooks(context.Context, *ListUserWebhooksRequest) (*ListUserWebhooksResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListUserWebhooks not implemented") return nil, status.Error(codes.Unimplemented, "method ListUserWebhooks not implemented")
} }
func (UnimplementedUserServiceServer) CreateUserWebhook(context.Context, *CreateUserWebhookRequest) (*UserWebhook, error) { func (UnimplementedUserServiceServer) CreateUserWebhook(context.Context, *CreateUserWebhookRequest) (*UserWebhook, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateUserWebhook not implemented") return nil, status.Error(codes.Unimplemented, "method CreateUserWebhook not implemented")
} }
func (UnimplementedUserServiceServer) UpdateUserWebhook(context.Context, *UpdateUserWebhookRequest) (*UserWebhook, error) { func (UnimplementedUserServiceServer) UpdateUserWebhook(context.Context, *UpdateUserWebhookRequest) (*UserWebhook, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateUserWebhook not implemented") return nil, status.Error(codes.Unimplemented, "method UpdateUserWebhook not implemented")
} }
func (UnimplementedUserServiceServer) DeleteUserWebhook(context.Context, *DeleteUserWebhookRequest) (*emptypb.Empty, error) { func (UnimplementedUserServiceServer) DeleteUserWebhook(context.Context, *DeleteUserWebhookRequest) (*emptypb.Empty, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteUserWebhook not implemented") return nil, status.Error(codes.Unimplemented, "method DeleteUserWebhook not implemented")
}
func (UnimplementedUserServiceServer) ListUserNotifications(context.Context, *ListUserNotificationsRequest) (*ListUserNotificationsResponse, error) {
return nil, status.Error(codes.Unimplemented, "method ListUserNotifications not implemented")
}
func (UnimplementedUserServiceServer) UpdateUserNotification(context.Context, *UpdateUserNotificationRequest) (*UserNotification, error) {
return nil, status.Error(codes.Unimplemented, "method UpdateUserNotification not implemented")
}
func (UnimplementedUserServiceServer) DeleteUserNotification(context.Context, *DeleteUserNotificationRequest) (*emptypb.Empty, error) {
return nil, status.Error(codes.Unimplemented, "method DeleteUserNotification not implemented")
} }
func (UnimplementedUserServiceServer) mustEmbedUnimplementedUserServiceServer() {} func (UnimplementedUserServiceServer) mustEmbedUnimplementedUserServiceServer() {}
func (UnimplementedUserServiceServer) testEmbeddedByValue() {} func (UnimplementedUserServiceServer) testEmbeddedByValue() {}
@ -428,7 +463,7 @@ type UnsafeUserServiceServer interface {
} }
func RegisterUserServiceServer(s grpc.ServiceRegistrar, srv UserServiceServer) { func RegisterUserServiceServer(s grpc.ServiceRegistrar, srv UserServiceServer) {
// If the following call pancis, it indicates UnimplementedUserServiceServer was // If the following call panics, it indicates UnimplementedUserServiceServer was
// embedded by pointer and is nil. This will cause panics if an // embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization // unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O. // time to prevent it from happening at runtime later due to I/O.
@ -528,24 +563,6 @@ func _UserService_DeleteUser_Handler(srv interface{}, ctx context.Context, dec f
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _UserService_GetUserAvatar_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetUserAvatarRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).GetUserAvatar(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: UserService_GetUserAvatar_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).GetUserAvatar(ctx, req.(*GetUserAvatarRequest))
}
return interceptor(ctx, in, info, handler)
}
func _UserService_ListAllUserStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { func _UserService_ListAllUserStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListAllUserStatsRequest) in := new(ListAllUserStatsRequest)
if err := dec(in); err != nil { if err := dec(in); err != nil {
@ -798,6 +815,60 @@ func _UserService_DeleteUserWebhook_Handler(srv interface{}, ctx context.Context
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _UserService_ListUserNotifications_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListUserNotificationsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).ListUserNotifications(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: UserService_ListUserNotifications_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).ListUserNotifications(ctx, req.(*ListUserNotificationsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _UserService_UpdateUserNotification_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateUserNotificationRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).UpdateUserNotification(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: UserService_UpdateUserNotification_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).UpdateUserNotification(ctx, req.(*UpdateUserNotificationRequest))
}
return interceptor(ctx, in, info, handler)
}
func _UserService_DeleteUserNotification_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteUserNotificationRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(UserServiceServer).DeleteUserNotification(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: UserService_DeleteUserNotification_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(UserServiceServer).DeleteUserNotification(ctx, req.(*DeleteUserNotificationRequest))
}
return interceptor(ctx, in, info, handler)
}
// UserService_ServiceDesc is the grpc.ServiceDesc for UserService service. // UserService_ServiceDesc is the grpc.ServiceDesc for UserService service.
// It's only intended for direct use with grpc.RegisterService, // It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy) // and not to be introspected or modified (even as a copy)
@ -825,10 +896,6 @@ var UserService_ServiceDesc = grpc.ServiceDesc{
MethodName: "DeleteUser", MethodName: "DeleteUser",
Handler: _UserService_DeleteUser_Handler, Handler: _UserService_DeleteUser_Handler,
}, },
{
MethodName: "GetUserAvatar",
Handler: _UserService_GetUserAvatar_Handler,
},
{ {
MethodName: "ListAllUserStats", MethodName: "ListAllUserStats",
Handler: _UserService_ListAllUserStats_Handler, Handler: _UserService_ListAllUserStats_Handler,
@ -885,6 +952,18 @@ var UserService_ServiceDesc = grpc.ServiceDesc{
MethodName: "DeleteUserWebhook", MethodName: "DeleteUserWebhook",
Handler: _UserService_DeleteUserWebhook_Handler, Handler: _UserService_DeleteUserWebhook_Handler,
}, },
{
MethodName: "ListUserNotifications",
Handler: _UserService_ListUserNotifications_Handler,
},
{
MethodName: "UpdateUserNotification",
Handler: _UserService_UpdateUserNotification_Handler,
},
{
MethodName: "DeleteUserNotification",
Handler: _UserService_DeleteUserNotification_Handler,
},
}, },
Streams: []grpc.StreamDesc{}, Streams: []grpc.StreamDesc{},
Metadata: "api/v1/user_service.proto", Metadata: "api/v1/user_service.proto",

File diff suppressed because it is too large Load Diff

View File

@ -1,203 +0,0 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc (unknown)
// source: api/v1/workspace_service.proto
package apiv1
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
WorkspaceService_GetWorkspaceProfile_FullMethodName = "/memos.api.v1.WorkspaceService/GetWorkspaceProfile"
WorkspaceService_GetWorkspaceSetting_FullMethodName = "/memos.api.v1.WorkspaceService/GetWorkspaceSetting"
WorkspaceService_UpdateWorkspaceSetting_FullMethodName = "/memos.api.v1.WorkspaceService/UpdateWorkspaceSetting"
)
// WorkspaceServiceClient is the client API for WorkspaceService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type WorkspaceServiceClient interface {
// Gets the workspace profile.
GetWorkspaceProfile(ctx context.Context, in *GetWorkspaceProfileRequest, opts ...grpc.CallOption) (*WorkspaceProfile, error)
// Gets a workspace setting.
GetWorkspaceSetting(ctx context.Context, in *GetWorkspaceSettingRequest, opts ...grpc.CallOption) (*WorkspaceSetting, error)
// Updates a workspace setting.
UpdateWorkspaceSetting(ctx context.Context, in *UpdateWorkspaceSettingRequest, opts ...grpc.CallOption) (*WorkspaceSetting, error)
}
type workspaceServiceClient struct {
cc grpc.ClientConnInterface
}
func NewWorkspaceServiceClient(cc grpc.ClientConnInterface) WorkspaceServiceClient {
return &workspaceServiceClient{cc}
}
func (c *workspaceServiceClient) GetWorkspaceProfile(ctx context.Context, in *GetWorkspaceProfileRequest, opts ...grpc.CallOption) (*WorkspaceProfile, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(WorkspaceProfile)
err := c.cc.Invoke(ctx, WorkspaceService_GetWorkspaceProfile_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *workspaceServiceClient) GetWorkspaceSetting(ctx context.Context, in *GetWorkspaceSettingRequest, opts ...grpc.CallOption) (*WorkspaceSetting, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(WorkspaceSetting)
err := c.cc.Invoke(ctx, WorkspaceService_GetWorkspaceSetting_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *workspaceServiceClient) UpdateWorkspaceSetting(ctx context.Context, in *UpdateWorkspaceSettingRequest, opts ...grpc.CallOption) (*WorkspaceSetting, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(WorkspaceSetting)
err := c.cc.Invoke(ctx, WorkspaceService_UpdateWorkspaceSetting_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// WorkspaceServiceServer is the server API for WorkspaceService service.
// All implementations must embed UnimplementedWorkspaceServiceServer
// for forward compatibility.
type WorkspaceServiceServer interface {
// Gets the workspace profile.
GetWorkspaceProfile(context.Context, *GetWorkspaceProfileRequest) (*WorkspaceProfile, error)
// Gets a workspace setting.
GetWorkspaceSetting(context.Context, *GetWorkspaceSettingRequest) (*WorkspaceSetting, error)
// Updates a workspace setting.
UpdateWorkspaceSetting(context.Context, *UpdateWorkspaceSettingRequest) (*WorkspaceSetting, error)
mustEmbedUnimplementedWorkspaceServiceServer()
}
// UnimplementedWorkspaceServiceServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedWorkspaceServiceServer struct{}
func (UnimplementedWorkspaceServiceServer) GetWorkspaceProfile(context.Context, *GetWorkspaceProfileRequest) (*WorkspaceProfile, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetWorkspaceProfile not implemented")
}
func (UnimplementedWorkspaceServiceServer) GetWorkspaceSetting(context.Context, *GetWorkspaceSettingRequest) (*WorkspaceSetting, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetWorkspaceSetting not implemented")
}
func (UnimplementedWorkspaceServiceServer) UpdateWorkspaceSetting(context.Context, *UpdateWorkspaceSettingRequest) (*WorkspaceSetting, error) {
return nil, status.Errorf(codes.Unimplemented, "method UpdateWorkspaceSetting not implemented")
}
func (UnimplementedWorkspaceServiceServer) mustEmbedUnimplementedWorkspaceServiceServer() {}
func (UnimplementedWorkspaceServiceServer) testEmbeddedByValue() {}
// UnsafeWorkspaceServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to WorkspaceServiceServer will
// result in compilation errors.
type UnsafeWorkspaceServiceServer interface {
mustEmbedUnimplementedWorkspaceServiceServer()
}
func RegisterWorkspaceServiceServer(s grpc.ServiceRegistrar, srv WorkspaceServiceServer) {
// If the following call pancis, it indicates UnimplementedWorkspaceServiceServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&WorkspaceService_ServiceDesc, srv)
}
func _WorkspaceService_GetWorkspaceProfile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetWorkspaceProfileRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(WorkspaceServiceServer).GetWorkspaceProfile(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: WorkspaceService_GetWorkspaceProfile_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(WorkspaceServiceServer).GetWorkspaceProfile(ctx, req.(*GetWorkspaceProfileRequest))
}
return interceptor(ctx, in, info, handler)
}
func _WorkspaceService_GetWorkspaceSetting_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetWorkspaceSettingRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(WorkspaceServiceServer).GetWorkspaceSetting(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: WorkspaceService_GetWorkspaceSetting_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(WorkspaceServiceServer).GetWorkspaceSetting(ctx, req.(*GetWorkspaceSettingRequest))
}
return interceptor(ctx, in, info, handler)
}
func _WorkspaceService_UpdateWorkspaceSetting_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateWorkspaceSettingRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(WorkspaceServiceServer).UpdateWorkspaceSetting(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: WorkspaceService_UpdateWorkspaceSetting_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(WorkspaceServiceServer).UpdateWorkspaceSetting(ctx, req.(*UpdateWorkspaceSettingRequest))
}
return interceptor(ctx, in, info, handler)
}
// WorkspaceService_ServiceDesc is the grpc.ServiceDesc for WorkspaceService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var WorkspaceService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "memos.api.v1.WorkspaceService",
HandlerType: (*WorkspaceServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetWorkspaceProfile",
Handler: _WorkspaceService_GetWorkspaceProfile_Handler,
},
{
MethodName: "GetWorkspaceSetting",
Handler: _WorkspaceService_GetWorkspaceSetting_Handler,
},
{
MethodName: "UpdateWorkspaceSetting",
Handler: _WorkspaceService_UpdateWorkspaceSetting_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "api/v1/workspace_service.proto",
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.10 // protoc-gen-go v1.36.11
// protoc (unknown) // protoc (unknown)
// source: store/activity.proto // source: store/activity.proto

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.10 // protoc-gen-go v1.36.11
// protoc (unknown) // protoc (unknown)
// source: store/attachment.proto // source: store/attachment.proto
@ -210,7 +210,7 @@ var File_store_attachment_proto protoreflect.FileDescriptor
const file_store_attachment_proto_rawDesc = "" + const file_store_attachment_proto_rawDesc = "" +
"\n" + "\n" +
"\x16store/attachment.proto\x12\vmemos.store\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1dstore/workspace_setting.proto\"\x8c\x02\n" + "\x16store/attachment.proto\x12\vmemos.store\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1cstore/instance_setting.proto\"\x8c\x02\n" +
"\x11AttachmentPayload\x12F\n" + "\x11AttachmentPayload\x12F\n" +
"\ts3_object\x18\x01 \x01(\v2'.memos.store.AttachmentPayload.S3ObjectH\x00R\bs3Object\x1a\xa3\x01\n" + "\ts3_object\x18\x01 \x01(\v2'.memos.store.AttachmentPayload.S3ObjectH\x00R\bs3Object\x1a\xa3\x01\n" +
"\bS3Object\x129\n" + "\bS3Object\x129\n" +
@ -262,7 +262,7 @@ func file_store_attachment_proto_init() {
if File_store_attachment_proto != nil { if File_store_attachment_proto != nil {
return return
} }
file_store_workspace_setting_proto_init() file_store_instance_setting_proto_init()
file_store_attachment_proto_msgTypes[0].OneofWrappers = []any{ file_store_attachment_proto_msgTypes[0].OneofWrappers = []any{
(*AttachmentPayload_S3Object_)(nil), (*AttachmentPayload_S3Object_)(nil),
} }

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.10 // protoc-gen-go v1.36.11
// protoc (unknown) // protoc (unknown)
// source: store/idp.proto // source: store/idp.proto

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.10 // protoc-gen-go v1.36.11
// protoc (unknown) // protoc (unknown)
// source: store/inbox.proto // source: store/inbox.proto
@ -25,8 +25,8 @@ type InboxMessage_Type int32
const ( const (
InboxMessage_TYPE_UNSPECIFIED InboxMessage_Type = 0 InboxMessage_TYPE_UNSPECIFIED InboxMessage_Type = 0
InboxMessage_MEMO_COMMENT InboxMessage_Type = 1 // Memo comment notification.
InboxMessage_VERSION_UPDATE InboxMessage_Type = 2 InboxMessage_MEMO_COMMENT InboxMessage_Type = 1
) )
// Enum value maps for InboxMessage_Type. // Enum value maps for InboxMessage_Type.
@ -34,12 +34,10 @@ var (
InboxMessage_Type_name = map[int32]string{ InboxMessage_Type_name = map[int32]string{
0: "TYPE_UNSPECIFIED", 0: "TYPE_UNSPECIFIED",
1: "MEMO_COMMENT", 1: "MEMO_COMMENT",
2: "VERSION_UPDATE",
} }
InboxMessage_Type_value = map[string]int32{ InboxMessage_Type_value = map[string]int32{
"TYPE_UNSPECIFIED": 0, "TYPE_UNSPECIFIED": 0,
"MEMO_COMMENT": 1, "MEMO_COMMENT": 1,
"VERSION_UPDATE": 2,
} }
) )
@ -71,9 +69,11 @@ func (InboxMessage_Type) EnumDescriptor() ([]byte, []int) {
} }
type InboxMessage struct { type InboxMessage struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Type InboxMessage_Type `protobuf:"varint,1,opt,name=type,proto3,enum=memos.store.InboxMessage_Type" json:"type,omitempty"` // The type of the inbox message.
ActivityId *int32 `protobuf:"varint,2,opt,name=activity_id,json=activityId,proto3,oneof" json:"activity_id,omitempty"` Type InboxMessage_Type `protobuf:"varint,1,opt,name=type,proto3,enum=memos.store.InboxMessage_Type" json:"type,omitempty"`
// The system-generated unique ID of related activity.
ActivityId *int32 `protobuf:"varint,2,opt,name=activity_id,json=activityId,proto3,oneof" json:"activity_id,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@ -126,15 +126,14 @@ var File_store_inbox_proto protoreflect.FileDescriptor
const file_store_inbox_proto_rawDesc = "" + const file_store_inbox_proto_rawDesc = "" +
"\n" + "\n" +
"\x11store/inbox.proto\x12\vmemos.store\"\xbc\x01\n" + "\x11store/inbox.proto\x12\vmemos.store\"\xa8\x01\n" +
"\fInboxMessage\x122\n" + "\fInboxMessage\x122\n" +
"\x04type\x18\x01 \x01(\x0e2\x1e.memos.store.InboxMessage.TypeR\x04type\x12$\n" + "\x04type\x18\x01 \x01(\x0e2\x1e.memos.store.InboxMessage.TypeR\x04type\x12$\n" +
"\vactivity_id\x18\x02 \x01(\x05H\x00R\n" + "\vactivity_id\x18\x02 \x01(\x05H\x00R\n" +
"activityId\x88\x01\x01\"B\n" + "activityId\x88\x01\x01\".\n" +
"\x04Type\x12\x14\n" + "\x04Type\x12\x14\n" +
"\x10TYPE_UNSPECIFIED\x10\x00\x12\x10\n" + "\x10TYPE_UNSPECIFIED\x10\x00\x12\x10\n" +
"\fMEMO_COMMENT\x10\x01\x12\x12\n" + "\fMEMO_COMMENT\x10\x01B\x0e\n" +
"\x0eVERSION_UPDATE\x10\x02B\x0e\n" +
"\f_activity_idB\x95\x01\n" + "\f_activity_idB\x95\x01\n" +
"\x0fcom.memos.storeB\n" + "\x0fcom.memos.storeB\n" +
"InboxProtoP\x01Z)github.com/usememos/memos/proto/gen/store\xa2\x02\x03MSX\xaa\x02\vMemos.Store\xca\x02\vMemos\\Store\xe2\x02\x17Memos\\Store\\GPBMetadata\xea\x02\fMemos::Storeb\x06proto3" "InboxProtoP\x01Z)github.com/usememos/memos/proto/gen/store\xa2\x02\x03MSX\xaa\x02\vMemos.Store\xca\x02\vMemos\\Store\xe2\x02\x17Memos\\Store\\GPBMetadata\xea\x02\fMemos::Storeb\x06proto3"

View File

@ -0,0 +1,874 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc (unknown)
// source: store/instance_setting.proto
package store
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type InstanceSettingKey int32
const (
InstanceSettingKey_INSTANCE_SETTING_KEY_UNSPECIFIED InstanceSettingKey = 0
// BASIC is the key for basic settings.
InstanceSettingKey_BASIC InstanceSettingKey = 1
// GENERAL is the key for general settings.
InstanceSettingKey_GENERAL InstanceSettingKey = 2
// STORAGE is the key for storage settings.
InstanceSettingKey_STORAGE InstanceSettingKey = 3
// MEMO_RELATED is the key for memo related settings.
InstanceSettingKey_MEMO_RELATED InstanceSettingKey = 4
)
// Enum value maps for InstanceSettingKey.
var (
InstanceSettingKey_name = map[int32]string{
0: "INSTANCE_SETTING_KEY_UNSPECIFIED",
1: "BASIC",
2: "GENERAL",
3: "STORAGE",
4: "MEMO_RELATED",
}
InstanceSettingKey_value = map[string]int32{
"INSTANCE_SETTING_KEY_UNSPECIFIED": 0,
"BASIC": 1,
"GENERAL": 2,
"STORAGE": 3,
"MEMO_RELATED": 4,
}
)
func (x InstanceSettingKey) Enum() *InstanceSettingKey {
p := new(InstanceSettingKey)
*p = x
return p
}
func (x InstanceSettingKey) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (InstanceSettingKey) Descriptor() protoreflect.EnumDescriptor {
return file_store_instance_setting_proto_enumTypes[0].Descriptor()
}
func (InstanceSettingKey) Type() protoreflect.EnumType {
return &file_store_instance_setting_proto_enumTypes[0]
}
func (x InstanceSettingKey) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use InstanceSettingKey.Descriptor instead.
func (InstanceSettingKey) EnumDescriptor() ([]byte, []int) {
return file_store_instance_setting_proto_rawDescGZIP(), []int{0}
}
type InstanceStorageSetting_StorageType int32
const (
InstanceStorageSetting_STORAGE_TYPE_UNSPECIFIED InstanceStorageSetting_StorageType = 0
// STORAGE_TYPE_DATABASE is the database storage type.
InstanceStorageSetting_DATABASE InstanceStorageSetting_StorageType = 1
// STORAGE_TYPE_LOCAL is the local storage type.
InstanceStorageSetting_LOCAL InstanceStorageSetting_StorageType = 2
// STORAGE_TYPE_S3 is the S3 storage type.
InstanceStorageSetting_S3 InstanceStorageSetting_StorageType = 3
)
// Enum value maps for InstanceStorageSetting_StorageType.
var (
InstanceStorageSetting_StorageType_name = map[int32]string{
0: "STORAGE_TYPE_UNSPECIFIED",
1: "DATABASE",
2: "LOCAL",
3: "S3",
}
InstanceStorageSetting_StorageType_value = map[string]int32{
"STORAGE_TYPE_UNSPECIFIED": 0,
"DATABASE": 1,
"LOCAL": 2,
"S3": 3,
}
)
func (x InstanceStorageSetting_StorageType) Enum() *InstanceStorageSetting_StorageType {
p := new(InstanceStorageSetting_StorageType)
*p = x
return p
}
func (x InstanceStorageSetting_StorageType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (InstanceStorageSetting_StorageType) Descriptor() protoreflect.EnumDescriptor {
return file_store_instance_setting_proto_enumTypes[1].Descriptor()
}
func (InstanceStorageSetting_StorageType) Type() protoreflect.EnumType {
return &file_store_instance_setting_proto_enumTypes[1]
}
func (x InstanceStorageSetting_StorageType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use InstanceStorageSetting_StorageType.Descriptor instead.
func (InstanceStorageSetting_StorageType) EnumDescriptor() ([]byte, []int) {
return file_store_instance_setting_proto_rawDescGZIP(), []int{4, 0}
}
type InstanceSetting struct {
state protoimpl.MessageState `protogen:"open.v1"`
Key InstanceSettingKey `protobuf:"varint,1,opt,name=key,proto3,enum=memos.store.InstanceSettingKey" json:"key,omitempty"`
// Types that are valid to be assigned to Value:
//
// *InstanceSetting_BasicSetting
// *InstanceSetting_GeneralSetting
// *InstanceSetting_StorageSetting
// *InstanceSetting_MemoRelatedSetting
Value isInstanceSetting_Value `protobuf_oneof:"value"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *InstanceSetting) Reset() {
*x = InstanceSetting{}
mi := &file_store_instance_setting_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *InstanceSetting) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*InstanceSetting) ProtoMessage() {}
func (x *InstanceSetting) ProtoReflect() protoreflect.Message {
mi := &file_store_instance_setting_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use InstanceSetting.ProtoReflect.Descriptor instead.
func (*InstanceSetting) Descriptor() ([]byte, []int) {
return file_store_instance_setting_proto_rawDescGZIP(), []int{0}
}
func (x *InstanceSetting) GetKey() InstanceSettingKey {
if x != nil {
return x.Key
}
return InstanceSettingKey_INSTANCE_SETTING_KEY_UNSPECIFIED
}
func (x *InstanceSetting) GetValue() isInstanceSetting_Value {
if x != nil {
return x.Value
}
return nil
}
func (x *InstanceSetting) GetBasicSetting() *InstanceBasicSetting {
if x != nil {
if x, ok := x.Value.(*InstanceSetting_BasicSetting); ok {
return x.BasicSetting
}
}
return nil
}
func (x *InstanceSetting) GetGeneralSetting() *InstanceGeneralSetting {
if x != nil {
if x, ok := x.Value.(*InstanceSetting_GeneralSetting); ok {
return x.GeneralSetting
}
}
return nil
}
func (x *InstanceSetting) GetStorageSetting() *InstanceStorageSetting {
if x != nil {
if x, ok := x.Value.(*InstanceSetting_StorageSetting); ok {
return x.StorageSetting
}
}
return nil
}
func (x *InstanceSetting) GetMemoRelatedSetting() *InstanceMemoRelatedSetting {
if x != nil {
if x, ok := x.Value.(*InstanceSetting_MemoRelatedSetting); ok {
return x.MemoRelatedSetting
}
}
return nil
}
type isInstanceSetting_Value interface {
isInstanceSetting_Value()
}
type InstanceSetting_BasicSetting struct {
BasicSetting *InstanceBasicSetting `protobuf:"bytes,2,opt,name=basic_setting,json=basicSetting,proto3,oneof"`
}
type InstanceSetting_GeneralSetting struct {
GeneralSetting *InstanceGeneralSetting `protobuf:"bytes,3,opt,name=general_setting,json=generalSetting,proto3,oneof"`
}
type InstanceSetting_StorageSetting struct {
StorageSetting *InstanceStorageSetting `protobuf:"bytes,4,opt,name=storage_setting,json=storageSetting,proto3,oneof"`
}
type InstanceSetting_MemoRelatedSetting struct {
MemoRelatedSetting *InstanceMemoRelatedSetting `protobuf:"bytes,5,opt,name=memo_related_setting,json=memoRelatedSetting,proto3,oneof"`
}
func (*InstanceSetting_BasicSetting) isInstanceSetting_Value() {}
func (*InstanceSetting_GeneralSetting) isInstanceSetting_Value() {}
func (*InstanceSetting_StorageSetting) isInstanceSetting_Value() {}
func (*InstanceSetting_MemoRelatedSetting) isInstanceSetting_Value() {}
type InstanceBasicSetting struct {
state protoimpl.MessageState `protogen:"open.v1"`
// The secret key for instance. Mainly used for session management.
SecretKey string `protobuf:"bytes,1,opt,name=secret_key,json=secretKey,proto3" json:"secret_key,omitempty"`
// The current schema version of database.
SchemaVersion string `protobuf:"bytes,2,opt,name=schema_version,json=schemaVersion,proto3" json:"schema_version,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *InstanceBasicSetting) Reset() {
*x = InstanceBasicSetting{}
mi := &file_store_instance_setting_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *InstanceBasicSetting) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*InstanceBasicSetting) ProtoMessage() {}
func (x *InstanceBasicSetting) ProtoReflect() protoreflect.Message {
mi := &file_store_instance_setting_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use InstanceBasicSetting.ProtoReflect.Descriptor instead.
func (*InstanceBasicSetting) Descriptor() ([]byte, []int) {
return file_store_instance_setting_proto_rawDescGZIP(), []int{1}
}
func (x *InstanceBasicSetting) GetSecretKey() string {
if x != nil {
return x.SecretKey
}
return ""
}
func (x *InstanceBasicSetting) GetSchemaVersion() string {
if x != nil {
return x.SchemaVersion
}
return ""
}
type InstanceGeneralSetting struct {
state protoimpl.MessageState `protogen:"open.v1"`
// disallow_user_registration disallows user registration.
DisallowUserRegistration bool `protobuf:"varint,2,opt,name=disallow_user_registration,json=disallowUserRegistration,proto3" json:"disallow_user_registration,omitempty"`
// disallow_password_auth disallows password authentication.
DisallowPasswordAuth bool `protobuf:"varint,3,opt,name=disallow_password_auth,json=disallowPasswordAuth,proto3" json:"disallow_password_auth,omitempty"`
// additional_script is the additional script.
AdditionalScript string `protobuf:"bytes,4,opt,name=additional_script,json=additionalScript,proto3" json:"additional_script,omitempty"`
// additional_style is the additional style.
AdditionalStyle string `protobuf:"bytes,5,opt,name=additional_style,json=additionalStyle,proto3" json:"additional_style,omitempty"`
// custom_profile is the custom profile.
CustomProfile *InstanceCustomProfile `protobuf:"bytes,6,opt,name=custom_profile,json=customProfile,proto3" json:"custom_profile,omitempty"`
// week_start_day_offset is the week start day offset from Sunday.
// 0: Sunday, 1: Monday, 2: Tuesday, 3: Wednesday, 4: Thursday, 5: Friday, 6: Saturday
// Default is Sunday.
WeekStartDayOffset int32 `protobuf:"varint,7,opt,name=week_start_day_offset,json=weekStartDayOffset,proto3" json:"week_start_day_offset,omitempty"`
// disallow_change_username disallows changing username.
DisallowChangeUsername bool `protobuf:"varint,8,opt,name=disallow_change_username,json=disallowChangeUsername,proto3" json:"disallow_change_username,omitempty"`
// disallow_change_nickname disallows changing nickname.
DisallowChangeNickname bool `protobuf:"varint,9,opt,name=disallow_change_nickname,json=disallowChangeNickname,proto3" json:"disallow_change_nickname,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *InstanceGeneralSetting) Reset() {
*x = InstanceGeneralSetting{}
mi := &file_store_instance_setting_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *InstanceGeneralSetting) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*InstanceGeneralSetting) ProtoMessage() {}
func (x *InstanceGeneralSetting) ProtoReflect() protoreflect.Message {
mi := &file_store_instance_setting_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use InstanceGeneralSetting.ProtoReflect.Descriptor instead.
func (*InstanceGeneralSetting) Descriptor() ([]byte, []int) {
return file_store_instance_setting_proto_rawDescGZIP(), []int{2}
}
func (x *InstanceGeneralSetting) GetDisallowUserRegistration() bool {
if x != nil {
return x.DisallowUserRegistration
}
return false
}
func (x *InstanceGeneralSetting) GetDisallowPasswordAuth() bool {
if x != nil {
return x.DisallowPasswordAuth
}
return false
}
func (x *InstanceGeneralSetting) GetAdditionalScript() string {
if x != nil {
return x.AdditionalScript
}
return ""
}
func (x *InstanceGeneralSetting) GetAdditionalStyle() string {
if x != nil {
return x.AdditionalStyle
}
return ""
}
func (x *InstanceGeneralSetting) GetCustomProfile() *InstanceCustomProfile {
if x != nil {
return x.CustomProfile
}
return nil
}
func (x *InstanceGeneralSetting) GetWeekStartDayOffset() int32 {
if x != nil {
return x.WeekStartDayOffset
}
return 0
}
func (x *InstanceGeneralSetting) GetDisallowChangeUsername() bool {
if x != nil {
return x.DisallowChangeUsername
}
return false
}
func (x *InstanceGeneralSetting) GetDisallowChangeNickname() bool {
if x != nil {
return x.DisallowChangeNickname
}
return false
}
type InstanceCustomProfile struct {
state protoimpl.MessageState `protogen:"open.v1"`
Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"`
Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
LogoUrl string `protobuf:"bytes,3,opt,name=logo_url,json=logoUrl,proto3" json:"logo_url,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *InstanceCustomProfile) Reset() {
*x = InstanceCustomProfile{}
mi := &file_store_instance_setting_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *InstanceCustomProfile) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*InstanceCustomProfile) ProtoMessage() {}
func (x *InstanceCustomProfile) ProtoReflect() protoreflect.Message {
mi := &file_store_instance_setting_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use InstanceCustomProfile.ProtoReflect.Descriptor instead.
func (*InstanceCustomProfile) Descriptor() ([]byte, []int) {
return file_store_instance_setting_proto_rawDescGZIP(), []int{3}
}
func (x *InstanceCustomProfile) GetTitle() string {
if x != nil {
return x.Title
}
return ""
}
func (x *InstanceCustomProfile) GetDescription() string {
if x != nil {
return x.Description
}
return ""
}
func (x *InstanceCustomProfile) GetLogoUrl() string {
if x != nil {
return x.LogoUrl
}
return ""
}
type InstanceStorageSetting struct {
state protoimpl.MessageState `protogen:"open.v1"`
// storage_type is the storage type.
StorageType InstanceStorageSetting_StorageType `protobuf:"varint,1,opt,name=storage_type,json=storageType,proto3,enum=memos.store.InstanceStorageSetting_StorageType" json:"storage_type,omitempty"`
// The template of file path.
// e.g. assets/{timestamp}_{filename}
FilepathTemplate string `protobuf:"bytes,2,opt,name=filepath_template,json=filepathTemplate,proto3" json:"filepath_template,omitempty"`
// The max upload size in megabytes.
UploadSizeLimitMb int64 `protobuf:"varint,3,opt,name=upload_size_limit_mb,json=uploadSizeLimitMb,proto3" json:"upload_size_limit_mb,omitempty"`
// The S3 config.
S3Config *StorageS3Config `protobuf:"bytes,4,opt,name=s3_config,json=s3Config,proto3" json:"s3_config,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *InstanceStorageSetting) Reset() {
*x = InstanceStorageSetting{}
mi := &file_store_instance_setting_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *InstanceStorageSetting) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*InstanceStorageSetting) ProtoMessage() {}
func (x *InstanceStorageSetting) ProtoReflect() protoreflect.Message {
mi := &file_store_instance_setting_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use InstanceStorageSetting.ProtoReflect.Descriptor instead.
func (*InstanceStorageSetting) Descriptor() ([]byte, []int) {
return file_store_instance_setting_proto_rawDescGZIP(), []int{4}
}
func (x *InstanceStorageSetting) GetStorageType() InstanceStorageSetting_StorageType {
if x != nil {
return x.StorageType
}
return InstanceStorageSetting_STORAGE_TYPE_UNSPECIFIED
}
func (x *InstanceStorageSetting) GetFilepathTemplate() string {
if x != nil {
return x.FilepathTemplate
}
return ""
}
func (x *InstanceStorageSetting) GetUploadSizeLimitMb() int64 {
if x != nil {
return x.UploadSizeLimitMb
}
return 0
}
func (x *InstanceStorageSetting) GetS3Config() *StorageS3Config {
if x != nil {
return x.S3Config
}
return nil
}
// Reference: https://developers.cloudflare.com/r2/examples/aws/aws-sdk-go/
type StorageS3Config struct {
state protoimpl.MessageState `protogen:"open.v1"`
AccessKeyId string `protobuf:"bytes,1,opt,name=access_key_id,json=accessKeyId,proto3" json:"access_key_id,omitempty"`
AccessKeySecret string `protobuf:"bytes,2,opt,name=access_key_secret,json=accessKeySecret,proto3" json:"access_key_secret,omitempty"`
Endpoint string `protobuf:"bytes,3,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
Region string `protobuf:"bytes,4,opt,name=region,proto3" json:"region,omitempty"`
Bucket string `protobuf:"bytes,5,opt,name=bucket,proto3" json:"bucket,omitempty"`
UsePathStyle bool `protobuf:"varint,6,opt,name=use_path_style,json=usePathStyle,proto3" json:"use_path_style,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *StorageS3Config) Reset() {
*x = StorageS3Config{}
mi := &file_store_instance_setting_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *StorageS3Config) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*StorageS3Config) ProtoMessage() {}
func (x *StorageS3Config) ProtoReflect() protoreflect.Message {
mi := &file_store_instance_setting_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use StorageS3Config.ProtoReflect.Descriptor instead.
func (*StorageS3Config) Descriptor() ([]byte, []int) {
return file_store_instance_setting_proto_rawDescGZIP(), []int{5}
}
func (x *StorageS3Config) GetAccessKeyId() string {
if x != nil {
return x.AccessKeyId
}
return ""
}
func (x *StorageS3Config) GetAccessKeySecret() string {
if x != nil {
return x.AccessKeySecret
}
return ""
}
func (x *StorageS3Config) GetEndpoint() string {
if x != nil {
return x.Endpoint
}
return ""
}
func (x *StorageS3Config) GetRegion() string {
if x != nil {
return x.Region
}
return ""
}
func (x *StorageS3Config) GetBucket() string {
if x != nil {
return x.Bucket
}
return ""
}
func (x *StorageS3Config) GetUsePathStyle() bool {
if x != nil {
return x.UsePathStyle
}
return false
}
type InstanceMemoRelatedSetting struct {
state protoimpl.MessageState `protogen:"open.v1"`
// disallow_public_visibility disallows set memo as public visibility.
DisallowPublicVisibility bool `protobuf:"varint,1,opt,name=disallow_public_visibility,json=disallowPublicVisibility,proto3" json:"disallow_public_visibility,omitempty"`
// display_with_update_time orders and displays memo with update time.
DisplayWithUpdateTime bool `protobuf:"varint,2,opt,name=display_with_update_time,json=displayWithUpdateTime,proto3" json:"display_with_update_time,omitempty"`
// content_length_limit is the limit of content length. Unit is byte.
ContentLengthLimit int32 `protobuf:"varint,3,opt,name=content_length_limit,json=contentLengthLimit,proto3" json:"content_length_limit,omitempty"`
// enable_double_click_edit enables editing on double click.
EnableDoubleClickEdit bool `protobuf:"varint,4,opt,name=enable_double_click_edit,json=enableDoubleClickEdit,proto3" json:"enable_double_click_edit,omitempty"`
// reactions is the list of reactions.
Reactions []string `protobuf:"bytes,7,rep,name=reactions,proto3" json:"reactions,omitempty"`
// enable_blur_nsfw_content enables blurring of content marked as not safe for work (NSFW).
EnableBlurNsfwContent bool `protobuf:"varint,9,opt,name=enable_blur_nsfw_content,json=enableBlurNsfwContent,proto3" json:"enable_blur_nsfw_content,omitempty"`
// nsfw_tags is the list of tags that mark content as NSFW for blurring.
NsfwTags []string `protobuf:"bytes,10,rep,name=nsfw_tags,json=nsfwTags,proto3" json:"nsfw_tags,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *InstanceMemoRelatedSetting) Reset() {
*x = InstanceMemoRelatedSetting{}
mi := &file_store_instance_setting_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *InstanceMemoRelatedSetting) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*InstanceMemoRelatedSetting) ProtoMessage() {}
func (x *InstanceMemoRelatedSetting) ProtoReflect() protoreflect.Message {
mi := &file_store_instance_setting_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use InstanceMemoRelatedSetting.ProtoReflect.Descriptor instead.
func (*InstanceMemoRelatedSetting) Descriptor() ([]byte, []int) {
return file_store_instance_setting_proto_rawDescGZIP(), []int{6}
}
func (x *InstanceMemoRelatedSetting) GetDisallowPublicVisibility() bool {
if x != nil {
return x.DisallowPublicVisibility
}
return false
}
func (x *InstanceMemoRelatedSetting) GetDisplayWithUpdateTime() bool {
if x != nil {
return x.DisplayWithUpdateTime
}
return false
}
func (x *InstanceMemoRelatedSetting) GetContentLengthLimit() int32 {
if x != nil {
return x.ContentLengthLimit
}
return 0
}
func (x *InstanceMemoRelatedSetting) GetEnableDoubleClickEdit() bool {
if x != nil {
return x.EnableDoubleClickEdit
}
return false
}
func (x *InstanceMemoRelatedSetting) GetReactions() []string {
if x != nil {
return x.Reactions
}
return nil
}
func (x *InstanceMemoRelatedSetting) GetEnableBlurNsfwContent() bool {
if x != nil {
return x.EnableBlurNsfwContent
}
return false
}
func (x *InstanceMemoRelatedSetting) GetNsfwTags() []string {
if x != nil {
return x.NsfwTags
}
return nil
}
var File_store_instance_setting_proto protoreflect.FileDescriptor
const file_store_instance_setting_proto_rawDesc = "" +
"\n" +
"\x1cstore/instance_setting.proto\x12\vmemos.store\"\x94\x03\n" +
"\x0fInstanceSetting\x121\n" +
"\x03key\x18\x01 \x01(\x0e2\x1f.memos.store.InstanceSettingKeyR\x03key\x12H\n" +
"\rbasic_setting\x18\x02 \x01(\v2!.memos.store.InstanceBasicSettingH\x00R\fbasicSetting\x12N\n" +
"\x0fgeneral_setting\x18\x03 \x01(\v2#.memos.store.InstanceGeneralSettingH\x00R\x0egeneralSetting\x12N\n" +
"\x0fstorage_setting\x18\x04 \x01(\v2#.memos.store.InstanceStorageSettingH\x00R\x0estorageSetting\x12[\n" +
"\x14memo_related_setting\x18\x05 \x01(\v2'.memos.store.InstanceMemoRelatedSettingH\x00R\x12memoRelatedSettingB\a\n" +
"\x05value\"\\\n" +
"\x14InstanceBasicSetting\x12\x1d\n" +
"\n" +
"secret_key\x18\x01 \x01(\tR\tsecretKey\x12%\n" +
"\x0eschema_version\x18\x02 \x01(\tR\rschemaVersion\"\xd6\x03\n" +
"\x16InstanceGeneralSetting\x12<\n" +
"\x1adisallow_user_registration\x18\x02 \x01(\bR\x18disallowUserRegistration\x124\n" +
"\x16disallow_password_auth\x18\x03 \x01(\bR\x14disallowPasswordAuth\x12+\n" +
"\x11additional_script\x18\x04 \x01(\tR\x10additionalScript\x12)\n" +
"\x10additional_style\x18\x05 \x01(\tR\x0fadditionalStyle\x12I\n" +
"\x0ecustom_profile\x18\x06 \x01(\v2\".memos.store.InstanceCustomProfileR\rcustomProfile\x121\n" +
"\x15week_start_day_offset\x18\a \x01(\x05R\x12weekStartDayOffset\x128\n" +
"\x18disallow_change_username\x18\b \x01(\bR\x16disallowChangeUsername\x128\n" +
"\x18disallow_change_nickname\x18\t \x01(\bR\x16disallowChangeNickname\"j\n" +
"\x15InstanceCustomProfile\x12\x14\n" +
"\x05title\x18\x01 \x01(\tR\x05title\x12 \n" +
"\vdescription\x18\x02 \x01(\tR\vdescription\x12\x19\n" +
"\blogo_url\x18\x03 \x01(\tR\alogoUrl\"\xd3\x02\n" +
"\x16InstanceStorageSetting\x12R\n" +
"\fstorage_type\x18\x01 \x01(\x0e2/.memos.store.InstanceStorageSetting.StorageTypeR\vstorageType\x12+\n" +
"\x11filepath_template\x18\x02 \x01(\tR\x10filepathTemplate\x12/\n" +
"\x14upload_size_limit_mb\x18\x03 \x01(\x03R\x11uploadSizeLimitMb\x129\n" +
"\ts3_config\x18\x04 \x01(\v2\x1c.memos.store.StorageS3ConfigR\bs3Config\"L\n" +
"\vStorageType\x12\x1c\n" +
"\x18STORAGE_TYPE_UNSPECIFIED\x10\x00\x12\f\n" +
"\bDATABASE\x10\x01\x12\t\n" +
"\x05LOCAL\x10\x02\x12\x06\n" +
"\x02S3\x10\x03\"\xd3\x01\n" +
"\x0fStorageS3Config\x12\"\n" +
"\raccess_key_id\x18\x01 \x01(\tR\vaccessKeyId\x12*\n" +
"\x11access_key_secret\x18\x02 \x01(\tR\x0faccessKeySecret\x12\x1a\n" +
"\bendpoint\x18\x03 \x01(\tR\bendpoint\x12\x16\n" +
"\x06region\x18\x04 \x01(\tR\x06region\x12\x16\n" +
"\x06bucket\x18\x05 \x01(\tR\x06bucket\x12$\n" +
"\x0euse_path_style\x18\x06 \x01(\bR\fusePathStyle\"\xf2\x02\n" +
"\x1aInstanceMemoRelatedSetting\x12<\n" +
"\x1adisallow_public_visibility\x18\x01 \x01(\bR\x18disallowPublicVisibility\x127\n" +
"\x18display_with_update_time\x18\x02 \x01(\bR\x15displayWithUpdateTime\x120\n" +
"\x14content_length_limit\x18\x03 \x01(\x05R\x12contentLengthLimit\x127\n" +
"\x18enable_double_click_edit\x18\x04 \x01(\bR\x15enableDoubleClickEdit\x12\x1c\n" +
"\treactions\x18\a \x03(\tR\treactions\x127\n" +
"\x18enable_blur_nsfw_content\x18\t \x01(\bR\x15enableBlurNsfwContent\x12\x1b\n" +
"\tnsfw_tags\x18\n" +
" \x03(\tR\bnsfwTags*q\n" +
"\x12InstanceSettingKey\x12$\n" +
" INSTANCE_SETTING_KEY_UNSPECIFIED\x10\x00\x12\t\n" +
"\x05BASIC\x10\x01\x12\v\n" +
"\aGENERAL\x10\x02\x12\v\n" +
"\aSTORAGE\x10\x03\x12\x10\n" +
"\fMEMO_RELATED\x10\x04B\x9f\x01\n" +
"\x0fcom.memos.storeB\x14InstanceSettingProtoP\x01Z)github.com/usememos/memos/proto/gen/store\xa2\x02\x03MSX\xaa\x02\vMemos.Store\xca\x02\vMemos\\Store\xe2\x02\x17Memos\\Store\\GPBMetadata\xea\x02\fMemos::Storeb\x06proto3"
var (
file_store_instance_setting_proto_rawDescOnce sync.Once
file_store_instance_setting_proto_rawDescData []byte
)
func file_store_instance_setting_proto_rawDescGZIP() []byte {
file_store_instance_setting_proto_rawDescOnce.Do(func() {
file_store_instance_setting_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_store_instance_setting_proto_rawDesc), len(file_store_instance_setting_proto_rawDesc)))
})
return file_store_instance_setting_proto_rawDescData
}
var file_store_instance_setting_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_store_instance_setting_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_store_instance_setting_proto_goTypes = []any{
(InstanceSettingKey)(0), // 0: memos.store.InstanceSettingKey
(InstanceStorageSetting_StorageType)(0), // 1: memos.store.InstanceStorageSetting.StorageType
(*InstanceSetting)(nil), // 2: memos.store.InstanceSetting
(*InstanceBasicSetting)(nil), // 3: memos.store.InstanceBasicSetting
(*InstanceGeneralSetting)(nil), // 4: memos.store.InstanceGeneralSetting
(*InstanceCustomProfile)(nil), // 5: memos.store.InstanceCustomProfile
(*InstanceStorageSetting)(nil), // 6: memos.store.InstanceStorageSetting
(*StorageS3Config)(nil), // 7: memos.store.StorageS3Config
(*InstanceMemoRelatedSetting)(nil), // 8: memos.store.InstanceMemoRelatedSetting
}
var file_store_instance_setting_proto_depIdxs = []int32{
0, // 0: memos.store.InstanceSetting.key:type_name -> memos.store.InstanceSettingKey
3, // 1: memos.store.InstanceSetting.basic_setting:type_name -> memos.store.InstanceBasicSetting
4, // 2: memos.store.InstanceSetting.general_setting:type_name -> memos.store.InstanceGeneralSetting
6, // 3: memos.store.InstanceSetting.storage_setting:type_name -> memos.store.InstanceStorageSetting
8, // 4: memos.store.InstanceSetting.memo_related_setting:type_name -> memos.store.InstanceMemoRelatedSetting
5, // 5: memos.store.InstanceGeneralSetting.custom_profile:type_name -> memos.store.InstanceCustomProfile
1, // 6: memos.store.InstanceStorageSetting.storage_type:type_name -> memos.store.InstanceStorageSetting.StorageType
7, // 7: memos.store.InstanceStorageSetting.s3_config:type_name -> memos.store.StorageS3Config
8, // [8:8] is the sub-list for method output_type
8, // [8:8] is the sub-list for method input_type
8, // [8:8] is the sub-list for extension type_name
8, // [8:8] is the sub-list for extension extendee
0, // [0:8] is the sub-list for field type_name
}
func init() { file_store_instance_setting_proto_init() }
func file_store_instance_setting_proto_init() {
if File_store_instance_setting_proto != nil {
return
}
file_store_instance_setting_proto_msgTypes[0].OneofWrappers = []any{
(*InstanceSetting_BasicSetting)(nil),
(*InstanceSetting_GeneralSetting)(nil),
(*InstanceSetting_StorageSetting)(nil),
(*InstanceSetting_MemoRelatedSetting)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_store_instance_setting_proto_rawDesc), len(file_store_instance_setting_proto_rawDesc)),
NumEnums: 2,
NumMessages: 7,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_store_instance_setting_proto_goTypes,
DependencyIndexes: file_store_instance_setting_proto_depIdxs,
EnumInfos: file_store_instance_setting_proto_enumTypes,
MessageInfos: file_store_instance_setting_proto_msgTypes,
}.Build()
File_store_instance_setting_proto = out.File
file_store_instance_setting_proto_goTypes = nil
file_store_instance_setting_proto_depIdxs = nil
}

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.10 // protoc-gen-go v1.36.11
// protoc (unknown) // protoc (unknown)
// source: store/memo.proto // source: store/memo.proto
@ -88,10 +88,8 @@ type MemoPayload_Property struct {
HasTaskList bool `protobuf:"varint,2,opt,name=has_task_list,json=hasTaskList,proto3" json:"has_task_list,omitempty"` HasTaskList bool `protobuf:"varint,2,opt,name=has_task_list,json=hasTaskList,proto3" json:"has_task_list,omitempty"`
HasCode bool `protobuf:"varint,3,opt,name=has_code,json=hasCode,proto3" json:"has_code,omitempty"` HasCode bool `protobuf:"varint,3,opt,name=has_code,json=hasCode,proto3" json:"has_code,omitempty"`
HasIncompleteTasks bool `protobuf:"varint,4,opt,name=has_incomplete_tasks,json=hasIncompleteTasks,proto3" json:"has_incomplete_tasks,omitempty"` HasIncompleteTasks bool `protobuf:"varint,4,opt,name=has_incomplete_tasks,json=hasIncompleteTasks,proto3" json:"has_incomplete_tasks,omitempty"`
// The references of the memo. Should be a list of uuid. unknownFields protoimpl.UnknownFields
References []string `protobuf:"bytes,5,rep,name=references,proto3" json:"references,omitempty"` sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
} }
func (x *MemoPayload_Property) Reset() { func (x *MemoPayload_Property) Reset() {
@ -152,13 +150,6 @@ func (x *MemoPayload_Property) GetHasIncompleteTasks() bool {
return false return false
} }
func (x *MemoPayload_Property) GetReferences() []string {
if x != nil {
return x.References
}
return nil
}
type MemoPayload_Location struct { type MemoPayload_Location struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
Placeholder string `protobuf:"bytes,1,opt,name=placeholder,proto3" json:"placeholder,omitempty"` Placeholder string `protobuf:"bytes,1,opt,name=placeholder,proto3" json:"placeholder,omitempty"`
@ -223,19 +214,16 @@ var File_store_memo_proto protoreflect.FileDescriptor
const file_store_memo_proto_rawDesc = "" + const file_store_memo_proto_rawDesc = "" +
"\n" + "\n" +
"\x10store/memo.proto\x12\vmemos.store\"\xc0\x03\n" + "\x10store/memo.proto\x12\vmemos.store\"\xa0\x03\n" +
"\vMemoPayload\x12=\n" + "\vMemoPayload\x12=\n" +
"\bproperty\x18\x01 \x01(\v2!.memos.store.MemoPayload.PropertyR\bproperty\x12=\n" + "\bproperty\x18\x01 \x01(\v2!.memos.store.MemoPayload.PropertyR\bproperty\x12=\n" +
"\blocation\x18\x02 \x01(\v2!.memos.store.MemoPayload.LocationR\blocation\x12\x12\n" + "\blocation\x18\x02 \x01(\v2!.memos.store.MemoPayload.LocationR\blocation\x12\x12\n" +
"\x04tags\x18\x03 \x03(\tR\x04tags\x1a\xb6\x01\n" + "\x04tags\x18\x03 \x03(\tR\x04tags\x1a\x96\x01\n" +
"\bProperty\x12\x19\n" + "\bProperty\x12\x19\n" +
"\bhas_link\x18\x01 \x01(\bR\ahasLink\x12\"\n" + "\bhas_link\x18\x01 \x01(\bR\ahasLink\x12\"\n" +
"\rhas_task_list\x18\x02 \x01(\bR\vhasTaskList\x12\x19\n" + "\rhas_task_list\x18\x02 \x01(\bR\vhasTaskList\x12\x19\n" +
"\bhas_code\x18\x03 \x01(\bR\ahasCode\x120\n" + "\bhas_code\x18\x03 \x01(\bR\ahasCode\x120\n" +
"\x14has_incomplete_tasks\x18\x04 \x01(\bR\x12hasIncompleteTasks\x12\x1e\n" + "\x14has_incomplete_tasks\x18\x04 \x01(\bR\x12hasIncompleteTasks\x1af\n" +
"\n" +
"references\x18\x05 \x03(\tR\n" +
"references\x1af\n" +
"\bLocation\x12 \n" + "\bLocation\x12 \n" +
"\vplaceholder\x18\x01 \x01(\tR\vplaceholder\x12\x1a\n" + "\vplaceholder\x18\x01 \x01(\tR\vplaceholder\x12\x1a\n" +
"\blatitude\x18\x02 \x01(\x01R\blatitude\x12\x1c\n" + "\blatitude\x18\x02 \x01(\x01R\blatitude\x12\x1c\n" +

View File

@ -1,6 +1,6 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// versions: // versions:
// protoc-gen-go v1.36.10 // protoc-gen-go v1.36.11
// protoc (unknown) // protoc (unknown)
// source: store/user_setting.proto // source: store/user_setting.proto

View File

@ -1,914 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.10
// protoc (unknown)
// source: store/workspace_setting.proto
package store
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type WorkspaceSettingKey int32
const (
WorkspaceSettingKey_WORKSPACE_SETTING_KEY_UNSPECIFIED WorkspaceSettingKey = 0
// BASIC is the key for basic settings.
WorkspaceSettingKey_BASIC WorkspaceSettingKey = 1
// GENERAL is the key for general settings.
WorkspaceSettingKey_GENERAL WorkspaceSettingKey = 2
// STORAGE is the key for storage settings.
WorkspaceSettingKey_STORAGE WorkspaceSettingKey = 3
// MEMO_RELATED is the key for memo related settings.
WorkspaceSettingKey_MEMO_RELATED WorkspaceSettingKey = 4
)
// Enum value maps for WorkspaceSettingKey.
var (
WorkspaceSettingKey_name = map[int32]string{
0: "WORKSPACE_SETTING_KEY_UNSPECIFIED",
1: "BASIC",
2: "GENERAL",
3: "STORAGE",
4: "MEMO_RELATED",
}
WorkspaceSettingKey_value = map[string]int32{
"WORKSPACE_SETTING_KEY_UNSPECIFIED": 0,
"BASIC": 1,
"GENERAL": 2,
"STORAGE": 3,
"MEMO_RELATED": 4,
}
)
func (x WorkspaceSettingKey) Enum() *WorkspaceSettingKey {
p := new(WorkspaceSettingKey)
*p = x
return p
}
func (x WorkspaceSettingKey) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (WorkspaceSettingKey) Descriptor() protoreflect.EnumDescriptor {
return file_store_workspace_setting_proto_enumTypes[0].Descriptor()
}
func (WorkspaceSettingKey) Type() protoreflect.EnumType {
return &file_store_workspace_setting_proto_enumTypes[0]
}
func (x WorkspaceSettingKey) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use WorkspaceSettingKey.Descriptor instead.
func (WorkspaceSettingKey) EnumDescriptor() ([]byte, []int) {
return file_store_workspace_setting_proto_rawDescGZIP(), []int{0}
}
type WorkspaceStorageSetting_StorageType int32
const (
WorkspaceStorageSetting_STORAGE_TYPE_UNSPECIFIED WorkspaceStorageSetting_StorageType = 0
// STORAGE_TYPE_DATABASE is the database storage type.
WorkspaceStorageSetting_DATABASE WorkspaceStorageSetting_StorageType = 1
// STORAGE_TYPE_LOCAL is the local storage type.
WorkspaceStorageSetting_LOCAL WorkspaceStorageSetting_StorageType = 2
// STORAGE_TYPE_S3 is the S3 storage type.
WorkspaceStorageSetting_S3 WorkspaceStorageSetting_StorageType = 3
)
// Enum value maps for WorkspaceStorageSetting_StorageType.
var (
WorkspaceStorageSetting_StorageType_name = map[int32]string{
0: "STORAGE_TYPE_UNSPECIFIED",
1: "DATABASE",
2: "LOCAL",
3: "S3",
}
WorkspaceStorageSetting_StorageType_value = map[string]int32{
"STORAGE_TYPE_UNSPECIFIED": 0,
"DATABASE": 1,
"LOCAL": 2,
"S3": 3,
}
)
func (x WorkspaceStorageSetting_StorageType) Enum() *WorkspaceStorageSetting_StorageType {
p := new(WorkspaceStorageSetting_StorageType)
*p = x
return p
}
func (x WorkspaceStorageSetting_StorageType) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (WorkspaceStorageSetting_StorageType) Descriptor() protoreflect.EnumDescriptor {
return file_store_workspace_setting_proto_enumTypes[1].Descriptor()
}
func (WorkspaceStorageSetting_StorageType) Type() protoreflect.EnumType {
return &file_store_workspace_setting_proto_enumTypes[1]
}
func (x WorkspaceStorageSetting_StorageType) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use WorkspaceStorageSetting_StorageType.Descriptor instead.
func (WorkspaceStorageSetting_StorageType) EnumDescriptor() ([]byte, []int) {
return file_store_workspace_setting_proto_rawDescGZIP(), []int{4, 0}
}
type WorkspaceSetting struct {
state protoimpl.MessageState `protogen:"open.v1"`
Key WorkspaceSettingKey `protobuf:"varint,1,opt,name=key,proto3,enum=memos.store.WorkspaceSettingKey" json:"key,omitempty"`
// Types that are valid to be assigned to Value:
//
// *WorkspaceSetting_BasicSetting
// *WorkspaceSetting_GeneralSetting
// *WorkspaceSetting_StorageSetting
// *WorkspaceSetting_MemoRelatedSetting
Value isWorkspaceSetting_Value `protobuf_oneof:"value"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *WorkspaceSetting) Reset() {
*x = WorkspaceSetting{}
mi := &file_store_workspace_setting_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *WorkspaceSetting) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*WorkspaceSetting) ProtoMessage() {}
func (x *WorkspaceSetting) ProtoReflect() protoreflect.Message {
mi := &file_store_workspace_setting_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use WorkspaceSetting.ProtoReflect.Descriptor instead.
func (*WorkspaceSetting) Descriptor() ([]byte, []int) {
return file_store_workspace_setting_proto_rawDescGZIP(), []int{0}
}
func (x *WorkspaceSetting) GetKey() WorkspaceSettingKey {
if x != nil {
return x.Key
}
return WorkspaceSettingKey_WORKSPACE_SETTING_KEY_UNSPECIFIED
}
func (x *WorkspaceSetting) GetValue() isWorkspaceSetting_Value {
if x != nil {
return x.Value
}
return nil
}
func (x *WorkspaceSetting) GetBasicSetting() *WorkspaceBasicSetting {
if x != nil {
if x, ok := x.Value.(*WorkspaceSetting_BasicSetting); ok {
return x.BasicSetting
}
}
return nil
}
func (x *WorkspaceSetting) GetGeneralSetting() *WorkspaceGeneralSetting {
if x != nil {
if x, ok := x.Value.(*WorkspaceSetting_GeneralSetting); ok {
return x.GeneralSetting
}
}
return nil
}
func (x *WorkspaceSetting) GetStorageSetting() *WorkspaceStorageSetting {
if x != nil {
if x, ok := x.Value.(*WorkspaceSetting_StorageSetting); ok {
return x.StorageSetting
}
}
return nil
}
func (x *WorkspaceSetting) GetMemoRelatedSetting() *WorkspaceMemoRelatedSetting {
if x != nil {
if x, ok := x.Value.(*WorkspaceSetting_MemoRelatedSetting); ok {
return x.MemoRelatedSetting
}
}
return nil
}
type isWorkspaceSetting_Value interface {
isWorkspaceSetting_Value()
}
type WorkspaceSetting_BasicSetting struct {
BasicSetting *WorkspaceBasicSetting `protobuf:"bytes,2,opt,name=basic_setting,json=basicSetting,proto3,oneof"`
}
type WorkspaceSetting_GeneralSetting struct {
GeneralSetting *WorkspaceGeneralSetting `protobuf:"bytes,3,opt,name=general_setting,json=generalSetting,proto3,oneof"`
}
type WorkspaceSetting_StorageSetting struct {
StorageSetting *WorkspaceStorageSetting `protobuf:"bytes,4,opt,name=storage_setting,json=storageSetting,proto3,oneof"`
}
type WorkspaceSetting_MemoRelatedSetting struct {
MemoRelatedSetting *WorkspaceMemoRelatedSetting `protobuf:"bytes,5,opt,name=memo_related_setting,json=memoRelatedSetting,proto3,oneof"`
}
func (*WorkspaceSetting_BasicSetting) isWorkspaceSetting_Value() {}
func (*WorkspaceSetting_GeneralSetting) isWorkspaceSetting_Value() {}
func (*WorkspaceSetting_StorageSetting) isWorkspaceSetting_Value() {}
func (*WorkspaceSetting_MemoRelatedSetting) isWorkspaceSetting_Value() {}
type WorkspaceBasicSetting struct {
state protoimpl.MessageState `protogen:"open.v1"`
// The secret key for workspace. Mainly used for session management.
SecretKey string `protobuf:"bytes,1,opt,name=secret_key,json=secretKey,proto3" json:"secret_key,omitempty"`
// The current schema version of database.
SchemaVersion string `protobuf:"bytes,2,opt,name=schema_version,json=schemaVersion,proto3" json:"schema_version,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *WorkspaceBasicSetting) Reset() {
*x = WorkspaceBasicSetting{}
mi := &file_store_workspace_setting_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *WorkspaceBasicSetting) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*WorkspaceBasicSetting) ProtoMessage() {}
func (x *WorkspaceBasicSetting) ProtoReflect() protoreflect.Message {
mi := &file_store_workspace_setting_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use WorkspaceBasicSetting.ProtoReflect.Descriptor instead.
func (*WorkspaceBasicSetting) Descriptor() ([]byte, []int) {
return file_store_workspace_setting_proto_rawDescGZIP(), []int{1}
}
func (x *WorkspaceBasicSetting) GetSecretKey() string {
if x != nil {
return x.SecretKey
}
return ""
}
func (x *WorkspaceBasicSetting) GetSchemaVersion() string {
if x != nil {
return x.SchemaVersion
}
return ""
}
type WorkspaceGeneralSetting struct {
state protoimpl.MessageState `protogen:"open.v1"`
// theme is the name of the selected theme.
// This references a CSS file in the web/public/themes/ directory.
Theme string `protobuf:"bytes,1,opt,name=theme,proto3" json:"theme,omitempty"`
// disallow_user_registration disallows user registration.
DisallowUserRegistration bool `protobuf:"varint,2,opt,name=disallow_user_registration,json=disallowUserRegistration,proto3" json:"disallow_user_registration,omitempty"`
// disallow_password_auth disallows password authentication.
DisallowPasswordAuth bool `protobuf:"varint,3,opt,name=disallow_password_auth,json=disallowPasswordAuth,proto3" json:"disallow_password_auth,omitempty"`
// additional_script is the additional script.
AdditionalScript string `protobuf:"bytes,4,opt,name=additional_script,json=additionalScript,proto3" json:"additional_script,omitempty"`
// additional_style is the additional style.
AdditionalStyle string `protobuf:"bytes,5,opt,name=additional_style,json=additionalStyle,proto3" json:"additional_style,omitempty"`
// custom_profile is the custom profile.
CustomProfile *WorkspaceCustomProfile `protobuf:"bytes,6,opt,name=custom_profile,json=customProfile,proto3" json:"custom_profile,omitempty"`
// week_start_day_offset is the week start day offset from Sunday.
// 0: Sunday, 1: Monday, 2: Tuesday, 3: Wednesday, 4: Thursday, 5: Friday, 6: Saturday
// Default is Sunday.
WeekStartDayOffset int32 `protobuf:"varint,7,opt,name=week_start_day_offset,json=weekStartDayOffset,proto3" json:"week_start_day_offset,omitempty"`
// disallow_change_username disallows changing username.
DisallowChangeUsername bool `protobuf:"varint,8,opt,name=disallow_change_username,json=disallowChangeUsername,proto3" json:"disallow_change_username,omitempty"`
// disallow_change_nickname disallows changing nickname.
DisallowChangeNickname bool `protobuf:"varint,9,opt,name=disallow_change_nickname,json=disallowChangeNickname,proto3" json:"disallow_change_nickname,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *WorkspaceGeneralSetting) Reset() {
*x = WorkspaceGeneralSetting{}
mi := &file_store_workspace_setting_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *WorkspaceGeneralSetting) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*WorkspaceGeneralSetting) ProtoMessage() {}
func (x *WorkspaceGeneralSetting) ProtoReflect() protoreflect.Message {
mi := &file_store_workspace_setting_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use WorkspaceGeneralSetting.ProtoReflect.Descriptor instead.
func (*WorkspaceGeneralSetting) Descriptor() ([]byte, []int) {
return file_store_workspace_setting_proto_rawDescGZIP(), []int{2}
}
func (x *WorkspaceGeneralSetting) GetTheme() string {
if x != nil {
return x.Theme
}
return ""
}
func (x *WorkspaceGeneralSetting) GetDisallowUserRegistration() bool {
if x != nil {
return x.DisallowUserRegistration
}
return false
}
func (x *WorkspaceGeneralSetting) GetDisallowPasswordAuth() bool {
if x != nil {
return x.DisallowPasswordAuth
}
return false
}
func (x *WorkspaceGeneralSetting) GetAdditionalScript() string {
if x != nil {
return x.AdditionalScript
}
return ""
}
func (x *WorkspaceGeneralSetting) GetAdditionalStyle() string {
if x != nil {
return x.AdditionalStyle
}
return ""
}
func (x *WorkspaceGeneralSetting) GetCustomProfile() *WorkspaceCustomProfile {
if x != nil {
return x.CustomProfile
}
return nil
}
func (x *WorkspaceGeneralSetting) GetWeekStartDayOffset() int32 {
if x != nil {
return x.WeekStartDayOffset
}
return 0
}
func (x *WorkspaceGeneralSetting) GetDisallowChangeUsername() bool {
if x != nil {
return x.DisallowChangeUsername
}
return false
}
func (x *WorkspaceGeneralSetting) GetDisallowChangeNickname() bool {
if x != nil {
return x.DisallowChangeNickname
}
return false
}
type WorkspaceCustomProfile struct {
state protoimpl.MessageState `protogen:"open.v1"`
Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"`
Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
LogoUrl string `protobuf:"bytes,3,opt,name=logo_url,json=logoUrl,proto3" json:"logo_url,omitempty"`
Locale string `protobuf:"bytes,4,opt,name=locale,proto3" json:"locale,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *WorkspaceCustomProfile) Reset() {
*x = WorkspaceCustomProfile{}
mi := &file_store_workspace_setting_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *WorkspaceCustomProfile) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*WorkspaceCustomProfile) ProtoMessage() {}
func (x *WorkspaceCustomProfile) ProtoReflect() protoreflect.Message {
mi := &file_store_workspace_setting_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use WorkspaceCustomProfile.ProtoReflect.Descriptor instead.
func (*WorkspaceCustomProfile) Descriptor() ([]byte, []int) {
return file_store_workspace_setting_proto_rawDescGZIP(), []int{3}
}
func (x *WorkspaceCustomProfile) GetTitle() string {
if x != nil {
return x.Title
}
return ""
}
func (x *WorkspaceCustomProfile) GetDescription() string {
if x != nil {
return x.Description
}
return ""
}
func (x *WorkspaceCustomProfile) GetLogoUrl() string {
if x != nil {
return x.LogoUrl
}
return ""
}
func (x *WorkspaceCustomProfile) GetLocale() string {
if x != nil {
return x.Locale
}
return ""
}
type WorkspaceStorageSetting struct {
state protoimpl.MessageState `protogen:"open.v1"`
// storage_type is the storage type.
StorageType WorkspaceStorageSetting_StorageType `protobuf:"varint,1,opt,name=storage_type,json=storageType,proto3,enum=memos.store.WorkspaceStorageSetting_StorageType" json:"storage_type,omitempty"`
// The template of file path.
// e.g. assets/{timestamp}_{filename}
FilepathTemplate string `protobuf:"bytes,2,opt,name=filepath_template,json=filepathTemplate,proto3" json:"filepath_template,omitempty"`
// The max upload size in megabytes.
UploadSizeLimitMb int64 `protobuf:"varint,3,opt,name=upload_size_limit_mb,json=uploadSizeLimitMb,proto3" json:"upload_size_limit_mb,omitempty"`
// The S3 config.
S3Config *StorageS3Config `protobuf:"bytes,4,opt,name=s3_config,json=s3Config,proto3" json:"s3_config,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *WorkspaceStorageSetting) Reset() {
*x = WorkspaceStorageSetting{}
mi := &file_store_workspace_setting_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *WorkspaceStorageSetting) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*WorkspaceStorageSetting) ProtoMessage() {}
func (x *WorkspaceStorageSetting) ProtoReflect() protoreflect.Message {
mi := &file_store_workspace_setting_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use WorkspaceStorageSetting.ProtoReflect.Descriptor instead.
func (*WorkspaceStorageSetting) Descriptor() ([]byte, []int) {
return file_store_workspace_setting_proto_rawDescGZIP(), []int{4}
}
func (x *WorkspaceStorageSetting) GetStorageType() WorkspaceStorageSetting_StorageType {
if x != nil {
return x.StorageType
}
return WorkspaceStorageSetting_STORAGE_TYPE_UNSPECIFIED
}
func (x *WorkspaceStorageSetting) GetFilepathTemplate() string {
if x != nil {
return x.FilepathTemplate
}
return ""
}
func (x *WorkspaceStorageSetting) GetUploadSizeLimitMb() int64 {
if x != nil {
return x.UploadSizeLimitMb
}
return 0
}
func (x *WorkspaceStorageSetting) GetS3Config() *StorageS3Config {
if x != nil {
return x.S3Config
}
return nil
}
// Reference: https://developers.cloudflare.com/r2/examples/aws/aws-sdk-go/
type StorageS3Config struct {
state protoimpl.MessageState `protogen:"open.v1"`
AccessKeyId string `protobuf:"bytes,1,opt,name=access_key_id,json=accessKeyId,proto3" json:"access_key_id,omitempty"`
AccessKeySecret string `protobuf:"bytes,2,opt,name=access_key_secret,json=accessKeySecret,proto3" json:"access_key_secret,omitempty"`
Endpoint string `protobuf:"bytes,3,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
Region string `protobuf:"bytes,4,opt,name=region,proto3" json:"region,omitempty"`
Bucket string `protobuf:"bytes,5,opt,name=bucket,proto3" json:"bucket,omitempty"`
UsePathStyle bool `protobuf:"varint,6,opt,name=use_path_style,json=usePathStyle,proto3" json:"use_path_style,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *StorageS3Config) Reset() {
*x = StorageS3Config{}
mi := &file_store_workspace_setting_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *StorageS3Config) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*StorageS3Config) ProtoMessage() {}
func (x *StorageS3Config) ProtoReflect() protoreflect.Message {
mi := &file_store_workspace_setting_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use StorageS3Config.ProtoReflect.Descriptor instead.
func (*StorageS3Config) Descriptor() ([]byte, []int) {
return file_store_workspace_setting_proto_rawDescGZIP(), []int{5}
}
func (x *StorageS3Config) GetAccessKeyId() string {
if x != nil {
return x.AccessKeyId
}
return ""
}
func (x *StorageS3Config) GetAccessKeySecret() string {
if x != nil {
return x.AccessKeySecret
}
return ""
}
func (x *StorageS3Config) GetEndpoint() string {
if x != nil {
return x.Endpoint
}
return ""
}
func (x *StorageS3Config) GetRegion() string {
if x != nil {
return x.Region
}
return ""
}
func (x *StorageS3Config) GetBucket() string {
if x != nil {
return x.Bucket
}
return ""
}
func (x *StorageS3Config) GetUsePathStyle() bool {
if x != nil {
return x.UsePathStyle
}
return false
}
type WorkspaceMemoRelatedSetting struct {
state protoimpl.MessageState `protogen:"open.v1"`
// disallow_public_visibility disallows set memo as public visibility.
DisallowPublicVisibility bool `protobuf:"varint,1,opt,name=disallow_public_visibility,json=disallowPublicVisibility,proto3" json:"disallow_public_visibility,omitempty"`
// display_with_update_time orders and displays memo with update time.
DisplayWithUpdateTime bool `protobuf:"varint,2,opt,name=display_with_update_time,json=displayWithUpdateTime,proto3" json:"display_with_update_time,omitempty"`
// content_length_limit is the limit of content length. Unit is byte.
ContentLengthLimit int32 `protobuf:"varint,3,opt,name=content_length_limit,json=contentLengthLimit,proto3" json:"content_length_limit,omitempty"`
// enable_double_click_edit enables editing on double click.
EnableDoubleClickEdit bool `protobuf:"varint,4,opt,name=enable_double_click_edit,json=enableDoubleClickEdit,proto3" json:"enable_double_click_edit,omitempty"`
// enable_link_preview enables links preview.
EnableLinkPreview bool `protobuf:"varint,5,opt,name=enable_link_preview,json=enableLinkPreview,proto3" json:"enable_link_preview,omitempty"`
// reactions is the list of reactions.
Reactions []string `protobuf:"bytes,7,rep,name=reactions,proto3" json:"reactions,omitempty"`
// disable markdown shortcuts
DisableMarkdownShortcuts bool `protobuf:"varint,8,opt,name=disable_markdown_shortcuts,json=disableMarkdownShortcuts,proto3" json:"disable_markdown_shortcuts,omitempty"`
// enable_blur_nsfw_content enables blurring of content marked as not safe for work (NSFW).
EnableBlurNsfwContent bool `protobuf:"varint,9,opt,name=enable_blur_nsfw_content,json=enableBlurNsfwContent,proto3" json:"enable_blur_nsfw_content,omitempty"`
// nsfw_tags is the list of tags that mark content as NSFW for blurring.
NsfwTags []string `protobuf:"bytes,10,rep,name=nsfw_tags,json=nsfwTags,proto3" json:"nsfw_tags,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *WorkspaceMemoRelatedSetting) Reset() {
*x = WorkspaceMemoRelatedSetting{}
mi := &file_store_workspace_setting_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *WorkspaceMemoRelatedSetting) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*WorkspaceMemoRelatedSetting) ProtoMessage() {}
func (x *WorkspaceMemoRelatedSetting) ProtoReflect() protoreflect.Message {
mi := &file_store_workspace_setting_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use WorkspaceMemoRelatedSetting.ProtoReflect.Descriptor instead.
func (*WorkspaceMemoRelatedSetting) Descriptor() ([]byte, []int) {
return file_store_workspace_setting_proto_rawDescGZIP(), []int{6}
}
func (x *WorkspaceMemoRelatedSetting) GetDisallowPublicVisibility() bool {
if x != nil {
return x.DisallowPublicVisibility
}
return false
}
func (x *WorkspaceMemoRelatedSetting) GetDisplayWithUpdateTime() bool {
if x != nil {
return x.DisplayWithUpdateTime
}
return false
}
func (x *WorkspaceMemoRelatedSetting) GetContentLengthLimit() int32 {
if x != nil {
return x.ContentLengthLimit
}
return 0
}
func (x *WorkspaceMemoRelatedSetting) GetEnableDoubleClickEdit() bool {
if x != nil {
return x.EnableDoubleClickEdit
}
return false
}
func (x *WorkspaceMemoRelatedSetting) GetEnableLinkPreview() bool {
if x != nil {
return x.EnableLinkPreview
}
return false
}
func (x *WorkspaceMemoRelatedSetting) GetReactions() []string {
if x != nil {
return x.Reactions
}
return nil
}
func (x *WorkspaceMemoRelatedSetting) GetDisableMarkdownShortcuts() bool {
if x != nil {
return x.DisableMarkdownShortcuts
}
return false
}
func (x *WorkspaceMemoRelatedSetting) GetEnableBlurNsfwContent() bool {
if x != nil {
return x.EnableBlurNsfwContent
}
return false
}
func (x *WorkspaceMemoRelatedSetting) GetNsfwTags() []string {
if x != nil {
return x.NsfwTags
}
return nil
}
var File_store_workspace_setting_proto protoreflect.FileDescriptor
const file_store_workspace_setting_proto_rawDesc = "" +
"\n" +
"\x1dstore/workspace_setting.proto\x12\vmemos.store\"\x9a\x03\n" +
"\x10WorkspaceSetting\x122\n" +
"\x03key\x18\x01 \x01(\x0e2 .memos.store.WorkspaceSettingKeyR\x03key\x12I\n" +
"\rbasic_setting\x18\x02 \x01(\v2\".memos.store.WorkspaceBasicSettingH\x00R\fbasicSetting\x12O\n" +
"\x0fgeneral_setting\x18\x03 \x01(\v2$.memos.store.WorkspaceGeneralSettingH\x00R\x0egeneralSetting\x12O\n" +
"\x0fstorage_setting\x18\x04 \x01(\v2$.memos.store.WorkspaceStorageSettingH\x00R\x0estorageSetting\x12\\\n" +
"\x14memo_related_setting\x18\x05 \x01(\v2(.memos.store.WorkspaceMemoRelatedSettingH\x00R\x12memoRelatedSettingB\a\n" +
"\x05value\"]\n" +
"\x15WorkspaceBasicSetting\x12\x1d\n" +
"\n" +
"secret_key\x18\x01 \x01(\tR\tsecretKey\x12%\n" +
"\x0eschema_version\x18\x02 \x01(\tR\rschemaVersion\"\xee\x03\n" +
"\x17WorkspaceGeneralSetting\x12\x14\n" +
"\x05theme\x18\x01 \x01(\tR\x05theme\x12<\n" +
"\x1adisallow_user_registration\x18\x02 \x01(\bR\x18disallowUserRegistration\x124\n" +
"\x16disallow_password_auth\x18\x03 \x01(\bR\x14disallowPasswordAuth\x12+\n" +
"\x11additional_script\x18\x04 \x01(\tR\x10additionalScript\x12)\n" +
"\x10additional_style\x18\x05 \x01(\tR\x0fadditionalStyle\x12J\n" +
"\x0ecustom_profile\x18\x06 \x01(\v2#.memos.store.WorkspaceCustomProfileR\rcustomProfile\x121\n" +
"\x15week_start_day_offset\x18\a \x01(\x05R\x12weekStartDayOffset\x128\n" +
"\x18disallow_change_username\x18\b \x01(\bR\x16disallowChangeUsername\x128\n" +
"\x18disallow_change_nickname\x18\t \x01(\bR\x16disallowChangeNickname\"\x83\x01\n" +
"\x16WorkspaceCustomProfile\x12\x14\n" +
"\x05title\x18\x01 \x01(\tR\x05title\x12 \n" +
"\vdescription\x18\x02 \x01(\tR\vdescription\x12\x19\n" +
"\blogo_url\x18\x03 \x01(\tR\alogoUrl\x12\x16\n" +
"\x06locale\x18\x04 \x01(\tR\x06locale\"\xd5\x02\n" +
"\x17WorkspaceStorageSetting\x12S\n" +
"\fstorage_type\x18\x01 \x01(\x0e20.memos.store.WorkspaceStorageSetting.StorageTypeR\vstorageType\x12+\n" +
"\x11filepath_template\x18\x02 \x01(\tR\x10filepathTemplate\x12/\n" +
"\x14upload_size_limit_mb\x18\x03 \x01(\x03R\x11uploadSizeLimitMb\x129\n" +
"\ts3_config\x18\x04 \x01(\v2\x1c.memos.store.StorageS3ConfigR\bs3Config\"L\n" +
"\vStorageType\x12\x1c\n" +
"\x18STORAGE_TYPE_UNSPECIFIED\x10\x00\x12\f\n" +
"\bDATABASE\x10\x01\x12\t\n" +
"\x05LOCAL\x10\x02\x12\x06\n" +
"\x02S3\x10\x03\"\xd3\x01\n" +
"\x0fStorageS3Config\x12\"\n" +
"\raccess_key_id\x18\x01 \x01(\tR\vaccessKeyId\x12*\n" +
"\x11access_key_secret\x18\x02 \x01(\tR\x0faccessKeySecret\x12\x1a\n" +
"\bendpoint\x18\x03 \x01(\tR\bendpoint\x12\x16\n" +
"\x06region\x18\x04 \x01(\tR\x06region\x12\x16\n" +
"\x06bucket\x18\x05 \x01(\tR\x06bucket\x12$\n" +
"\x0euse_path_style\x18\x06 \x01(\bR\fusePathStyle\"\xe1\x03\n" +
"\x1bWorkspaceMemoRelatedSetting\x12<\n" +
"\x1adisallow_public_visibility\x18\x01 \x01(\bR\x18disallowPublicVisibility\x127\n" +
"\x18display_with_update_time\x18\x02 \x01(\bR\x15displayWithUpdateTime\x120\n" +
"\x14content_length_limit\x18\x03 \x01(\x05R\x12contentLengthLimit\x127\n" +
"\x18enable_double_click_edit\x18\x04 \x01(\bR\x15enableDoubleClickEdit\x12.\n" +
"\x13enable_link_preview\x18\x05 \x01(\bR\x11enableLinkPreview\x12\x1c\n" +
"\treactions\x18\a \x03(\tR\treactions\x12<\n" +
"\x1adisable_markdown_shortcuts\x18\b \x01(\bR\x18disableMarkdownShortcuts\x127\n" +
"\x18enable_blur_nsfw_content\x18\t \x01(\bR\x15enableBlurNsfwContent\x12\x1b\n" +
"\tnsfw_tags\x18\n" +
" \x03(\tR\bnsfwTags*s\n" +
"\x13WorkspaceSettingKey\x12%\n" +
"!WORKSPACE_SETTING_KEY_UNSPECIFIED\x10\x00\x12\t\n" +
"\x05BASIC\x10\x01\x12\v\n" +
"\aGENERAL\x10\x02\x12\v\n" +
"\aSTORAGE\x10\x03\x12\x10\n" +
"\fMEMO_RELATED\x10\x04B\xa0\x01\n" +
"\x0fcom.memos.storeB\x15WorkspaceSettingProtoP\x01Z)github.com/usememos/memos/proto/gen/store\xa2\x02\x03MSX\xaa\x02\vMemos.Store\xca\x02\vMemos\\Store\xe2\x02\x17Memos\\Store\\GPBMetadata\xea\x02\fMemos::Storeb\x06proto3"
var (
file_store_workspace_setting_proto_rawDescOnce sync.Once
file_store_workspace_setting_proto_rawDescData []byte
)
func file_store_workspace_setting_proto_rawDescGZIP() []byte {
file_store_workspace_setting_proto_rawDescOnce.Do(func() {
file_store_workspace_setting_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_store_workspace_setting_proto_rawDesc), len(file_store_workspace_setting_proto_rawDesc)))
})
return file_store_workspace_setting_proto_rawDescData
}
var file_store_workspace_setting_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_store_workspace_setting_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_store_workspace_setting_proto_goTypes = []any{
(WorkspaceSettingKey)(0), // 0: memos.store.WorkspaceSettingKey
(WorkspaceStorageSetting_StorageType)(0), // 1: memos.store.WorkspaceStorageSetting.StorageType
(*WorkspaceSetting)(nil), // 2: memos.store.WorkspaceSetting
(*WorkspaceBasicSetting)(nil), // 3: memos.store.WorkspaceBasicSetting
(*WorkspaceGeneralSetting)(nil), // 4: memos.store.WorkspaceGeneralSetting
(*WorkspaceCustomProfile)(nil), // 5: memos.store.WorkspaceCustomProfile
(*WorkspaceStorageSetting)(nil), // 6: memos.store.WorkspaceStorageSetting
(*StorageS3Config)(nil), // 7: memos.store.StorageS3Config
(*WorkspaceMemoRelatedSetting)(nil), // 8: memos.store.WorkspaceMemoRelatedSetting
}
var file_store_workspace_setting_proto_depIdxs = []int32{
0, // 0: memos.store.WorkspaceSetting.key:type_name -> memos.store.WorkspaceSettingKey
3, // 1: memos.store.WorkspaceSetting.basic_setting:type_name -> memos.store.WorkspaceBasicSetting
4, // 2: memos.store.WorkspaceSetting.general_setting:type_name -> memos.store.WorkspaceGeneralSetting
6, // 3: memos.store.WorkspaceSetting.storage_setting:type_name -> memos.store.WorkspaceStorageSetting
8, // 4: memos.store.WorkspaceSetting.memo_related_setting:type_name -> memos.store.WorkspaceMemoRelatedSetting
5, // 5: memos.store.WorkspaceGeneralSetting.custom_profile:type_name -> memos.store.WorkspaceCustomProfile
1, // 6: memos.store.WorkspaceStorageSetting.storage_type:type_name -> memos.store.WorkspaceStorageSetting.StorageType
7, // 7: memos.store.WorkspaceStorageSetting.s3_config:type_name -> memos.store.StorageS3Config
8, // [8:8] is the sub-list for method output_type
8, // [8:8] is the sub-list for method input_type
8, // [8:8] is the sub-list for extension type_name
8, // [8:8] is the sub-list for extension extendee
0, // [0:8] is the sub-list for field type_name
}
func init() { file_store_workspace_setting_proto_init() }
func file_store_workspace_setting_proto_init() {
if File_store_workspace_setting_proto != nil {
return
}
file_store_workspace_setting_proto_msgTypes[0].OneofWrappers = []any{
(*WorkspaceSetting_BasicSetting)(nil),
(*WorkspaceSetting_GeneralSetting)(nil),
(*WorkspaceSetting_StorageSetting)(nil),
(*WorkspaceSetting_MemoRelatedSetting)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_store_workspace_setting_proto_rawDesc), len(file_store_workspace_setting_proto_rawDesc)),
NumEnums: 2,
NumMessages: 7,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_store_workspace_setting_proto_goTypes,
DependencyIndexes: file_store_workspace_setting_proto_depIdxs,
EnumInfos: file_store_workspace_setting_proto_enumTypes,
MessageInfos: file_store_workspace_setting_proto_msgTypes,
}.Build()
File_store_workspace_setting_proto = out.File
file_store_workspace_setting_proto_goTypes = nil
file_store_workspace_setting_proto_depIdxs = nil
}

View File

@ -3,7 +3,7 @@ syntax = "proto3";
package memos.store; package memos.store;
import "google/protobuf/timestamp.proto"; import "google/protobuf/timestamp.proto";
import "store/workspace_setting.proto"; import "store/instance_setting.proto";
option go_package = "gen/store"; option go_package = "gen/store";

View File

@ -5,11 +5,14 @@ package memos.store;
option go_package = "gen/store"; option go_package = "gen/store";
message InboxMessage { message InboxMessage {
// The type of the inbox message.
Type type = 1;
// The system-generated unique ID of related activity.
optional int32 activity_id = 2;
enum Type { enum Type {
TYPE_UNSPECIFIED = 0; TYPE_UNSPECIFIED = 0;
// Memo comment notification.
MEMO_COMMENT = 1; MEMO_COMMENT = 1;
VERSION_UPDATE = 2;
} }
Type type = 1;
optional int32 activity_id = 2;
} }

View File

@ -4,8 +4,8 @@ package memos.store;
option go_package = "gen/store"; option go_package = "gen/store";
enum WorkspaceSettingKey { enum InstanceSettingKey {
WORKSPACE_SETTING_KEY_UNSPECIFIED = 0; INSTANCE_SETTING_KEY_UNSPECIFIED = 0;
// BASIC is the key for basic settings. // BASIC is the key for basic settings.
BASIC = 1; BASIC = 1;
// GENERAL is the key for general settings. // GENERAL is the key for general settings.
@ -16,27 +16,24 @@ enum WorkspaceSettingKey {
MEMO_RELATED = 4; MEMO_RELATED = 4;
} }
message WorkspaceSetting { message InstanceSetting {
WorkspaceSettingKey key = 1; InstanceSettingKey key = 1;
oneof value { oneof value {
WorkspaceBasicSetting basic_setting = 2; InstanceBasicSetting basic_setting = 2;
WorkspaceGeneralSetting general_setting = 3; InstanceGeneralSetting general_setting = 3;
WorkspaceStorageSetting storage_setting = 4; InstanceStorageSetting storage_setting = 4;
WorkspaceMemoRelatedSetting memo_related_setting = 5; InstanceMemoRelatedSetting memo_related_setting = 5;
} }
} }
message WorkspaceBasicSetting { message InstanceBasicSetting {
// The secret key for workspace. Mainly used for session management. // The secret key for instance. Mainly used for session management.
string secret_key = 1; string secret_key = 1;
// The current schema version of database. // The current schema version of database.
string schema_version = 2; string schema_version = 2;
} }
message WorkspaceGeneralSetting { message InstanceGeneralSetting {
// theme is the name of the selected theme.
// This references a CSS file in the web/public/themes/ directory.
string theme = 1;
// disallow_user_registration disallows user registration. // disallow_user_registration disallows user registration.
bool disallow_user_registration = 2; bool disallow_user_registration = 2;
// disallow_password_auth disallows password authentication. // disallow_password_auth disallows password authentication.
@ -46,7 +43,7 @@ message WorkspaceGeneralSetting {
// additional_style is the additional style. // additional_style is the additional style.
string additional_style = 5; string additional_style = 5;
// custom_profile is the custom profile. // custom_profile is the custom profile.
WorkspaceCustomProfile custom_profile = 6; InstanceCustomProfile custom_profile = 6;
// week_start_day_offset is the week start day offset from Sunday. // week_start_day_offset is the week start day offset from Sunday.
// 0: Sunday, 1: Monday, 2: Tuesday, 3: Wednesday, 4: Thursday, 5: Friday, 6: Saturday // 0: Sunday, 1: Monday, 2: Tuesday, 3: Wednesday, 4: Thursday, 5: Friday, 6: Saturday
// Default is Sunday. // Default is Sunday.
@ -57,14 +54,13 @@ message WorkspaceGeneralSetting {
bool disallow_change_nickname = 9; bool disallow_change_nickname = 9;
} }
message WorkspaceCustomProfile { message InstanceCustomProfile {
string title = 1; string title = 1;
string description = 2; string description = 2;
string logo_url = 3; string logo_url = 3;
string locale = 4;
} }
message WorkspaceStorageSetting { message InstanceStorageSetting {
enum StorageType { enum StorageType {
STORAGE_TYPE_UNSPECIFIED = 0; STORAGE_TYPE_UNSPECIFIED = 0;
// STORAGE_TYPE_DATABASE is the database storage type. // STORAGE_TYPE_DATABASE is the database storage type.
@ -95,7 +91,7 @@ message StorageS3Config {
bool use_path_style = 6; bool use_path_style = 6;
} }
message WorkspaceMemoRelatedSetting { message InstanceMemoRelatedSetting {
// disallow_public_visibility disallows set memo as public visibility. // disallow_public_visibility disallows set memo as public visibility.
bool disallow_public_visibility = 1; bool disallow_public_visibility = 1;
// display_with_update_time orders and displays memo with update time. // display_with_update_time orders and displays memo with update time.
@ -104,12 +100,8 @@ message WorkspaceMemoRelatedSetting {
int32 content_length_limit = 3; int32 content_length_limit = 3;
// enable_double_click_edit enables editing on double click. // enable_double_click_edit enables editing on double click.
bool enable_double_click_edit = 4; bool enable_double_click_edit = 4;
// enable_link_preview enables links preview.
bool enable_link_preview = 5;
// reactions is the list of reactions. // reactions is the list of reactions.
repeated string reactions = 7; repeated string reactions = 7;
// disable markdown shortcuts
bool disable_markdown_shortcuts = 8;
// enable_blur_nsfw_content enables blurring of content marked as not safe for work (NSFW). // enable_blur_nsfw_content enables blurring of content marked as not safe for work (NSFW).
bool enable_blur_nsfw_content = 9; bool enable_blur_nsfw_content = 9;
// nsfw_tags is the list of tags that mark content as NSFW for blurring. // nsfw_tags is the list of tags that mark content as NSFW for blurring.

View File

@ -17,8 +17,6 @@ message MemoPayload {
bool has_task_list = 2; bool has_task_list = 2;
bool has_code = 3; bool has_code = 3;
bool has_incomplete_tasks = 4; bool has_incomplete_tasks = 4;
// The references of the memo. Should be a list of uuid.
repeated string references = 5;
} }
message Location { message Location {

View File

@ -9,7 +9,6 @@ RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \ --mount=type=cache,target=/root/.cache/go-build \
go build -ldflags="-s -w" -o memos ./cmd/memos go build -ldflags="-s -w" -o memos ./cmd/memos
# Make workspace with above generated files.
FROM alpine:latest AS monolithic FROM alpine:latest AS monolithic
WORKDIR /usr/local/memos WORKDIR /usr/local/memos

View File

@ -0,0 +1,219 @@
package auth
import (
"context"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/pkg/errors"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/usememos/memos/internal/util"
storepb "github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/store"
)
// Authenticator provides shared authentication and authorization logic.
// Used by gRPC interceptor, Connect interceptor, and file server to ensure
// consistent authentication behavior across all API endpoints.
//
// Authentication methods:
// - Session cookie: Browser-based authentication with sliding expiration
// - JWT token: API token authentication for programmatic access
//
// This struct is safe for concurrent use.
type Authenticator struct {
store *store.Store
secret string
}
// NewAuthenticator creates a new Authenticator instance.
func NewAuthenticator(store *store.Store, secret string) *Authenticator {
return &Authenticator{
store: store,
secret: secret,
}
}
// AuthenticateBySession validates a session cookie and returns the authenticated user.
//
// Validation steps:
// 1. Use session ID to find the user and session details (single DB query)
// 2. Verify user exists and is not archived
// 3. Check session hasn't expired (sliding expiration: 14 days from last access)
//
// Returns the user if authentication succeeds, or an error describing the failure.
func (a *Authenticator) AuthenticateBySession(ctx context.Context, sessionID string) (*store.User, error) {
if sessionID == "" {
return nil, errors.New("session ID not found")
}
// Find the session and user in a single database query
result, err := a.store.GetUserSessionByID(ctx, sessionID)
if err != nil {
return nil, errors.Wrap(err, "session not found")
}
user, err := a.store.GetUser(ctx, &store.FindUser{ID: &result.UserID})
if err != nil {
return nil, errors.Wrap(err, "failed to get user")
}
if user == nil {
return nil, errors.New("user not found")
}
if user.RowStatus == store.Archived {
return nil, errors.New("user is archived")
}
// Validate session expiration
if result.Session.LastAccessedTime != nil {
expiration := result.Session.LastAccessedTime.AsTime().Add(SessionSlidingDuration)
if expiration.Before(time.Now()) {
return nil, errors.New("session expired")
}
}
return user, nil
}
// AuthenticateByJWT validates a JWT access token and returns the authenticated user.
//
// Validation steps:
// 1. Parse and verify JWT signature using server secret
// 2. Verify key ID matches expected version
// 3. Extract user ID from JWT claims (subject field)
// 4. Verify user exists and is not archived
// 5. Verify token exists in user's access_tokens list (for revocation support)
//
// Returns the user if authentication succeeds, or an error describing the failure.
func (a *Authenticator) AuthenticateByJWT(ctx context.Context, accessToken string) (*store.User, error) {
if accessToken == "" {
return nil, errors.New("access token not found")
}
claims := &ClaimsMessage{}
_, err := jwt.ParseWithClaims(accessToken, claims, func(t *jwt.Token) (any, error) {
if t.Method.Alg() != jwt.SigningMethodHS256.Name {
return nil, errors.Errorf("unexpected signing method: %v", t.Header["alg"])
}
kid, ok := t.Header["kid"].(string)
if !ok || kid != KeyID {
return nil, errors.Errorf("unexpected kid: %v", t.Header["kid"])
}
return []byte(a.secret), nil
})
if err != nil {
return nil, errors.New("invalid or expired access token")
}
userID, err := util.ConvertStringToInt32(claims.Subject)
if err != nil {
return nil, errors.Wrap(err, "malformed ID in token")
}
user, err := a.store.GetUser(ctx, &store.FindUser{ID: &userID})
if err != nil {
return nil, errors.Wrap(err, "failed to get user")
}
if user == nil {
return nil, errors.Errorf("user %d not found", userID)
}
if user.RowStatus == store.Archived {
return nil, errors.Errorf("user %d is archived", userID)
}
accessTokens, err := a.store.GetUserAccessTokens(ctx, user.ID)
if err != nil {
return nil, errors.Wrap(err, "failed to get user access tokens")
}
if !validateAccessToken(accessToken, accessTokens) {
return nil, errors.New("invalid access token")
}
return user, nil
}
// AuthorizeAndSetContext checks user authorization for the given procedure and sets context values.
//
// Authorization checks:
// - Admin-only methods require Host or Admin role (checked via isAdminOnly function)
//
// Context values set:
// - UserIDContextKey: Always set with the user's ID
// - SessionIDContextKey: Set if authenticated via session cookie
// - AccessTokenContextKey: Set if authenticated via JWT token
//
// Also updates session last accessed time for session-based auth (sliding expiration).
//
// Returns the updated context or an error if authorization fails.
func (a *Authenticator) AuthorizeAndSetContext(ctx context.Context, procedure string, user *store.User, sessionID, accessToken string, isAdminOnly func(string) bool) (context.Context, error) {
// Check admin-only method authorization
if isAdminOnly != nil && isAdminOnly(procedure) && user.Role != store.RoleHost && user.Role != store.RoleAdmin {
return nil, errors.Errorf("user %q is not authorized for this operation", user.Username)
}
// Set user ID in context (always)
ctx = context.WithValue(ctx, UserIDContextKey, user.ID)
// Set authentication method specific context values
if sessionID != "" {
ctx = context.WithValue(ctx, SessionIDContextKey, sessionID)
// Update session last accessed time for sliding expiration
_ = a.store.UpdateUserSessionLastAccessed(ctx, user.ID, sessionID, timestamppb.Now())
} else if accessToken != "" {
ctx = context.WithValue(ctx, AccessTokenContextKey, accessToken)
}
return ctx, nil
}
// validateAccessToken checks if the token exists in the user's access tokens list.
// This enables token revocation: deleted tokens are removed from the list.
func validateAccessToken(token string, tokens []*storepb.AccessTokensUserSetting_AccessToken) bool {
for _, t := range tokens {
if token == t.AccessToken {
return true
}
}
return false
}
// UpdateSessionLastAccessed updates the last accessed time for a session.
// This implements sliding expiration - sessions remain valid as long as they're used.
// Should be called after successful session-based authentication.
func (a *Authenticator) UpdateSessionLastAccessed(ctx context.Context, userID int32, sessionID string) {
// Fire-and-forget update; failures are logged but don't block the request
_ = a.store.UpdateUserSessionLastAccessed(ctx, userID, sessionID, timestamppb.Now())
}
// AuthResult contains the result of an authentication attempt.
type AuthResult struct {
User *store.User
SessionID string // Non-empty if authenticated via session cookie
AccessToken string // Non-empty if authenticated via JWT
}
// Authenticate tries to authenticate using the provided credentials.
// It tries session cookie first, then JWT token.
// Returns nil if no valid credentials are provided.
// On successful session auth, it also updates the session sliding expiration.
func (a *Authenticator) Authenticate(ctx context.Context, sessionID, authHeader string) *AuthResult {
// Try session cookie authentication first
if sessionID != "" {
user, err := a.AuthenticateBySession(ctx, sessionID)
if err == nil && user != nil {
a.UpdateSessionLastAccessed(ctx, user.ID, sessionID)
return &AuthResult{User: user, SessionID: sessionID}
}
}
// Try JWT token authentication
if token := ExtractBearerToken(authHeader); token != "" {
user, err := a.AuthenticateByJWT(ctx, token)
if err == nil && user != nil {
return &AuthResult{User: user, AccessToken: token}
}
}
return nil
}

72
server/auth/context.go Normal file
View File

@ -0,0 +1,72 @@
package auth
import (
"context"
"github.com/usememos/memos/store"
)
// ContextKey is the key type for context values.
// Using a custom type prevents collisions with other packages.
type ContextKey int
const (
// UserIDContextKey stores the authenticated user's ID.
// Set for both session-based and token-based authentication.
// Use GetUserID(ctx) to retrieve this value.
UserIDContextKey ContextKey = iota
// SessionIDContextKey stores the session ID for session-based auth.
// Only set when authenticated via session cookie.
SessionIDContextKey
// AccessTokenContextKey stores the JWT token for token-based auth.
// Only set when authenticated via Bearer token.
AccessTokenContextKey
)
// GetUserID retrieves the authenticated user's ID from the context.
// Returns 0 if no user ID is set (unauthenticated request).
func GetUserID(ctx context.Context) int32 {
if v, ok := ctx.Value(UserIDContextKey).(int32); ok {
return v
}
return 0
}
// GetSessionID retrieves the session ID from the context.
// Returns empty string if not authenticated via session cookie.
func GetSessionID(ctx context.Context) string {
if v, ok := ctx.Value(SessionIDContextKey).(string); ok {
return v
}
return ""
}
// GetAccessToken retrieves the JWT access token from the context.
// Returns empty string if not authenticated via bearer token.
func GetAccessToken(ctx context.Context) string {
if v, ok := ctx.Value(AccessTokenContextKey).(string); ok {
return v
}
return ""
}
// SetUserInContext sets the authenticated user's information in the context.
// This is a simpler alternative to AuthorizeAndSetContext for cases where
// authorization is handled separately (e.g., HTTP middleware).
//
// Parameters:
// - user: The authenticated user
// - sessionID: Set if authenticated via session cookie (empty string otherwise)
// - accessToken: Set if authenticated via JWT token (empty string otherwise)
func SetUserInContext(ctx context.Context, user *store.User, sessionID, accessToken string) context.Context {
ctx = context.WithValue(ctx, UserIDContextKey, user.ID)
if sessionID != "" {
ctx = context.WithValue(ctx, SessionIDContextKey, sessionID)
}
if accessToken != "" {
ctx = context.WithValue(ctx, AccessTokenContextKey, accessToken)
}
return ctx
}

35
server/auth/extract.go Normal file
View File

@ -0,0 +1,35 @@
package auth
import (
"net/http"
"strings"
)
// ExtractSessionCookieFromHeader extracts the session cookie value from an HTTP Cookie header.
// Returns empty string if the session cookie is not found.
func ExtractSessionCookieFromHeader(cookieHeader string) string {
if cookieHeader == "" {
return ""
}
// Use http.Request to parse cookies properly
req := &http.Request{Header: http.Header{"Cookie": []string{cookieHeader}}}
cookie, err := req.Cookie(SessionCookieName)
if err != nil {
return ""
}
return cookie.Value
}
// ExtractBearerToken extracts the JWT token from an Authorization header value.
// Expected format: "Bearer {token}"
// Returns empty string if no valid bearer token is found.
func ExtractBearerToken(authHeader string) string {
if authHeader == "" {
return ""
}
parts := strings.Fields(authHeader)
if len(parts) != 2 || !strings.EqualFold(parts[0], "bearer") {
return ""
}
return parts[1]
}

112
server/auth/token.go Normal file
View File

@ -0,0 +1,112 @@
// Package auth provides authentication and authorization for the Memos server.
//
// This package is used by:
// - server/router/api/v1: gRPC and Connect API interceptors
// - server/router/fileserver: HTTP file server authentication
//
// Authentication methods supported:
// - Session cookie: Browser-based authentication with sliding expiration
// - JWT token: API token authentication for programmatic access
package auth
import (
"fmt"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/usememos/memos/internal/util"
)
const (
// Issuer is the issuer claim in JWT tokens.
// This identifies tokens as issued by Memos.
Issuer = "memos"
// KeyID is the key identifier used in JWT header.
// Version "v1" allows for future key rotation while maintaining backward compatibility.
// If signing mechanism changes, add "v2", "v3", etc. and verify both versions.
KeyID = "v1"
// AccessTokenAudienceName is the audience claim for JWT access tokens.
// This ensures tokens are only used for API access, not other purposes.
AccessTokenAudienceName = "user.access-token"
// SessionSlidingDuration is the sliding expiration duration for user sessions.
// Sessions remain valid if accessed within the last 14 days.
// Each API call extends the session by updating last_accessed_time.
SessionSlidingDuration = 14 * 24 * time.Hour
// SessionCookieName is the HTTP cookie name used to store session information.
// Cookie value is the session ID (UUID).
SessionCookieName = "user_session"
)
// ClaimsMessage represents the claims structure in a JWT token.
//
// JWT Claims include:
// - name: Username (custom claim)
// - iss: Issuer = "memos"
// - aud: Audience = "user.access-token"
// - sub: Subject = user ID
// - iat: Issued at time
// - exp: Expiration time (optional, may be empty for never-expiring tokens).
type ClaimsMessage struct {
Name string `json:"name"` // Username
jwt.RegisteredClaims
}
// GenerateAccessToken generates a JWT access token for a user.
//
// Parameters:
// - username: The user's username (stored in "name" claim)
// - userID: The user's ID (stored in "sub" claim)
// - expirationTime: When the token expires (pass zero time for no expiration)
// - secret: Server secret used to sign the token
//
// Returns a signed JWT string or an error.
func GenerateAccessToken(username string, userID int32, expirationTime time.Time, secret []byte) (string, error) {
return generateToken(username, userID, AccessTokenAudienceName, expirationTime, secret)
}
// generateToken generates a JWT token with the given claims.
//
// Token structure:
// Header: {"alg": "HS256", "kid": "v1", "typ": "JWT"}
// Claims: {"name": username, "iss": "memos", "aud": [audience], "sub": userID, "iat": now, "exp": expiry}
// Signature: HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret).
func generateToken(username string, userID int32, audience string, expirationTime time.Time, secret []byte) (string, error) {
registeredClaims := jwt.RegisteredClaims{
Issuer: Issuer,
Audience: jwt.ClaimStrings{audience},
IssuedAt: jwt.NewNumericDate(time.Now()),
Subject: fmt.Sprint(userID),
}
if !expirationTime.IsZero() {
registeredClaims.ExpiresAt = jwt.NewNumericDate(expirationTime)
}
// Declare the token with the HS256 algorithm used for signing, and the claims.
token := jwt.NewWithClaims(jwt.SigningMethodHS256, &ClaimsMessage{
Name: username,
RegisteredClaims: registeredClaims,
})
token.Header["kid"] = KeyID
// Create the JWT string.
tokenString, err := token.SignedString(secret)
if err != nil {
return "", err
}
return tokenString, nil
}
// GenerateSessionID generates a unique session ID.
//
// Uses UUID v4 (random) for high entropy and uniqueness.
// Session IDs are stored in user settings and used to identify browser sessions.
// The session ID is stored directly in the cookie as the cookie value.
func GenerateSessionID() string {
return util.GenUUID()
}

View File

@ -1,120 +0,0 @@
package profiler
import (
"context"
"fmt"
"log/slog"
"net/http"
"net/http/pprof"
"runtime"
"time"
"github.com/labstack/echo/v4"
)
// Profiler provides HTTP endpoints for memory profiling.
type Profiler struct {
memStatsLogInterval time.Duration
}
// NewProfiler creates a new profiler.
func NewProfiler() *Profiler {
return &Profiler{
memStatsLogInterval: 1 * time.Minute,
}
}
// RegisterRoutes adds profiling endpoints to the Echo server.
func (*Profiler) RegisterRoutes(e *echo.Echo) {
// Register pprof handlers
g := e.Group("/debug/pprof")
g.GET("", echo.WrapHandler(http.HandlerFunc(pprof.Index)))
g.GET("/cmdline", echo.WrapHandler(http.HandlerFunc(pprof.Cmdline)))
g.GET("/profile", echo.WrapHandler(http.HandlerFunc(pprof.Profile)))
g.POST("/symbol", echo.WrapHandler(http.HandlerFunc(pprof.Symbol)))
g.GET("/symbol", echo.WrapHandler(http.HandlerFunc(pprof.Symbol)))
g.GET("/trace", echo.WrapHandler(http.HandlerFunc(pprof.Trace)))
g.GET("/allocs", echo.WrapHandler(http.HandlerFunc(pprof.Handler("allocs").ServeHTTP)))
g.GET("/block", echo.WrapHandler(http.HandlerFunc(pprof.Handler("block").ServeHTTP)))
g.GET("/goroutine", echo.WrapHandler(http.HandlerFunc(pprof.Handler("goroutine").ServeHTTP)))
g.GET("/heap", echo.WrapHandler(http.HandlerFunc(pprof.Handler("heap").ServeHTTP)))
g.GET("/mutex", echo.WrapHandler(http.HandlerFunc(pprof.Handler("mutex").ServeHTTP)))
g.GET("/threadcreate", echo.WrapHandler(http.HandlerFunc(pprof.Handler("threadcreate").ServeHTTP)))
// Add a custom memory stats endpoint.
g.GET("/memstats", func(c echo.Context) error {
var m runtime.MemStats
runtime.ReadMemStats(&m)
return c.JSON(http.StatusOK, map[string]interface{}{
"alloc": m.Alloc,
"totalAlloc": m.TotalAlloc,
"sys": m.Sys,
"numGC": m.NumGC,
"heapAlloc": m.HeapAlloc,
"heapSys": m.HeapSys,
"heapInuse": m.HeapInuse,
"heapObjects": m.HeapObjects,
})
})
}
// StartMemoryMonitor starts a goroutine that periodically logs memory stats.
func (p *Profiler) StartMemoryMonitor(ctx context.Context) {
go func() {
ticker := time.NewTicker(p.memStatsLogInterval)
defer ticker.Stop()
// Store previous heap allocation to track growth.
var lastHeapAlloc uint64
var lastNumGC uint32
for {
select {
case <-ticker.C:
var m runtime.MemStats
runtime.ReadMemStats(&m)
// Calculate heap growth since last check.
heapGrowth := int64(m.HeapAlloc) - int64(lastHeapAlloc)
gcCount := m.NumGC - lastNumGC
slog.Info("memory stats",
"heapAlloc", byteCountIEC(m.HeapAlloc),
"heapSys", byteCountIEC(m.HeapSys),
"heapObjects", m.HeapObjects,
"heapGrowth", byteCountIEC(uint64(heapGrowth)),
"numGoroutine", runtime.NumGoroutine(),
"numGC", m.NumGC,
"gcSince", gcCount,
"nextGC", byteCountIEC(m.NextGC),
"gcPause", time.Duration(m.PauseNs[(m.NumGC+255)%256]).String(),
)
// Track values for next iteration.
lastHeapAlloc = m.HeapAlloc
lastNumGC = m.NumGC
// Force GC if memory usage is high to see if objects can be reclaimed.
if m.HeapAlloc > 500*1024*1024 { // 500 MB threshold
slog.Info("forcing garbage collection due to high memory usage")
}
case <-ctx.Done():
return
}
}
}()
}
// byteCountIEC converts bytes to a human-readable string (MiB, GiB).
func byteCountIEC(b uint64) string {
const unit = 1024
if b < unit {
return fmt.Sprintf("%d B", b)
}
div, exp := uint64(unit), 0
for n := b / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.1f %ciB", float64(b)/float64(div), "KMGTPE"[exp])
}

View File

@ -1,262 +0,0 @@
package v1
import (
"context"
"net/http"
"strings"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/pkg/errors"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/usememos/memos/internal/util"
storepb "github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/store"
)
// ContextKey is the key type of context value.
type ContextKey int
const (
// The key name used to store user's ID in the context (for user-based auth).
userIDContextKey ContextKey = iota
// The key name used to store session ID in the context (for session-based auth).
sessionIDContextKey
// The key name used to store access token in the context (for token-based auth).
accessTokenContextKey
)
// GRPCAuthInterceptor is the auth interceptor for gRPC server.
type GRPCAuthInterceptor struct {
Store *store.Store
secret string
}
// NewGRPCAuthInterceptor returns a new API auth interceptor.
func NewGRPCAuthInterceptor(store *store.Store, secret string) *GRPCAuthInterceptor {
return &GRPCAuthInterceptor{
Store: store,
secret: secret,
}
}
// AuthenticationInterceptor is the unary interceptor for gRPC API.
func (in *GRPCAuthInterceptor) AuthenticationInterceptor(ctx context.Context, request any, serverInfo *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, status.Errorf(codes.Unauthenticated, "failed to parse metadata from incoming context")
}
// Try to authenticate via session ID (from cookie) first
if sessionCookieValue, err := getSessionIDFromMetadata(md); err == nil && sessionCookieValue != "" {
user, err := in.authenticateBySession(ctx, sessionCookieValue)
if err == nil && user != nil {
// Extract just the sessionID part for context storage
_, sessionID, parseErr := ParseSessionCookieValue(sessionCookieValue)
if parseErr != nil {
return nil, status.Errorf(codes.Internal, "failed to parse session cookie: %v", parseErr)
}
return in.handleAuthenticatedRequest(ctx, request, serverInfo, handler, user, sessionID, "")
}
}
// Try to authenticate via JWT access token (from Authorization header)
if accessToken, err := getAccessTokenFromMetadata(md); err == nil && accessToken != "" {
user, err := in.authenticateByJWT(ctx, accessToken)
if err == nil && user != nil {
return in.handleAuthenticatedRequest(ctx, request, serverInfo, handler, user, "", accessToken)
}
}
// If no valid authentication found, check if this method is in the allowlist (public endpoints)
if isUnauthorizeAllowedMethod(serverInfo.FullMethod) {
return handler(ctx, request)
}
// If authentication is required but not found, reject the request
return nil, status.Errorf(codes.Unauthenticated, "authentication required")
}
// handleAuthenticatedRequest processes an authenticated request with the given user and auth info.
func (in *GRPCAuthInterceptor) handleAuthenticatedRequest(ctx context.Context, request any, serverInfo *grpc.UnaryServerInfo, handler grpc.UnaryHandler, user *store.User, sessionID, accessToken string) (any, error) {
// Check user status
if user.RowStatus == store.Archived {
return nil, errors.Errorf("user %q is archived", user.Username)
}
if isOnlyForAdminAllowedMethod(serverInfo.FullMethod) && user.Role != store.RoleHost && user.Role != store.RoleAdmin {
return nil, errors.Errorf("user %q is not admin", user.Username)
}
// Set context values
ctx = context.WithValue(ctx, userIDContextKey, user.ID)
if sessionID != "" {
// Session-based authentication
ctx = context.WithValue(ctx, sessionIDContextKey, sessionID)
// Update session last accessed time
_ = in.updateSessionLastAccessed(ctx, user.ID, sessionID)
} else if accessToken != "" {
// JWT access token-based authentication
ctx = context.WithValue(ctx, accessTokenContextKey, accessToken)
}
return handler(ctx, request)
}
// authenticateByJWT authenticates a user using JWT access token from Authorization header.
func (in *GRPCAuthInterceptor) authenticateByJWT(ctx context.Context, accessToken string) (*store.User, error) {
if accessToken == "" {
return nil, status.Errorf(codes.Unauthenticated, "access token not found")
}
claims := &ClaimsMessage{}
_, err := jwt.ParseWithClaims(accessToken, claims, func(t *jwt.Token) (any, error) {
if t.Method.Alg() != jwt.SigningMethodHS256.Name {
return nil, status.Errorf(codes.Unauthenticated, "unexpected access token signing method=%v, expect %v", t.Header["alg"], jwt.SigningMethodHS256)
}
if kid, ok := t.Header["kid"].(string); ok {
if kid == "v1" {
return []byte(in.secret), nil
}
}
return nil, status.Errorf(codes.Unauthenticated, "unexpected access token kid=%v", t.Header["kid"])
})
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "Invalid or expired access token")
}
// Get user from JWT claims
userID, err := util.ConvertStringToInt32(claims.Subject)
if err != nil {
return nil, errors.Wrap(err, "malformed ID in the token")
}
user, err := in.Store.GetUser(ctx, &store.FindUser{
ID: &userID,
})
if err != nil {
return nil, errors.Wrap(err, "failed to get user")
}
if user == nil {
return nil, errors.Errorf("user %q not exists", userID)
}
if user.RowStatus == store.Archived {
return nil, errors.Errorf("user %q is archived", userID)
}
// Validate that this access token exists in the user's access tokens
accessTokens, err := in.Store.GetUserAccessTokens(ctx, user.ID)
if err != nil {
return nil, errors.Wrapf(err, "failed to get user access tokens")
}
if !validateAccessToken(accessToken, accessTokens) {
return nil, status.Errorf(codes.Unauthenticated, "invalid access token")
}
return user, nil
}
// authenticateBySession authenticates a user using session ID from cookie.
func (in *GRPCAuthInterceptor) authenticateBySession(ctx context.Context, sessionCookieValue string) (*store.User, error) {
if sessionCookieValue == "" {
return nil, status.Errorf(codes.Unauthenticated, "session cookie value not found")
}
// Parse the cookie value to extract userID and sessionID
userID, sessionID, err := ParseSessionCookieValue(sessionCookieValue)
if err != nil {
return nil, status.Errorf(codes.Unauthenticated, "invalid session cookie format: %v", err)
}
// Get the user directly using the userID from the cookie
user, err := in.Store.GetUser(ctx, &store.FindUser{
ID: &userID,
})
if err != nil {
return nil, errors.Wrap(err, "failed to get user")
}
if user == nil {
return nil, status.Errorf(codes.Unauthenticated, "user not found")
}
if user.RowStatus == store.Archived {
return nil, status.Errorf(codes.Unauthenticated, "user is archived")
}
// Get user sessions and validate the sessionID
sessions, err := in.Store.GetUserSessions(ctx, userID)
if err != nil {
return nil, errors.Wrap(err, "failed to get user sessions")
}
if !validateUserSession(sessionID, sessions) {
return nil, status.Errorf(codes.Unauthenticated, "invalid or expired session")
}
return user, nil
}
// updateSessionLastAccessed updates the last accessed time for a user session.
func (in *GRPCAuthInterceptor) updateSessionLastAccessed(ctx context.Context, userID int32, sessionID string) error {
return in.Store.UpdateUserSessionLastAccessed(ctx, userID, sessionID, timestamppb.Now())
}
// validateUserSession checks if a session exists and is still valid using sliding expiration.
func validateUserSession(sessionID string, userSessions []*storepb.SessionsUserSetting_Session) bool {
for _, session := range userSessions {
if sessionID == session.SessionId {
// Use sliding expiration: check if last_accessed_time + 2 weeks > current_time
if session.LastAccessedTime != nil {
expirationTime := session.LastAccessedTime.AsTime().Add(SessionSlidingDuration)
if expirationTime.Before(time.Now()) {
return false
}
}
return true
}
}
return false
}
// getSessionIDFromMetadata extracts session cookie value from cookie.
func getSessionIDFromMetadata(md metadata.MD) (string, error) {
// Check the cookie header for session cookie value
var sessionCookieValue string
for _, t := range append(md.Get("grpcgateway-cookie"), md.Get("cookie")...) {
header := http.Header{}
header.Add("Cookie", t)
request := http.Request{Header: header}
if v, _ := request.Cookie(SessionCookieName); v != nil {
sessionCookieValue = v.Value
}
}
if sessionCookieValue == "" {
return "", errors.New("session cookie not found")
}
return sessionCookieValue, nil
}
// getAccessTokenFromMetadata extracts access token from Authorization header.
func getAccessTokenFromMetadata(md metadata.MD) (string, error) {
// Check the HTTP request Authorization header.
authorizationHeaders := md.Get("Authorization")
if len(authorizationHeaders) == 0 {
return "", errors.New("authorization header not found")
}
authHeaderParts := strings.Fields(authorizationHeaders[0])
if len(authHeaderParts) != 2 || strings.ToLower(authHeaderParts[0]) != "bearer" {
return "", errors.New("authorization header format must be Bearer {token}")
}
return authHeaderParts[1], nil
}
func validateAccessToken(accessTokenString string, userAccessTokens []*storepb.AccessTokensUserSetting_AccessToken) bool {
for _, userAccessToken := range userAccessTokens {
if accessTokenString == userAccessToken.AccessToken {
return true
}
}
return false
}

View File

@ -1,34 +1,41 @@
package v1 package v1
var authenticationAllowlistMethods = map[string]bool{ // PublicMethods defines API endpoints that don't require authentication.
"/memos.api.v1.WorkspaceService/GetWorkspaceProfile": true, // All other endpoints require a valid session or access token.
"/memos.api.v1.WorkspaceService/GetWorkspaceSetting": true, //
"/memos.api.v1.IdentityProviderService/ListIdentityProviders": true, // This is the SINGLE SOURCE OF TRUTH for public endpoints.
"/memos.api.v1.AuthService/CreateSession": true, // Both Connect interceptor and gRPC-Gateway interceptor use this map.
"/memos.api.v1.AuthService/GetCurrentSession": true, //
"/memos.api.v1.UserService/CreateUser": true, // Format: Full gRPC procedure path as returned by req.Spec().Procedure (Connect)
"/memos.api.v1.UserService/GetUser": true, // or info.FullMethod (gRPC interceptor).
"/memos.api.v1.UserService/GetUserAvatar": true, var PublicMethods = map[string]struct{}{
"/memos.api.v1.UserService/GetUserStats": true, // Auth Service - login flow must be accessible without auth
"/memos.api.v1.UserService/ListAllUserStats": true, "/memos.api.v1.AuthService/CreateSession": {},
"/memos.api.v1.UserService/SearchUsers": true, "/memos.api.v1.AuthService/GetCurrentSession": {},
"/memos.api.v1.MemoService/GetMemo": true,
"/memos.api.v1.MemoService/ListMemos": true, // Instance Service - needed before login to show instance info
"/memos.api.v1.MarkdownService/GetLinkMetadata": true, "/memos.api.v1.InstanceService/GetInstanceProfile": {},
"/memos.api.v1.AttachmentService/GetAttachmentBinary": true, "/memos.api.v1.InstanceService/GetInstanceSetting": {},
// User Service - public user profiles and stats
"/memos.api.v1.UserService/CreateUser": {}, // Allow first user registration
"/memos.api.v1.UserService/GetUser": {},
"/memos.api.v1.UserService/GetUserAvatar": {},
"/memos.api.v1.UserService/GetUserStats": {},
"/memos.api.v1.UserService/ListAllUserStats": {},
"/memos.api.v1.UserService/SearchUsers": {},
// Identity Provider Service - SSO buttons on login page
"/memos.api.v1.IdentityProviderService/ListIdentityProviders": {},
// Memo Service - public memos (visibility filtering done in service layer)
"/memos.api.v1.MemoService/GetMemo": {},
"/memos.api.v1.MemoService/ListMemos": {},
} }
// isUnauthorizeAllowedMethod returns whether the method is exempted from authentication. // IsPublicMethod checks if a procedure path is public (no authentication required).
func isUnauthorizeAllowedMethod(fullMethodName string) bool { // Returns true for public methods, false for protected methods.
return authenticationAllowlistMethods[fullMethodName] func IsPublicMethod(procedure string) bool {
} _, ok := PublicMethods[procedure]
return ok
var allowedMethodsOnlyForAdmin = map[string]bool{
"/memos.api.v1.UserService/CreateUser": true,
"/memos.api.v1.WorkspaceService/UpdateWorkspaceSetting": true,
}
// isOnlyForAdminAllowedMethod returns true if the method is allowed to be called only by admin.
func isOnlyForAdminAllowedMethod(methodName string) bool {
return allowedMethodsOnlyForAdmin[methodName]
} }

View File

@ -0,0 +1,87 @@
package v1
import (
"testing"
"github.com/stretchr/testify/assert"
)
// TestPublicMethodsArePublic verifies that methods in PublicMethods are recognized as public.
func TestPublicMethodsArePublic(t *testing.T) {
publicMethods := []string{
// Auth Service
"/memos.api.v1.AuthService/CreateSession",
"/memos.api.v1.AuthService/GetCurrentSession",
// Instance Service
"/memos.api.v1.InstanceService/GetInstanceProfile",
"/memos.api.v1.InstanceService/GetInstanceSetting",
// User Service
"/memos.api.v1.UserService/CreateUser",
"/memos.api.v1.UserService/GetUser",
"/memos.api.v1.UserService/GetUserAvatar",
"/memos.api.v1.UserService/GetUserStats",
"/memos.api.v1.UserService/ListAllUserStats",
"/memos.api.v1.UserService/SearchUsers",
// Identity Provider Service
"/memos.api.v1.IdentityProviderService/ListIdentityProviders",
// Memo Service
"/memos.api.v1.MemoService/GetMemo",
"/memos.api.v1.MemoService/ListMemos",
}
for _, method := range publicMethods {
t.Run(method, func(t *testing.T) {
assert.True(t, IsPublicMethod(method), "Expected %s to be public", method)
})
}
}
// TestProtectedMethodsRequireAuth verifies that non-public methods are recognized as protected.
func TestProtectedMethodsRequireAuth(t *testing.T) {
protectedMethods := []string{
// Auth Service - logout requires auth
"/memos.api.v1.AuthService/DeleteSession",
// Instance Service - admin operations
"/memos.api.v1.InstanceService/UpdateInstanceSetting",
// User Service - modification operations
"/memos.api.v1.UserService/ListUsers",
"/memos.api.v1.UserService/UpdateUser",
"/memos.api.v1.UserService/DeleteUser",
// Memo Service - write operations
"/memos.api.v1.MemoService/CreateMemo",
"/memos.api.v1.MemoService/UpdateMemo",
"/memos.api.v1.MemoService/DeleteMemo",
// Attachment Service - write operations
"/memos.api.v1.AttachmentService/CreateAttachment",
"/memos.api.v1.AttachmentService/DeleteAttachment",
// Shortcut Service
"/memos.api.v1.ShortcutService/CreateShortcut",
"/memos.api.v1.ShortcutService/ListShortcuts",
"/memos.api.v1.ShortcutService/UpdateShortcut",
"/memos.api.v1.ShortcutService/DeleteShortcut",
// Activity Service
"/memos.api.v1.ActivityService/GetActivity",
}
for _, method := range protectedMethods {
t.Run(method, func(t *testing.T) {
assert.False(t, IsPublicMethod(method), "Expected %s to require auth", method)
})
}
}
// TestUnknownMethodsRequireAuth verifies that unknown methods default to requiring auth.
func TestUnknownMethodsRequireAuth(t *testing.T) {
unknownMethods := []string{
"/unknown.Service/Method",
"/memos.api.v1.UnknownService/Method",
"",
"invalid",
}
for _, method := range unknownMethods {
t.Run(method, func(t *testing.T) {
assert.False(t, IsPublicMethod(method), "Unknown method %q should require auth", method)
})
}
}

View File

@ -64,6 +64,9 @@ func (s *APIV1Service) GetActivity(ctx context.Context, request *v1pb.GetActivit
return activityMessage, nil return activityMessage, nil
} }
// convertActivityFromStore converts a storage-layer activity to an API activity.
// This handles the mapping between internal activity representation and the public API,
// including proper type and level conversions.
func (s *APIV1Service) convertActivityFromStore(ctx context.Context, activity *store.Activity) (*v1pb.Activity, error) { func (s *APIV1Service) convertActivityFromStore(ctx context.Context, activity *store.Activity) (*v1pb.Activity, error) {
payload, err := s.convertActivityPayloadFromStore(ctx, activity.Payload) payload, err := s.convertActivityPayloadFromStore(ctx, activity.Payload)
if err != nil { if err != nil {
@ -98,9 +101,12 @@ func (s *APIV1Service) convertActivityFromStore(ctx context.Context, activity *s
}, nil }, nil
} }
// convertActivityPayloadFromStore converts a storage-layer activity payload to an API payload.
// This resolves references (e.g., memo IDs) to resource names for the API.
func (s *APIV1Service) convertActivityPayloadFromStore(ctx context.Context, payload *storepb.ActivityPayload) (*v1pb.ActivityPayload, error) { func (s *APIV1Service) convertActivityPayloadFromStore(ctx context.Context, payload *storepb.ActivityPayload) (*v1pb.ActivityPayload, error) {
v2Payload := &v1pb.ActivityPayload{} v2Payload := &v1pb.ActivityPayload{}
if payload.MemoComment != nil { if payload.MemoComment != nil {
// Fetch the comment memo
memo, err := s.Store.GetMemo(ctx, &store.FindMemo{ memo, err := s.Store.GetMemo(ctx, &store.FindMemo{
ID: &payload.MemoComment.MemoId, ID: &payload.MemoComment.MemoId,
ExcludeContent: true, ExcludeContent: true,
@ -111,6 +117,8 @@ func (s *APIV1Service) convertActivityPayloadFromStore(ctx context.Context, payl
if memo == nil { if memo == nil {
return nil, status.Errorf(codes.NotFound, "memo does not exist") return nil, status.Errorf(codes.NotFound, "memo does not exist")
} }
// Fetch the related memo (the one being commented on)
relatedMemo, err := s.Store.GetMemo(ctx, &store.FindMemo{ relatedMemo, err := s.Store.GetMemo(ctx, &store.FindMemo{
ID: &payload.MemoComment.RelatedMemoId, ID: &payload.MemoComment.RelatedMemoId,
ExcludeContent: true, ExcludeContent: true,
@ -118,6 +126,7 @@ func (s *APIV1Service) convertActivityPayloadFromStore(ctx context.Context, payl
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get related memo: %v", err) return nil, status.Errorf(codes.Internal, "failed to get related memo: %v", err)
} }
v2Payload.Payload = &v1pb.ActivityPayload_MemoComment{ v2Payload.Payload = &v1pb.ActivityPayload_MemoComment{
MemoComment: &v1pb.ActivityMemoCommentPayload{ MemoComment: &v1pb.ActivityMemoCommentPayload{
Memo: fmt.Sprintf("%s%s", MemoNamePrefix, memo.UID), Memo: fmt.Sprintf("%s%s", MemoNamePrefix, memo.UID),

View File

@ -6,21 +6,15 @@ import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io" "io"
"log/slog"
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strconv"
"strings" "strings"
"time" "time"
"github.com/disintegration/imaging"
"github.com/lithammer/shortuuid/v4" "github.com/lithammer/shortuuid/v4"
"github.com/pkg/errors" "github.com/pkg/errors"
"google.golang.org/genproto/googleapis/api/httpbody"
"google.golang.org/grpc"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb" "google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/timestamppb"
@ -64,9 +58,15 @@ func (s *APIV1Service) CreateAttachment(ctx context.Context, request *v1pb.Creat
if request.Attachment.Filename == "" { if request.Attachment.Filename == "" {
return nil, status.Errorf(codes.InvalidArgument, "filename is required") return nil, status.Errorf(codes.InvalidArgument, "filename is required")
} }
if !validateFilename(request.Attachment.Filename) {
return nil, status.Errorf(codes.InvalidArgument, "filename contains invalid characters or format")
}
if request.Attachment.Type == "" { if request.Attachment.Type == "" {
return nil, status.Errorf(codes.InvalidArgument, "type is required") return nil, status.Errorf(codes.InvalidArgument, "type is required")
} }
if !isValidMimeType(request.Attachment.Type) {
return nil, status.Errorf(codes.InvalidArgument, "invalid MIME type format")
}
// Use provided attachment_id or generate a new one // Use provided attachment_id or generate a new one
attachmentUID := request.AttachmentId attachmentUID := request.AttachmentId
@ -81,12 +81,12 @@ func (s *APIV1Service) CreateAttachment(ctx context.Context, request *v1pb.Creat
Type: request.Attachment.Type, Type: request.Attachment.Type,
} }
workspaceStorageSetting, err := s.Store.GetWorkspaceStorageSetting(ctx) instanceStorageSetting, err := s.Store.GetInstanceStorageSetting(ctx)
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get workspace storage setting: %v", err) return nil, status.Errorf(codes.Internal, "failed to get instance storage setting: %v", err)
} }
size := binary.Size(request.Attachment.Content) size := binary.Size(request.Attachment.Content)
uploadSizeLimit := int(workspaceStorageSetting.UploadSizeLimitMb) * MebiByte uploadSizeLimit := int(instanceStorageSetting.UploadSizeLimitMb) * MebiByte
if uploadSizeLimit == 0 { if uploadSizeLimit == 0 {
uploadSizeLimit = MaxUploadBufferSizeBytes uploadSizeLimit = MaxUploadBufferSizeBytes
} }
@ -194,117 +194,6 @@ func (s *APIV1Service) GetAttachment(ctx context.Context, request *v1pb.GetAttac
return convertAttachmentFromStore(attachment), nil return convertAttachmentFromStore(attachment), nil
} }
func (s *APIV1Service) GetAttachmentBinary(ctx context.Context, request *v1pb.GetAttachmentBinaryRequest) (*httpbody.HttpBody, error) {
attachmentUID, err := ExtractAttachmentUIDFromName(request.Name)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid attachment id: %v", err)
}
attachment, err := s.Store.GetAttachment(ctx, &store.FindAttachment{
GetBlob: true,
UID: &attachmentUID,
})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get attachment: %v", err)
}
if attachment == nil {
return nil, status.Errorf(codes.NotFound, "attachment not found")
}
// Check the related memo visibility.
if attachment.MemoID != nil {
memo, err := s.Store.GetMemo(ctx, &store.FindMemo{
ID: attachment.MemoID,
})
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to find memo by ID: %v", attachment.MemoID)
}
if memo != nil && memo.Visibility != store.Public {
user, err := s.GetCurrentUser(ctx)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get current user: %v", err)
}
if user == nil {
return nil, status.Errorf(codes.Unauthenticated, "unauthorized access")
}
if memo.Visibility == store.Private && user.ID != attachment.CreatorID {
return nil, status.Errorf(codes.Unauthenticated, "unauthorized access")
}
}
}
if request.Thumbnail && util.HasPrefixes(attachment.Type, SupportedThumbnailMimeTypes...) {
thumbnailBlob, err := s.getOrGenerateThumbnail(attachment)
if err != nil {
// thumbnail failures are logged as warnings and not cosidered critical failures as
// a attachment image can be used in its place.
slog.Warn("failed to get attachment thumbnail image", slog.Any("error", err))
} else {
return &httpbody.HttpBody{
ContentType: attachment.Type,
Data: thumbnailBlob,
}, nil
}
}
blob, err := s.GetAttachmentBlob(attachment)
if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get attachment blob: %v", err)
}
contentType := attachment.Type
if strings.HasPrefix(contentType, "text/") {
contentType += "; charset=utf-8"
}
// Prevent XSS attacks by serving potentially unsafe files with a content type that prevents script execution.
if strings.EqualFold(contentType, "image/svg+xml") ||
strings.EqualFold(contentType, "text/html") ||
strings.EqualFold(contentType, "application/xhtml+xml") {
contentType = "application/octet-stream"
}
// Extract range header from gRPC metadata for iOS Safari video support
var rangeHeader string
if md, ok := metadata.FromIncomingContext(ctx); ok {
// Check for range header from gRPC-Gateway
if ranges := md.Get("grpcgateway-range"); len(ranges) > 0 {
rangeHeader = ranges[0]
} else if ranges := md.Get("range"); len(ranges) > 0 {
rangeHeader = ranges[0]
}
// Log for debugging iOS Safari issues
if userAgents := md.Get("user-agent"); len(userAgents) > 0 {
userAgent := userAgents[0]
if strings.Contains(strings.ToLower(userAgent), "safari") && rangeHeader != "" {
slog.Debug("Safari range request detected",
slog.String("range", rangeHeader),
slog.String("user-agent", userAgent),
slog.String("content-type", contentType))
}
}
}
// Handle range requests for video/audio streaming (iOS Safari requirement)
if rangeHeader != "" && (strings.HasPrefix(contentType, "video/") || strings.HasPrefix(contentType, "audio/")) {
return s.handleRangeRequest(ctx, blob, rangeHeader, contentType)
}
// Set headers for streaming support
if strings.HasPrefix(contentType, "video/") || strings.HasPrefix(contentType, "audio/") {
if err := setResponseHeaders(ctx, map[string]string{
"accept-ranges": "bytes",
"content-length": fmt.Sprintf("%d", len(blob)),
"cache-control": "public, max-age=3600", // 1 hour cache
}); err != nil {
slog.Warn("failed to set streaming headers", slog.Any("error", err))
}
}
return &httpbody.HttpBody{
ContentType: contentType,
Data: blob,
}, nil
}
func (s *APIV1Service) UpdateAttachment(ctx context.Context, request *v1pb.UpdateAttachmentRequest) (*v1pb.Attachment, error) { func (s *APIV1Service) UpdateAttachment(ctx context.Context, request *v1pb.UpdateAttachmentRequest) (*v1pb.Attachment, error) {
attachmentUID, err := ExtractAttachmentUIDFromName(request.Attachment.Name) attachmentUID, err := ExtractAttachmentUIDFromName(request.Attachment.Name)
if err != nil { if err != nil {
@ -325,6 +214,9 @@ func (s *APIV1Service) UpdateAttachment(ctx context.Context, request *v1pb.Updat
} }
for _, field := range request.UpdateMask.Paths { for _, field := range request.UpdateMask.Paths {
if field == "filename" { if field == "filename" {
if !validateFilename(request.Attachment.Filename) {
return nil, status.Errorf(codes.InvalidArgument, "filename contains invalid characters or format")
}
update.Filename = &request.Attachment.Filename update.Filename = &request.Attachment.Filename
} }
} }
@ -389,15 +281,15 @@ func convertAttachmentFromStore(attachment *store.Attachment) *v1pb.Attachment {
// SaveAttachmentBlob save the blob of attachment based on the storage config. // SaveAttachmentBlob save the blob of attachment based on the storage config.
func SaveAttachmentBlob(ctx context.Context, profile *profile.Profile, stores *store.Store, create *store.Attachment) error { func SaveAttachmentBlob(ctx context.Context, profile *profile.Profile, stores *store.Store, create *store.Attachment) error {
workspaceStorageSetting, err := stores.GetWorkspaceStorageSetting(ctx) instanceStorageSetting, err := stores.GetInstanceStorageSetting(ctx)
if err != nil { if err != nil {
return errors.Wrap(err, "Failed to find workspace storage setting") return errors.Wrap(err, "Failed to find instance storage setting")
} }
if workspaceStorageSetting.StorageType == storepb.WorkspaceStorageSetting_LOCAL { if instanceStorageSetting.StorageType == storepb.InstanceStorageSetting_LOCAL {
filepathTemplate := "assets/{timestamp}_{filename}" filepathTemplate := "assets/{timestamp}_{filename}"
if workspaceStorageSetting.FilepathTemplate != "" { if instanceStorageSetting.FilepathTemplate != "" {
filepathTemplate = workspaceStorageSetting.FilepathTemplate filepathTemplate = instanceStorageSetting.FilepathTemplate
} }
internalPath := filepathTemplate internalPath := filepathTemplate
@ -429,17 +321,17 @@ func SaveAttachmentBlob(ctx context.Context, profile *profile.Profile, stores *s
create.Reference = internalPath create.Reference = internalPath
create.Blob = nil create.Blob = nil
create.StorageType = storepb.AttachmentStorageType_LOCAL create.StorageType = storepb.AttachmentStorageType_LOCAL
} else if workspaceStorageSetting.StorageType == storepb.WorkspaceStorageSetting_S3 { } else if instanceStorageSetting.StorageType == storepb.InstanceStorageSetting_S3 {
s3Config := workspaceStorageSetting.S3Config s3Config := instanceStorageSetting.S3Config
if s3Config == nil { if s3Config == nil {
return errors.Errorf("No actived external storage found") return errors.Errorf("No activated external storage found")
} }
s3Client, err := s3.NewClient(ctx, s3Config) s3Client, err := s3.NewClient(ctx, s3Config)
if err != nil { if err != nil {
return errors.Wrap(err, "Failed to create s3 client") return errors.Wrap(err, "Failed to create s3 client")
} }
filepathTemplate := workspaceStorageSetting.FilepathTemplate filepathTemplate := instanceStorageSetting.FilepathTemplate
if !strings.Contains(filepathTemplate, "{filename}") { if !strings.Contains(filepathTemplate, "{filename}") {
filepathTemplate = filepath.Join(filepathTemplate, "{filename}") filepathTemplate = filepath.Join(filepathTemplate, "{filename}")
} }
@ -523,75 +415,6 @@ func (s *APIV1Service) GetAttachmentBlob(attachment *store.Attachment) ([]byte,
return attachment.Blob, nil return attachment.Blob, nil
} }
const (
// thumbnailMaxSize is the maximum size in pixels for the largest dimension of the thumbnail image.
thumbnailMaxSize = 600
)
// getOrGenerateThumbnail returns the thumbnail image of the attachment.
func (s *APIV1Service) getOrGenerateThumbnail(attachment *store.Attachment) ([]byte, error) {
thumbnailCacheFolder := filepath.Join(s.Profile.Data, ThumbnailCacheFolder)
if err := os.MkdirAll(thumbnailCacheFolder, os.ModePerm); err != nil {
return nil, errors.Wrap(err, "failed to create thumbnail cache folder")
}
filePath := filepath.Join(thumbnailCacheFolder, fmt.Sprintf("%d%s", attachment.ID, filepath.Ext(attachment.Filename)))
if _, err := os.Stat(filePath); err != nil {
if !os.IsNotExist(err) {
return nil, errors.Wrap(err, "failed to check thumbnail image stat")
}
// If thumbnail image does not exist, generate and save the thumbnail image.
blob, err := s.GetAttachmentBlob(attachment)
if err != nil {
return nil, errors.Wrap(err, "failed to get attachment blob")
}
img, err := imaging.Decode(bytes.NewReader(blob), imaging.AutoOrientation(true))
if err != nil {
return nil, errors.Wrap(err, "failed to decode thumbnail image")
}
// The largest dimension is set to thumbnailMaxSize and the smaller dimension is scaled proportionally.
// Small images are not enlarged.
width := img.Bounds().Dx()
height := img.Bounds().Dy()
var thumbnailWidth, thumbnailHeight int
// Only resize if the image is larger than thumbnailMaxSize
if max(width, height) > thumbnailMaxSize {
if width >= height {
// Landscape or square - constrain width, maintain aspect ratio for height
thumbnailWidth = thumbnailMaxSize
thumbnailHeight = 0
} else {
// Portrait - constrain height, maintain aspect ratio for width
thumbnailWidth = 0
thumbnailHeight = thumbnailMaxSize
}
} else {
// Keep original dimensions for small images
thumbnailWidth = width
thumbnailHeight = height
}
// Resize the image to the calculated dimensions.
thumbnailImage := imaging.Resize(img, thumbnailWidth, thumbnailHeight, imaging.Lanczos)
if err := imaging.Save(thumbnailImage, filePath); err != nil {
return nil, errors.Wrap(err, "failed to save thumbnail file")
}
}
thumbnailFile, err := os.Open(filePath)
if err != nil {
return nil, errors.Wrap(err, "failed to open thumbnail file")
}
defer thumbnailFile.Close()
blob, err := io.ReadAll(thumbnailFile)
if err != nil {
return nil, errors.Wrap(err, "failed to read thumbnail file")
}
return blob, nil
}
var fileKeyPattern = regexp.MustCompile(`\{[a-z]{1,9}\}`) var fileKeyPattern = regexp.MustCompile(`\{[a-z]{1,9}\}`)
func replaceFilenameWithPathTemplate(path, filename string) string { func replaceFilenameWithPathTemplate(path, filename string) string {
@ -623,81 +446,29 @@ func replaceFilenameWithPathTemplate(path, filename string) string {
return path return path
} }
// handleRangeRequest handles HTTP range requests for video/audio streaming (iOS Safari requirement). func validateFilename(filename string) bool {
func (*APIV1Service) handleRangeRequest(ctx context.Context, data []byte, rangeHeader, contentType string) (*httpbody.HttpBody, error) { // Reject path traversal attempts and make sure no additional directories are created
// Parse "bytes=start-end" if !filepath.IsLocal(filename) || strings.ContainsAny(filename, "/\\") {
if !strings.HasPrefix(rangeHeader, "bytes=") { return false
return nil, status.Errorf(codes.InvalidArgument, "invalid range header format")
} }
rangeSpec := strings.TrimPrefix(rangeHeader, "bytes=") // Reject filenames starting or ending with spaces or periods
parts := strings.Split(rangeSpec, "-") if strings.HasPrefix(filename, " ") || strings.HasSuffix(filename, " ") ||
if len(parts) != 2 { strings.HasPrefix(filename, ".") || strings.HasSuffix(filename, ".") {
return nil, status.Errorf(codes.InvalidArgument, "invalid range specification") return false
} }
fileSize := int64(len(data)) return true
start, end := int64(0), fileSize-1
// Parse start position
if parts[0] != "" {
if s, err := strconv.ParseInt(parts[0], 10, 64); err == nil {
start = s
} else {
return nil, status.Errorf(codes.InvalidArgument, "invalid range start: %s", parts[0])
}
}
// Parse end position
if parts[1] != "" {
if e, err := strconv.ParseInt(parts[1], 10, 64); err == nil {
end = e
} else {
return nil, status.Errorf(codes.InvalidArgument, "invalid range end: %s", parts[1])
}
}
// Validate range
if start < 0 || end >= fileSize || start > end {
// Set Content-Range header for 416 response
if err := setResponseHeaders(ctx, map[string]string{
"content-range": fmt.Sprintf("bytes */%d", fileSize),
}); err != nil {
slog.Warn("failed to set content-range header", slog.Any("error", err))
}
return nil, status.Errorf(codes.OutOfRange, "requested range not satisfiable")
}
// Set partial content headers (HTTP 206)
if err := setResponseHeaders(ctx, map[string]string{
"accept-ranges": "bytes",
"content-range": fmt.Sprintf("bytes %d-%d/%d", start, end, fileSize),
"content-length": fmt.Sprintf("%d", end-start+1),
"cache-control": "public, max-age=3600",
}); err != nil {
slog.Warn("failed to set partial content headers", slog.Any("error", err))
}
// Extract the requested range
rangeData := data[start : end+1]
slog.Debug("serving partial content",
slog.Int64("start", start),
slog.Int64("end", end),
slog.Int64("total", fileSize),
slog.Int("chunk_size", len(rangeData)))
return &httpbody.HttpBody{
ContentType: contentType,
Data: rangeData,
}, nil
} }
// setResponseHeaders is a helper function to set gRPC response headers. func isValidMimeType(mimeType string) bool {
func setResponseHeaders(ctx context.Context, headers map[string]string) error { // Reject empty or excessively long MIME types
pairs := make([]string, 0, len(headers)*2) if mimeType == "" || len(mimeType) > 255 {
for key, value := range headers { return false
pairs = append(pairs, key, value)
} }
return grpc.SetHeader(ctx, metadata.Pairs(pairs...))
// MIME type must match the pattern: type/subtype
// Allow common characters in MIME types per RFC 2045
matched, _ := regexp.MatchString(`^[a-zA-Z0-9][a-zA-Z0-9!#$&^_.+-]{0,126}/[a-zA-Z0-9][a-zA-Z0-9!#$&^_.+-]{0,126}$`, mimeType)
return matched
} }

View File

@ -1,91 +0,0 @@
package v1
import (
"fmt"
"strings"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/pkg/errors"
"github.com/usememos/memos/internal/util"
)
const (
// issuer is the issuer of the jwt token.
Issuer = "memos"
// Signing key section. For now, this is only used for signing, not for verifying since we only
// have 1 version. But it will be used to maintain backward compatibility if we change the signing mechanism.
KeyID = "v1"
// AccessTokenAudienceName is the audience name of the access token.
AccessTokenAudienceName = "user.access-token"
// SessionSlidingDuration is the sliding expiration duration for user sessions (2 weeks).
// Sessions are considered valid if last_accessed_time + SessionSlidingDuration > current_time.
SessionSlidingDuration = 14 * 24 * time.Hour
// SessionCookieName is the cookie name of user session ID.
SessionCookieName = "user_session"
)
type ClaimsMessage struct {
Name string `json:"name"`
jwt.RegisteredClaims
}
// GenerateAccessToken generates an access token.
func GenerateAccessToken(username string, userID int32, expirationTime time.Time, secret []byte) (string, error) {
return generateToken(username, userID, AccessTokenAudienceName, expirationTime, secret)
}
// generateToken generates a jwt token.
func generateToken(username string, userID int32, audience string, expirationTime time.Time, secret []byte) (string, error) {
registeredClaims := jwt.RegisteredClaims{
Issuer: Issuer,
Audience: jwt.ClaimStrings{audience},
IssuedAt: jwt.NewNumericDate(time.Now()),
Subject: fmt.Sprint(userID),
}
if !expirationTime.IsZero() {
registeredClaims.ExpiresAt = jwt.NewNumericDate(expirationTime)
}
// Declare the token with the HS256 algorithm used for signing, and the claims.
token := jwt.NewWithClaims(jwt.SigningMethodHS256, &ClaimsMessage{
Name: username,
RegisteredClaims: registeredClaims,
})
token.Header["kid"] = KeyID
// Create the JWT string.
tokenString, err := token.SignedString(secret)
if err != nil {
return "", err
}
return tokenString, nil
}
// GenerateSessionID generates a unique session ID using UUIDv4.
func GenerateSessionID() (string, error) {
return util.GenUUID(), nil
}
// BuildSessionCookieValue builds the session cookie value in format {userID}-{sessionID}.
func BuildSessionCookieValue(userID int32, sessionID string) string {
return fmt.Sprintf("%d-%s", userID, sessionID)
}
// ParseSessionCookieValue parses the session cookie value to extract userID and sessionID.
func ParseSessionCookieValue(cookieValue string) (int32, string, error) {
parts := strings.SplitN(cookieValue, "-", 2)
if len(parts) != 2 {
return 0, "", errors.New("invalid session cookie format")
}
userID, err := util.ConvertStringToInt32(parts[0])
if err != nil {
return 0, "", errors.Errorf("invalid user ID in session cookie: %v", err)
}
return userID, parts[1], nil
}

View File

@ -10,7 +10,6 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"google.golang.org/grpc"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
@ -22,6 +21,7 @@ import (
"github.com/usememos/memos/plugin/idp/oauth2" "github.com/usememos/memos/plugin/idp/oauth2"
v1pb "github.com/usememos/memos/proto/gen/api/v1" v1pb "github.com/usememos/memos/proto/gen/api/v1"
storepb "github.com/usememos/memos/proto/gen/store" storepb "github.com/usememos/memos/proto/gen/store"
"github.com/usememos/memos/server/auth"
"github.com/usememos/memos/store" "github.com/usememos/memos/store"
) )
@ -29,6 +29,15 @@ const (
unmatchedUsernameAndPasswordError = "unmatched username and password" unmatchedUsernameAndPasswordError = "unmatched username and password"
) )
// GetCurrentSession retrieves the current authenticated session information.
//
// This endpoint is used to:
// - Check if a user is currently authenticated
// - Get the current user's information
// - Retrieve the last accessed time of the session
//
// Authentication: Required (session cookie or access token)
// Returns: User information and last accessed timestamp.
func (s *APIV1Service) GetCurrentSession(ctx context.Context, _ *v1pb.GetCurrentSessionRequest) (*v1pb.GetCurrentSessionResponse, error) { func (s *APIV1Service) GetCurrentSession(ctx context.Context, _ *v1pb.GetCurrentSessionRequest) (*v1pb.GetCurrentSessionResponse, error) {
user, err := s.GetCurrentUser(ctx) user, err := s.GetCurrentUser(ctx)
if err != nil { if err != nil {
@ -44,7 +53,7 @@ func (s *APIV1Service) GetCurrentSession(ctx context.Context, _ *v1pb.GetCurrent
var lastAccessedAt *timestamppb.Timestamp var lastAccessedAt *timestamppb.Timestamp
// Update session last accessed time if we have a session ID and get the current session info // Update session last accessed time if we have a session ID and get the current session info
if sessionID, ok := ctx.Value(sessionIDContextKey).(string); ok && sessionID != "" { if sessionID := auth.GetSessionID(ctx); sessionID != "" {
now := timestamppb.Now() now := timestamppb.Now()
if err := s.Store.UpdateUserSessionLastAccessed(ctx, user.ID, sessionID, now); err != nil { if err := s.Store.UpdateUserSessionLastAccessed(ctx, user.ID, sessionID, now); err != nil {
// Log error but don't fail the request // Log error but don't fail the request
@ -59,8 +68,23 @@ func (s *APIV1Service) GetCurrentSession(ctx context.Context, _ *v1pb.GetCurrent
}, nil }, nil
} }
// CreateSession authenticates a user and establishes a new session.
//
// This endpoint supports two authentication methods:
// 1. Password-based authentication (username + password)
// 2. SSO authentication (OAuth2 authorization code)
//
// On successful authentication:
// - A session cookie is set for web browsers (cookie: user_session={userID}-{sessionID})
// - Session information is stored including client details (IP, user agent, device type)
// - Sessions use sliding expiration: 14 days from last access
//
// Authentication: Not required (public endpoint)
// Returns: Authenticated user information and last accessed timestamp.
func (s *APIV1Service) CreateSession(ctx context.Context, request *v1pb.CreateSessionRequest) (*v1pb.CreateSessionResponse, error) { func (s *APIV1Service) CreateSession(ctx context.Context, request *v1pb.CreateSessionRequest) (*v1pb.CreateSessionResponse, error) {
var existingUser *store.User var existingUser *store.User
// Authentication Method 1: Password-based authentication
if passwordCredentials := request.GetPasswordCredentials(); passwordCredentials != nil { if passwordCredentials := request.GetPasswordCredentials(); passwordCredentials != nil {
user, err := s.Store.GetUser(ctx, &store.FindUser{ user, err := s.Store.GetUser(ctx, &store.FindUser{
Username: &passwordCredentials.Username, Username: &passwordCredentials.Username,
@ -75,16 +99,17 @@ func (s *APIV1Service) CreateSession(ctx context.Context, request *v1pb.CreateSe
if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(passwordCredentials.Password)); err != nil { if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(passwordCredentials.Password)); err != nil {
return nil, status.Errorf(codes.InvalidArgument, unmatchedUsernameAndPasswordError) return nil, status.Errorf(codes.InvalidArgument, unmatchedUsernameAndPasswordError)
} }
workspaceGeneralSetting, err := s.Store.GetWorkspaceGeneralSetting(ctx) instanceGeneralSetting, err := s.Store.GetInstanceGeneralSetting(ctx)
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get workspace general setting, error: %v", err) return nil, status.Errorf(codes.Internal, "failed to get instance general setting, error: %v", err)
} }
// Check if the password auth in is allowed. // Check if the password auth in is allowed.
if workspaceGeneralSetting.DisallowPasswordAuth && user.Role == store.RoleUser { if instanceGeneralSetting.DisallowPasswordAuth && user.Role == store.RoleUser {
return nil, status.Errorf(codes.PermissionDenied, "password signin is not allowed") return nil, status.Errorf(codes.PermissionDenied, "password signin is not allowed")
} }
existingUser = user existingUser = user
} else if ssoCredentials := request.GetSsoCredentials(); ssoCredentials != nil { } else if ssoCredentials := request.GetSsoCredentials(); ssoCredentials != nil {
// Authentication Method 2: SSO (OAuth2) authentication
identityProvider, err := s.Store.GetIdentityProvider(ctx, &store.FindIdentityProvider{ identityProvider, err := s.Store.GetIdentityProvider(ctx, &store.FindIdentityProvider{
ID: &ssoCredentials.IdpId, ID: &ssoCredentials.IdpId,
}) })
@ -101,7 +126,8 @@ func (s *APIV1Service) CreateSession(ctx context.Context, request *v1pb.CreateSe
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to create oauth2 identity provider, error: %v", err) return nil, status.Errorf(codes.Internal, "failed to create oauth2 identity provider, error: %v", err)
} }
token, err := oauth2IdentityProvider.ExchangeToken(ctx, ssoCredentials.RedirectUri, ssoCredentials.Code) // Pass code_verifier for PKCE support (empty string if not provided for backward compatibility)
token, err := oauth2IdentityProvider.ExchangeToken(ctx, ssoCredentials.RedirectUri, ssoCredentials.Code, ssoCredentials.CodeVerifier)
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to exchange token, error: %v", err) return nil, status.Errorf(codes.Internal, "failed to exchange token, error: %v", err)
} }
@ -130,11 +156,11 @@ func (s *APIV1Service) CreateSession(ctx context.Context, request *v1pb.CreateSe
} }
if user == nil { if user == nil {
// Check if the user is allowed to sign up. // Check if the user is allowed to sign up.
workspaceGeneralSetting, err := s.Store.GetWorkspaceGeneralSetting(ctx) instanceGeneralSetting, err := s.Store.GetInstanceGeneralSetting(ctx)
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to get workspace general setting, error: %v", err) return nil, status.Errorf(codes.Internal, "failed to get instance general setting, error: %v", err)
} }
if workspaceGeneralSetting.DisallowUserRegistration { if instanceGeneralSetting.DisallowUserRegistration {
return nil, status.Errorf(codes.PermissionDenied, "user registration is not allowed") return nil, status.Errorf(codes.PermissionDenied, "user registration is not allowed")
} }
@ -183,12 +209,19 @@ func (s *APIV1Service) CreateSession(ctx context.Context, request *v1pb.CreateSe
}, nil }, nil
} }
// doSignIn performs the actual sign-in operation by creating a session and setting the cookie.
//
// This function:
// 1. Generates a unique session ID (UUID)
// 2. Tracks the session in user settings with client information
// 3. Sets a session cookie in the format: {userID}-{sessionID}
// 4. Configures cookie security settings (HttpOnly, Secure, SameSite)
//
// Cookie lifetime is 100 years, but actual session validity is controlled by
// sliding expiration (14 days from last access) checked during authentication.
func (s *APIV1Service) doSignIn(ctx context.Context, user *store.User, expireTime time.Time) error { func (s *APIV1Service) doSignIn(ctx context.Context, user *store.User, expireTime time.Time) error {
// Generate unique session ID for web use // Generate unique session ID for web use
sessionID, err := GenerateSessionID() sessionID := auth.GenerateSessionID()
if err != nil {
return status.Errorf(codes.Internal, "failed to generate session ID, error: %v", err)
}
// Track session in user settings // Track session in user settings
if err := s.trackUserSession(ctx, user.ID, sessionID); err != nil { if err := s.trackUserSession(ctx, user.ID, sessionID); err != nil {
@ -197,21 +230,26 @@ func (s *APIV1Service) doSignIn(ctx context.Context, user *store.User, expireTim
slog.Error("failed to track user session", "error", err) slog.Error("failed to track user session", "error", err)
} }
// Set session cookie for web use (format: userID-sessionID) // Set session cookie for web use
sessionCookieValue := BuildSessionCookieValue(user.ID, sessionID) sessionCookie, err := s.buildSessionCookie(ctx, sessionID, expireTime)
sessionCookie, err := s.buildSessionCookie(ctx, sessionCookieValue, expireTime)
if err != nil { if err != nil {
return status.Errorf(codes.Internal, "failed to build session cookie, error: %v", err) return status.Errorf(codes.Internal, "failed to build session cookie, error: %v", err)
} }
if err := grpc.SetHeader(ctx, metadata.New(map[string]string{ if err := SetResponseHeader(ctx, "Set-Cookie", sessionCookie); err != nil {
"Set-Cookie": sessionCookie, return status.Errorf(codes.Internal, "failed to set response header, error: %v", err)
})); err != nil {
return status.Errorf(codes.Internal, "failed to set grpc header, error: %v", err)
} }
return nil return nil
} }
// DeleteSession terminates the current user session (logout).
//
// This endpoint:
// 1. Removes the session from the user's sessions list in the database
// 2. Clears the session cookie by setting it to expire immediately
//
// Authentication: Required (session cookie or access token)
// Returns: Empty response on success.
func (s *APIV1Service) DeleteSession(ctx context.Context, _ *v1pb.DeleteSessionRequest) (*emptypb.Empty, error) { func (s *APIV1Service) DeleteSession(ctx context.Context, _ *v1pb.DeleteSessionRequest) (*emptypb.Empty, error) {
user, err := s.GetCurrentUser(ctx) user, err := s.GetCurrentUser(ctx)
if err != nil { if err != nil {
@ -222,7 +260,7 @@ func (s *APIV1Service) DeleteSession(ctx context.Context, _ *v1pb.DeleteSessionR
} }
// Check if we have a session ID (from cookie-based auth) // Check if we have a session ID (from cookie-based auth)
if sessionID, ok := ctx.Value(sessionIDContextKey).(string); ok && sessionID != "" { if sessionID := auth.GetSessionID(ctx); sessionID != "" {
// Remove session from user settings // Remove session from user settings
if err := s.Store.RemoveUserSession(ctx, user.ID, sessionID); err != nil { if err := s.Store.RemoveUserSession(ctx, user.ID, sessionID); err != nil {
slog.Error("failed to remove user session", "error", err) slog.Error("failed to remove user session", "error", err)
@ -242,18 +280,16 @@ func (s *APIV1Service) clearAuthCookies(ctx context.Context) error {
return errors.Wrap(err, "failed to build session cookie") return errors.Wrap(err, "failed to build session cookie")
} }
// Set both cookies in the response // Set cookie in the response
if err := grpc.SetHeader(ctx, metadata.New(map[string]string{ if err := SetResponseHeader(ctx, "Set-Cookie", sessionCookie); err != nil {
"Set-Cookie": sessionCookie, return errors.Wrap(err, "failed to set response header")
})); err != nil {
return errors.Wrap(err, "failed to set grpc header")
} }
return nil return nil
} }
func (*APIV1Service) buildSessionCookie(ctx context.Context, sessionCookieValue string, expireTime time.Time) (string, error) { func (*APIV1Service) buildSessionCookie(ctx context.Context, sessionCookieValue string, expireTime time.Time) (string, error) {
attrs := []string{ attrs := []string{
fmt.Sprintf("%s=%s", SessionCookieName, sessionCookieValue), fmt.Sprintf("%s=%s", auth.SessionCookieName, sessionCookieValue),
"Path=/", "Path=/",
"HttpOnly", "HttpOnly",
} }
@ -263,15 +299,18 @@ func (*APIV1Service) buildSessionCookie(ctx context.Context, sessionCookieValue
attrs = append(attrs, "Expires="+expireTime.Format(time.RFC1123)) attrs = append(attrs, "Expires="+expireTime.Format(time.RFC1123))
} }
md, ok := metadata.FromIncomingContext(ctx) // Try to determine if the request is HTTPS by checking the origin header
if !ok { // Default to non-HTTPS (Strict SameSite) if metadata is not available
return "", errors.New("failed to get metadata from context") isHTTPS := false
if md, ok := metadata.FromIncomingContext(ctx); ok {
for _, v := range md.Get("origin") {
if strings.HasPrefix(v, "https://") {
isHTTPS = true
break
}
}
} }
var origin string
for _, v := range md.Get("origin") {
origin = v
}
isHTTPS := strings.HasPrefix(origin, "https://")
if isHTTPS { if isHTTPS {
attrs = append(attrs, "SameSite=None") attrs = append(attrs, "SameSite=None")
attrs = append(attrs, "Secure") attrs = append(attrs, "Secure")
@ -282,8 +321,8 @@ func (*APIV1Service) buildSessionCookie(ctx context.Context, sessionCookieValue
} }
func (s *APIV1Service) GetCurrentUser(ctx context.Context) (*store.User, error) { func (s *APIV1Service) GetCurrentUser(ctx context.Context) (*store.User, error) {
userID, ok := ctx.Value(userIDContextKey).(int32) userID := auth.GetUserID(ctx)
if !ok { if userID == 0 {
return nil, nil return nil, nil
} }
user, err := s.Store.GetUser(ctx, &store.FindUser{ user, err := s.Store.GetUser(ctx, &store.FindUser{
@ -298,7 +337,13 @@ func (s *APIV1Service) GetCurrentUser(ctx context.Context) (*store.User, error)
return user, nil return user, nil
} }
// Helper function to track user session for session management. // trackUserSession creates a new session record in the user's settings.
//
// Session information includes:
// - session_id: Unique UUID for this session
// - create_time: When the session was created
// - last_accessed_time: When the session was last used (for sliding expiration)
// - client_info: Device details (user agent, IP, device type, OS, browser).
func (s *APIV1Service) trackUserSession(ctx context.Context, userID int32, sessionID string) error { func (s *APIV1Service) trackUserSession(ctx context.Context, userID int32, sessionID string) error {
// Extract client information from the context // Extract client information from the context
clientInfo := s.extractClientInfo(ctx) clientInfo := s.extractClientInfo(ctx)
@ -313,19 +358,19 @@ func (s *APIV1Service) trackUserSession(ctx context.Context, userID int32, sessi
return s.Store.AddUserSession(ctx, userID, session) return s.Store.AddUserSession(ctx, userID, session)
} }
// Helper function to extract client information from the gRPC context.
// extractClientInfo extracts comprehensive client information from the request context. // extractClientInfo extracts comprehensive client information from the request context.
// This includes user agent parsing to determine device type, operating system, browser,
// and IP address extraction. This information is used to provide detailed session
// tracking and management capabilities in the web UI.
// //
// Fields populated: // This function parses metadata from the gRPC context to extract:
// - UserAgent: Raw user agent string // - User Agent: Raw user agent string for detailed parsing
// - IpAddress: Client IP (from X-Forwarded-For or X-Real-IP headers) // - IP Address: Client IP from X-Forwarded-For or X-Real-IP headers
// - DeviceType: "mobile", "tablet", or "desktop" // - Device Type: "mobile", "tablet", or "desktop" (parsed from user agent)
// - Os: Operating system name and version (e.g., "iOS 17.1", "Windows 10/11") // - Operating System: OS name and version (e.g., "iOS 17.1", "Windows 10/11")
// - Browser: Browser name and version (e.g., "Chrome 120.0.0.0") // - Browser: Browser name and version (e.g., "Chrome 120.0.0.0")
// - Country: Geographic location (TODO: implement with GeoIP service). //
// This information enables users to:
// - See all active sessions with device details
// - Identify suspicious login attempts
// - Revoke specific sessions from unknown devices.
func (s *APIV1Service) extractClientInfo(ctx context.Context) *storepb.SessionsUserSetting_ClientInfo { func (s *APIV1Service) extractClientInfo(ctx context.Context) *storepb.SessionsUserSetting_ClientInfo {
clientInfo := &storepb.SessionsUserSetting_ClientInfo{} clientInfo := &storepb.SessionsUserSetting_ClientInfo{}
@ -351,6 +396,14 @@ func (s *APIV1Service) extractClientInfo(ctx context.Context) *storepb.SessionsU
} }
// parseUserAgent extracts device type, OS, and browser information from user agent string. // parseUserAgent extracts device type, OS, and browser information from user agent string.
//
// Detection logic:
// - Device Type: Checks for keywords like "mobile", "tablet", "ipad"
// - OS: Pattern matches for iOS, Android, Windows, macOS, Linux, Chrome OS
// - Browser: Identifies Edge, Chrome, Firefox, Safari, Opera
//
// Note: This is a simplified parser. For production use with high accuracy requirements,
// consider using a dedicated user agent parsing library.
func (*APIV1Service) parseUserAgent(userAgent string, clientInfo *storepb.SessionsUserSetting_ClientInfo) { func (*APIV1Service) parseUserAgent(userAgent string, clientInfo *storepb.SessionsUserSetting_ClientInfo) {
if userAgent == "" { if userAgent == "" {
return return

View File

@ -0,0 +1,80 @@
package v1
import (
"net/http"
"connectrpc.com/connect"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/usememos/memos/proto/gen/api/v1/apiv1connect"
)
// ConnectServiceHandler wraps APIV1Service to implement Connect handler interfaces.
// It adapts the existing gRPC service implementations to work with Connect's
// request/response wrapper types.
//
// This wrapper pattern allows us to:
// - Reuse existing gRPC service implementations
// - Support both native gRPC and Connect protocols
// - Maintain a single source of truth for business logic.
type ConnectServiceHandler struct {
*APIV1Service
}
// NewConnectServiceHandler creates a new Connect service handler.
func NewConnectServiceHandler(svc *APIV1Service) *ConnectServiceHandler {
return &ConnectServiceHandler{APIV1Service: svc}
}
// RegisterConnectHandlers registers all Connect service handlers on the given mux.
func (s *ConnectServiceHandler) RegisterConnectHandlers(mux *http.ServeMux, opts ...connect.HandlerOption) {
// Register all service handlers
handlers := []struct {
path string
handler http.Handler
}{
wrap(apiv1connect.NewInstanceServiceHandler(s, opts...)),
wrap(apiv1connect.NewAuthServiceHandler(s, opts...)),
wrap(apiv1connect.NewUserServiceHandler(s, opts...)),
wrap(apiv1connect.NewMemoServiceHandler(s, opts...)),
wrap(apiv1connect.NewAttachmentServiceHandler(s, opts...)),
wrap(apiv1connect.NewShortcutServiceHandler(s, opts...)),
wrap(apiv1connect.NewActivityServiceHandler(s, opts...)),
wrap(apiv1connect.NewIdentityProviderServiceHandler(s, opts...)),
}
for _, h := range handlers {
mux.Handle(h.path, h.handler)
}
}
// wrap converts (path, handler) return value to a struct for cleaner iteration.
func wrap(path string, handler http.Handler) struct {
path string
handler http.Handler
} {
return struct {
path string
handler http.Handler
}{path, handler}
}
// convertGRPCError converts gRPC status errors to Connect errors.
// This preserves the error code semantics between the two protocols.
func convertGRPCError(err error) error {
if err == nil {
return nil
}
if st, ok := status.FromError(err); ok {
return connect.NewError(grpcCodeToConnectCode(st.Code()), err)
}
return connect.NewError(connect.CodeInternal, err)
}
// grpcCodeToConnectCode converts gRPC status codes to Connect error codes.
// gRPC and Connect use the same error code semantics, so this is a direct cast.
// See: https://connectrpc.com/docs/protocol/#error-codes
func grpcCodeToConnectCode(code codes.Code) connect.Code {
return connect.Code(code)
}

View File

@ -0,0 +1,216 @@
package v1
import (
"context"
"errors"
"fmt"
"log/slog"
"runtime/debug"
"connectrpc.com/connect"
pkgerrors "github.com/pkg/errors"
"google.golang.org/grpc/metadata"
"github.com/usememos/memos/server/auth"
"github.com/usememos/memos/store"
)
// MetadataInterceptor converts Connect HTTP headers to gRPC metadata.
//
// This ensures service methods can use metadata.FromIncomingContext() to access
// headers like User-Agent, X-Forwarded-For, etc., regardless of whether the
// request came via Connect RPC or gRPC-Gateway.
type MetadataInterceptor struct{}
// NewMetadataInterceptor creates a new metadata interceptor.
func NewMetadataInterceptor() *MetadataInterceptor {
return &MetadataInterceptor{}
}
func (*MetadataInterceptor) WrapUnary(next connect.UnaryFunc) connect.UnaryFunc {
return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
// Convert HTTP headers to gRPC metadata
header := req.Header()
md := metadata.MD{}
// Copy important headers for client info extraction
if ua := header.Get("User-Agent"); ua != "" {
md.Set("user-agent", ua)
}
if xff := header.Get("X-Forwarded-For"); xff != "" {
md.Set("x-forwarded-for", xff)
}
if xri := header.Get("X-Real-Ip"); xri != "" {
md.Set("x-real-ip", xri)
}
// Set metadata in context so services can use metadata.FromIncomingContext()
ctx = metadata.NewIncomingContext(ctx, md)
return next(ctx, req)
}
}
func (*MetadataInterceptor) WrapStreamingClient(next connect.StreamingClientFunc) connect.StreamingClientFunc {
return next
}
func (*MetadataInterceptor) WrapStreamingHandler(next connect.StreamingHandlerFunc) connect.StreamingHandlerFunc {
return next
}
// LoggingInterceptor logs Connect RPC requests with appropriate log levels.
//
// Log levels:
// - INFO: Successful requests and expected client errors (not found, permission denied, etc.)
// - ERROR: Server errors (internal, unavailable, etc.)
type LoggingInterceptor struct {
logStacktrace bool
}
// NewLoggingInterceptor creates a new logging interceptor.
func NewLoggingInterceptor(logStacktrace bool) *LoggingInterceptor {
return &LoggingInterceptor{logStacktrace: logStacktrace}
}
func (in *LoggingInterceptor) WrapUnary(next connect.UnaryFunc) connect.UnaryFunc {
return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
resp, err := next(ctx, req)
in.log(req.Spec().Procedure, err)
return resp, err
}
}
func (*LoggingInterceptor) WrapStreamingClient(next connect.StreamingClientFunc) connect.StreamingClientFunc {
return next // No-op for server-side interceptor
}
func (*LoggingInterceptor) WrapStreamingHandler(next connect.StreamingHandlerFunc) connect.StreamingHandlerFunc {
return next // Streaming not used in this service
}
func (in *LoggingInterceptor) log(procedure string, err error) {
level, msg := in.classifyError(err)
attrs := []slog.Attr{slog.String("method", procedure)}
if err != nil {
attrs = append(attrs, slog.String("error", err.Error()))
if in.logStacktrace {
attrs = append(attrs, slog.String("stacktrace", fmt.Sprintf("%+v", err)))
}
}
slog.LogAttrs(context.Background(), level, msg, attrs...)
}
func (*LoggingInterceptor) classifyError(err error) (slog.Level, string) {
if err == nil {
return slog.LevelInfo, "OK"
}
var connectErr *connect.Error
if !pkgerrors.As(err, &connectErr) {
return slog.LevelError, "unknown error"
}
// Client errors (expected, log at INFO)
switch connectErr.Code() {
case connect.CodeCanceled,
connect.CodeInvalidArgument,
connect.CodeNotFound,
connect.CodeAlreadyExists,
connect.CodePermissionDenied,
connect.CodeUnauthenticated,
connect.CodeResourceExhausted,
connect.CodeFailedPrecondition,
connect.CodeAborted,
connect.CodeOutOfRange:
return slog.LevelInfo, "client error"
default:
// Server errors
return slog.LevelError, "server error"
}
}
// RecoveryInterceptor recovers from panics in Connect handlers and returns an internal error.
type RecoveryInterceptor struct {
logStacktrace bool
}
// NewRecoveryInterceptor creates a new recovery interceptor.
func NewRecoveryInterceptor(logStacktrace bool) *RecoveryInterceptor {
return &RecoveryInterceptor{logStacktrace: logStacktrace}
}
func (in *RecoveryInterceptor) WrapUnary(next connect.UnaryFunc) connect.UnaryFunc {
return func(ctx context.Context, req connect.AnyRequest) (resp connect.AnyResponse, err error) {
defer func() {
if r := recover(); r != nil {
in.logPanic(req.Spec().Procedure, r)
err = connect.NewError(connect.CodeInternal, pkgerrors.New("internal server error"))
}
}()
return next(ctx, req)
}
}
func (*RecoveryInterceptor) WrapStreamingClient(next connect.StreamingClientFunc) connect.StreamingClientFunc {
return next
}
func (*RecoveryInterceptor) WrapStreamingHandler(next connect.StreamingHandlerFunc) connect.StreamingHandlerFunc {
return next
}
func (in *RecoveryInterceptor) logPanic(procedure string, panicValue any) {
attrs := []slog.Attr{
slog.String("method", procedure),
slog.Any("panic", panicValue),
}
if in.logStacktrace {
attrs = append(attrs, slog.String("stacktrace", string(debug.Stack())))
}
slog.LogAttrs(context.Background(), slog.LevelError, "panic recovered in Connect handler", attrs...)
}
// AuthInterceptor handles authentication for Connect handlers.
//
// It enforces authentication for all endpoints except those listed in PublicMethods.
// Role-based authorization (admin checks) remains in the service layer.
type AuthInterceptor struct {
authenticator *auth.Authenticator
}
// NewAuthInterceptor creates a new auth interceptor.
func NewAuthInterceptor(store *store.Store, secret string) *AuthInterceptor {
return &AuthInterceptor{
authenticator: auth.NewAuthenticator(store, secret),
}
}
func (in *AuthInterceptor) WrapUnary(next connect.UnaryFunc) connect.UnaryFunc {
return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
header := req.Header()
sessionCookie := auth.ExtractSessionCookieFromHeader(header.Get("Cookie"))
authHeader := header.Get("Authorization")
result := in.authenticator.Authenticate(ctx, sessionCookie, authHeader)
// Enforce authentication for non-public methods
if result == nil && !IsPublicMethod(req.Spec().Procedure) {
return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("authentication required"))
}
// Set user in context (may be nil for public endpoints)
if result != nil {
ctx = auth.SetUserInContext(ctx, result.User, result.SessionID, result.AccessToken)
}
return next(ctx, req)
}
}
func (*AuthInterceptor) WrapStreamingClient(next connect.StreamingClientFunc) connect.StreamingClientFunc {
return next
}
func (*AuthInterceptor) WrapStreamingHandler(next connect.StreamingHandlerFunc) connect.StreamingHandlerFunc {
return next
}

View File

@ -0,0 +1,500 @@
package v1
import (
"context"
"connectrpc.com/connect"
"google.golang.org/protobuf/types/known/emptypb"
v1pb "github.com/usememos/memos/proto/gen/api/v1"
)
// This file contains all Connect service handler method implementations.
// Each method delegates to the underlying gRPC service implementation,
// converting between Connect and gRPC request/response types.
// InstanceService
func (s *ConnectServiceHandler) GetInstanceProfile(ctx context.Context, req *connect.Request[v1pb.GetInstanceProfileRequest]) (*connect.Response[v1pb.InstanceProfile], error) {
resp, err := s.APIV1Service.GetInstanceProfile(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) GetInstanceSetting(ctx context.Context, req *connect.Request[v1pb.GetInstanceSettingRequest]) (*connect.Response[v1pb.InstanceSetting], error) {
resp, err := s.APIV1Service.GetInstanceSetting(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) UpdateInstanceSetting(ctx context.Context, req *connect.Request[v1pb.UpdateInstanceSettingRequest]) (*connect.Response[v1pb.InstanceSetting], error) {
resp, err := s.APIV1Service.UpdateInstanceSetting(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
// AuthService
//
// Auth service methods need special handling for response headers (cookies).
// We use connectWithHeaderCarrier helper to inject a header carrier into the context,
// which allows the service to set headers in a protocol-agnostic way.
func (s *ConnectServiceHandler) GetCurrentSession(ctx context.Context, req *connect.Request[v1pb.GetCurrentSessionRequest]) (*connect.Response[v1pb.GetCurrentSessionResponse], error) {
return connectWithHeaderCarrier(ctx, func(ctx context.Context) (*v1pb.GetCurrentSessionResponse, error) {
return s.APIV1Service.GetCurrentSession(ctx, req.Msg)
})
}
func (s *ConnectServiceHandler) CreateSession(ctx context.Context, req *connect.Request[v1pb.CreateSessionRequest]) (*connect.Response[v1pb.CreateSessionResponse], error) {
return connectWithHeaderCarrier(ctx, func(ctx context.Context) (*v1pb.CreateSessionResponse, error) {
return s.APIV1Service.CreateSession(ctx, req.Msg)
})
}
func (s *ConnectServiceHandler) DeleteSession(ctx context.Context, req *connect.Request[v1pb.DeleteSessionRequest]) (*connect.Response[emptypb.Empty], error) {
return connectWithHeaderCarrier(ctx, func(ctx context.Context) (*emptypb.Empty, error) {
return s.APIV1Service.DeleteSession(ctx, req.Msg)
})
}
// UserService
func (s *ConnectServiceHandler) ListUsers(ctx context.Context, req *connect.Request[v1pb.ListUsersRequest]) (*connect.Response[v1pb.ListUsersResponse], error) {
resp, err := s.APIV1Service.ListUsers(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) GetUser(ctx context.Context, req *connect.Request[v1pb.GetUserRequest]) (*connect.Response[v1pb.User], error) {
resp, err := s.APIV1Service.GetUser(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) CreateUser(ctx context.Context, req *connect.Request[v1pb.CreateUserRequest]) (*connect.Response[v1pb.User], error) {
resp, err := s.APIV1Service.CreateUser(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) UpdateUser(ctx context.Context, req *connect.Request[v1pb.UpdateUserRequest]) (*connect.Response[v1pb.User], error) {
resp, err := s.APIV1Service.UpdateUser(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) DeleteUser(ctx context.Context, req *connect.Request[v1pb.DeleteUserRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.DeleteUser(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListAllUserStats(ctx context.Context, req *connect.Request[v1pb.ListAllUserStatsRequest]) (*connect.Response[v1pb.ListAllUserStatsResponse], error) {
resp, err := s.APIV1Service.ListAllUserStats(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) GetUserStats(ctx context.Context, req *connect.Request[v1pb.GetUserStatsRequest]) (*connect.Response[v1pb.UserStats], error) {
resp, err := s.APIV1Service.GetUserStats(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) GetUserSetting(ctx context.Context, req *connect.Request[v1pb.GetUserSettingRequest]) (*connect.Response[v1pb.UserSetting], error) {
resp, err := s.APIV1Service.GetUserSetting(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) UpdateUserSetting(ctx context.Context, req *connect.Request[v1pb.UpdateUserSettingRequest]) (*connect.Response[v1pb.UserSetting], error) {
resp, err := s.APIV1Service.UpdateUserSetting(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListUserSettings(ctx context.Context, req *connect.Request[v1pb.ListUserSettingsRequest]) (*connect.Response[v1pb.ListUserSettingsResponse], error) {
resp, err := s.APIV1Service.ListUserSettings(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListUserAccessTokens(ctx context.Context, req *connect.Request[v1pb.ListUserAccessTokensRequest]) (*connect.Response[v1pb.ListUserAccessTokensResponse], error) {
resp, err := s.APIV1Service.ListUserAccessTokens(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) CreateUserAccessToken(ctx context.Context, req *connect.Request[v1pb.CreateUserAccessTokenRequest]) (*connect.Response[v1pb.UserAccessToken], error) {
resp, err := s.APIV1Service.CreateUserAccessToken(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) DeleteUserAccessToken(ctx context.Context, req *connect.Request[v1pb.DeleteUserAccessTokenRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.DeleteUserAccessToken(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListUserSessions(ctx context.Context, req *connect.Request[v1pb.ListUserSessionsRequest]) (*connect.Response[v1pb.ListUserSessionsResponse], error) {
resp, err := s.APIV1Service.ListUserSessions(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) RevokeUserSession(ctx context.Context, req *connect.Request[v1pb.RevokeUserSessionRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.RevokeUserSession(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListUserWebhooks(ctx context.Context, req *connect.Request[v1pb.ListUserWebhooksRequest]) (*connect.Response[v1pb.ListUserWebhooksResponse], error) {
resp, err := s.APIV1Service.ListUserWebhooks(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) CreateUserWebhook(ctx context.Context, req *connect.Request[v1pb.CreateUserWebhookRequest]) (*connect.Response[v1pb.UserWebhook], error) {
resp, err := s.APIV1Service.CreateUserWebhook(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) UpdateUserWebhook(ctx context.Context, req *connect.Request[v1pb.UpdateUserWebhookRequest]) (*connect.Response[v1pb.UserWebhook], error) {
resp, err := s.APIV1Service.UpdateUserWebhook(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) DeleteUserWebhook(ctx context.Context, req *connect.Request[v1pb.DeleteUserWebhookRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.DeleteUserWebhook(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListUserNotifications(ctx context.Context, req *connect.Request[v1pb.ListUserNotificationsRequest]) (*connect.Response[v1pb.ListUserNotificationsResponse], error) {
resp, err := s.APIV1Service.ListUserNotifications(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) UpdateUserNotification(ctx context.Context, req *connect.Request[v1pb.UpdateUserNotificationRequest]) (*connect.Response[v1pb.UserNotification], error) {
resp, err := s.APIV1Service.UpdateUserNotification(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) DeleteUserNotification(ctx context.Context, req *connect.Request[v1pb.DeleteUserNotificationRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.DeleteUserNotification(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
// MemoService
func (s *ConnectServiceHandler) CreateMemo(ctx context.Context, req *connect.Request[v1pb.CreateMemoRequest]) (*connect.Response[v1pb.Memo], error) {
resp, err := s.APIV1Service.CreateMemo(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListMemos(ctx context.Context, req *connect.Request[v1pb.ListMemosRequest]) (*connect.Response[v1pb.ListMemosResponse], error) {
resp, err := s.APIV1Service.ListMemos(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) GetMemo(ctx context.Context, req *connect.Request[v1pb.GetMemoRequest]) (*connect.Response[v1pb.Memo], error) {
resp, err := s.APIV1Service.GetMemo(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) UpdateMemo(ctx context.Context, req *connect.Request[v1pb.UpdateMemoRequest]) (*connect.Response[v1pb.Memo], error) {
resp, err := s.APIV1Service.UpdateMemo(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) DeleteMemo(ctx context.Context, req *connect.Request[v1pb.DeleteMemoRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.DeleteMemo(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) SetMemoAttachments(ctx context.Context, req *connect.Request[v1pb.SetMemoAttachmentsRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.SetMemoAttachments(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListMemoAttachments(ctx context.Context, req *connect.Request[v1pb.ListMemoAttachmentsRequest]) (*connect.Response[v1pb.ListMemoAttachmentsResponse], error) {
resp, err := s.APIV1Service.ListMemoAttachments(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) SetMemoRelations(ctx context.Context, req *connect.Request[v1pb.SetMemoRelationsRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.SetMemoRelations(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListMemoRelations(ctx context.Context, req *connect.Request[v1pb.ListMemoRelationsRequest]) (*connect.Response[v1pb.ListMemoRelationsResponse], error) {
resp, err := s.APIV1Service.ListMemoRelations(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) CreateMemoComment(ctx context.Context, req *connect.Request[v1pb.CreateMemoCommentRequest]) (*connect.Response[v1pb.Memo], error) {
resp, err := s.APIV1Service.CreateMemoComment(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListMemoComments(ctx context.Context, req *connect.Request[v1pb.ListMemoCommentsRequest]) (*connect.Response[v1pb.ListMemoCommentsResponse], error) {
resp, err := s.APIV1Service.ListMemoComments(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListMemoReactions(ctx context.Context, req *connect.Request[v1pb.ListMemoReactionsRequest]) (*connect.Response[v1pb.ListMemoReactionsResponse], error) {
resp, err := s.APIV1Service.ListMemoReactions(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) UpsertMemoReaction(ctx context.Context, req *connect.Request[v1pb.UpsertMemoReactionRequest]) (*connect.Response[v1pb.Reaction], error) {
resp, err := s.APIV1Service.UpsertMemoReaction(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) DeleteMemoReaction(ctx context.Context, req *connect.Request[v1pb.DeleteMemoReactionRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.DeleteMemoReaction(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
// AttachmentService
func (s *ConnectServiceHandler) CreateAttachment(ctx context.Context, req *connect.Request[v1pb.CreateAttachmentRequest]) (*connect.Response[v1pb.Attachment], error) {
resp, err := s.APIV1Service.CreateAttachment(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) ListAttachments(ctx context.Context, req *connect.Request[v1pb.ListAttachmentsRequest]) (*connect.Response[v1pb.ListAttachmentsResponse], error) {
resp, err := s.APIV1Service.ListAttachments(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) GetAttachment(ctx context.Context, req *connect.Request[v1pb.GetAttachmentRequest]) (*connect.Response[v1pb.Attachment], error) {
resp, err := s.APIV1Service.GetAttachment(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) UpdateAttachment(ctx context.Context, req *connect.Request[v1pb.UpdateAttachmentRequest]) (*connect.Response[v1pb.Attachment], error) {
resp, err := s.APIV1Service.UpdateAttachment(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) DeleteAttachment(ctx context.Context, req *connect.Request[v1pb.DeleteAttachmentRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.DeleteAttachment(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
// ShortcutService
func (s *ConnectServiceHandler) ListShortcuts(ctx context.Context, req *connect.Request[v1pb.ListShortcutsRequest]) (*connect.Response[v1pb.ListShortcutsResponse], error) {
resp, err := s.APIV1Service.ListShortcuts(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) GetShortcut(ctx context.Context, req *connect.Request[v1pb.GetShortcutRequest]) (*connect.Response[v1pb.Shortcut], error) {
resp, err := s.APIV1Service.GetShortcut(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) CreateShortcut(ctx context.Context, req *connect.Request[v1pb.CreateShortcutRequest]) (*connect.Response[v1pb.Shortcut], error) {
resp, err := s.APIV1Service.CreateShortcut(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) UpdateShortcut(ctx context.Context, req *connect.Request[v1pb.UpdateShortcutRequest]) (*connect.Response[v1pb.Shortcut], error) {
resp, err := s.APIV1Service.UpdateShortcut(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) DeleteShortcut(ctx context.Context, req *connect.Request[v1pb.DeleteShortcutRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.DeleteShortcut(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
// ActivityService
func (s *ConnectServiceHandler) ListActivities(ctx context.Context, req *connect.Request[v1pb.ListActivitiesRequest]) (*connect.Response[v1pb.ListActivitiesResponse], error) {
resp, err := s.APIV1Service.ListActivities(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) GetActivity(ctx context.Context, req *connect.Request[v1pb.GetActivityRequest]) (*connect.Response[v1pb.Activity], error) {
resp, err := s.APIV1Service.GetActivity(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
// IdentityProviderService
func (s *ConnectServiceHandler) ListIdentityProviders(ctx context.Context, req *connect.Request[v1pb.ListIdentityProvidersRequest]) (*connect.Response[v1pb.ListIdentityProvidersResponse], error) {
resp, err := s.APIV1Service.ListIdentityProviders(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) GetIdentityProvider(ctx context.Context, req *connect.Request[v1pb.GetIdentityProviderRequest]) (*connect.Response[v1pb.IdentityProvider], error) {
resp, err := s.APIV1Service.GetIdentityProvider(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) CreateIdentityProvider(ctx context.Context, req *connect.Request[v1pb.CreateIdentityProviderRequest]) (*connect.Response[v1pb.IdentityProvider], error) {
resp, err := s.APIV1Service.CreateIdentityProvider(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) UpdateIdentityProvider(ctx context.Context, req *connect.Request[v1pb.UpdateIdentityProviderRequest]) (*connect.Response[v1pb.IdentityProvider], error) {
resp, err := s.APIV1Service.UpdateIdentityProvider(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}
func (s *ConnectServiceHandler) DeleteIdentityProvider(ctx context.Context, req *connect.Request[v1pb.DeleteIdentityProviderRequest]) (*connect.Response[emptypb.Empty], error) {
resp, err := s.APIV1Service.DeleteIdentityProvider(ctx, req.Msg)
if err != nil {
return nil, convertGRPCError(err)
}
return connect.NewResponse(resp), nil
}

View File

@ -0,0 +1,124 @@
package v1
import (
"context"
"connectrpc.com/connect"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
// headerCarrierKey is the context key for storing headers to be set in the response.
type headerCarrierKey struct{}
// HeaderCarrier stores headers that need to be set in the response.
//
// Problem: The codebase supports two protocols simultaneously:
// - Native gRPC: Uses grpc.SetHeader() to set response headers
// - Connect-RPC: Uses connect.Response.Header().Set() to set response headers
//
// Solution: HeaderCarrier provides a protocol-agnostic way to set headers.
// - Service methods call SetResponseHeader() regardless of protocol
// - For gRPC requests: SetResponseHeader uses grpc.SetHeader directly
// - For Connect requests: SetResponseHeader stores headers in HeaderCarrier
// - Connect wrappers extract headers from HeaderCarrier and apply to response
//
// This allows service methods to work with both protocols without knowing which one is being used.
type HeaderCarrier struct {
headers map[string]string
}
// newHeaderCarrier creates a new header carrier.
func newHeaderCarrier() *HeaderCarrier {
return &HeaderCarrier{
headers: make(map[string]string),
}
}
// Set adds a header to the carrier.
func (h *HeaderCarrier) Set(key, value string) {
h.headers[key] = value
}
// Get retrieves a header from the carrier.
func (h *HeaderCarrier) Get(key string) string {
return h.headers[key]
}
// All returns all headers.
func (h *HeaderCarrier) All() map[string]string {
return h.headers
}
// WithHeaderCarrier adds a header carrier to the context.
func WithHeaderCarrier(ctx context.Context) context.Context {
return context.WithValue(ctx, headerCarrierKey{}, newHeaderCarrier())
}
// GetHeaderCarrier retrieves the header carrier from the context.
// Returns nil if no carrier is present.
func GetHeaderCarrier(ctx context.Context) *HeaderCarrier {
if carrier, ok := ctx.Value(headerCarrierKey{}).(*HeaderCarrier); ok {
return carrier
}
return nil
}
// SetResponseHeader sets a header in the response.
//
// This function works for both gRPC and Connect protocols:
// - For gRPC: Uses grpc.SetHeader to set headers in gRPC metadata
// - For Connect: Stores in HeaderCarrier for Connect wrapper to apply later
//
// The protocol is automatically detected based on whether a HeaderCarrier
// exists in the context (injected by Connect wrappers).
func SetResponseHeader(ctx context.Context, key, value string) error {
// Try Connect first (check if we have a header carrier)
if carrier := GetHeaderCarrier(ctx); carrier != nil {
carrier.Set(key, value)
return nil
}
// Fall back to gRPC
return grpc.SetHeader(ctx, metadata.New(map[string]string{
key: value,
}))
}
// connectWithHeaderCarrier is a helper for Connect service wrappers that need to set response headers.
//
// It injects a HeaderCarrier into the context, calls the service method,
// and applies any headers from the carrier to the Connect response.
//
// The generic parameter T is the non-pointer protobuf message type (e.g., v1pb.CreateSessionResponse),
// while fn returns *T (the pointer type) as is standard for protobuf messages.
//
// Usage in Connect wrappers:
//
// func (s *ConnectServiceHandler) CreateSession(ctx context.Context, req *connect.Request[v1pb.CreateSessionRequest]) (*connect.Response[v1pb.CreateSessionResponse], error) {
// return connectWithHeaderCarrier(ctx, func(ctx context.Context) (*v1pb.CreateSessionResponse, error) {
// return s.APIV1Service.CreateSession(ctx, req.Msg)
// })
// }
func connectWithHeaderCarrier[T any](ctx context.Context, fn func(context.Context) (*T, error)) (*connect.Response[T], error) {
// Inject header carrier for Connect protocol
ctx = WithHeaderCarrier(ctx)
// Call the service method
resp, err := fn(ctx)
if err != nil {
return nil, convertGRPCError(err)
}
// Create Connect response
connectResp := connect.NewResponse(resp)
// Apply any headers set via the header carrier
if carrier := GetHeaderCarrier(ctx); carrier != nil {
for key, value := range carrier.All() {
connectResp.Header().Set(key, value)
}
}
return connectResp, nil
}

Some files were not shown because too many files have changed in this diff Show More