Showing preview only (4,344K chars total). Download the full file or copy to clipboard to get everything.
Repository: usememos/memos
Branch: main
Commit: a7cabb7ce66f
Files: 743
Total size: 4.0 MB
Directory structure:
gitextract_vixwurpk/
├── .dockerignore
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ ├── config.yml
│ │ └── feature_request.yml
│ └── workflows/
│ ├── backend-tests.yml
│ ├── build-canary-image.yml
│ ├── demo-deploy.yml
│ ├── frontend-tests.yml
│ ├── proto-linter.yml
│ ├── release.yml
│ └── stale.yml
├── .gitignore
├── .golangci.yaml
├── AGENTS.md
├── CLAUDE.md
├── CODEOWNERS
├── LICENSE
├── README.md
├── SECURITY.md
├── go.mod
├── go.sum
├── internal/
│ ├── base/
│ │ ├── resource_name.go
│ │ └── resource_name_test.go
│ ├── profile/
│ │ └── profile.go
│ ├── util/
│ │ ├── util.go
│ │ └── util_test.go
│ └── version/
│ ├── version.go
│ └── version_test.go
├── plugin/
│ ├── cron/
│ │ ├── README.md
│ │ ├── chain.go
│ │ ├── chain_test.go
│ │ ├── constantdelay.go
│ │ ├── constantdelay_test.go
│ │ ├── cron.go
│ │ ├── cron_test.go
│ │ ├── logger.go
│ │ ├── option.go
│ │ ├── option_test.go
│ │ ├── parser.go
│ │ ├── parser_test.go
│ │ ├── spec.go
│ │ └── spec_test.go
│ ├── email/
│ │ ├── README.md
│ │ ├── client.go
│ │ ├── client_test.go
│ │ ├── config.go
│ │ ├── config_test.go
│ │ ├── doc.go
│ │ ├── email.go
│ │ ├── email_test.go
│ │ ├── message.go
│ │ └── message_test.go
│ ├── filter/
│ │ ├── MAINTENANCE.md
│ │ ├── README.md
│ │ ├── engine.go
│ │ ├── helpers.go
│ │ ├── ir.go
│ │ ├── parser.go
│ │ ├── render.go
│ │ └── schema.go
│ ├── httpgetter/
│ │ ├── html_meta.go
│ │ ├── html_meta_test.go
│ │ ├── http_getter.go
│ │ ├── image.go
│ │ └── util.go
│ ├── idp/
│ │ ├── idp.go
│ │ └── oauth2/
│ │ ├── oauth2.go
│ │ └── oauth2_test.go
│ ├── markdown/
│ │ ├── ast/
│ │ │ └── tag.go
│ │ ├── extensions/
│ │ │ └── tag.go
│ │ ├── markdown.go
│ │ ├── markdown_test.go
│ │ ├── parser/
│ │ │ ├── tag.go
│ │ │ └── tag_test.go
│ │ └── renderer/
│ │ ├── markdown_renderer.go
│ │ └── markdown_renderer_test.go
│ ├── scheduler/
│ │ ├── README.md
│ │ ├── doc.go
│ │ ├── example_test.go
│ │ ├── integration_test.go
│ │ ├── job.go
│ │ ├── job_test.go
│ │ ├── middleware.go
│ │ ├── middleware_test.go
│ │ ├── parser.go
│ │ ├── parser_test.go
│ │ ├── scheduler.go
│ │ └── scheduler_test.go
│ ├── storage/
│ │ └── s3/
│ │ └── s3.go
│ └── webhook/
│ ├── validate.go
│ ├── webhook.go
│ └── webhook_test.go
├── proto/
│ ├── README.md
│ ├── api/
│ │ └── v1/
│ │ ├── README.md
│ │ ├── attachment_service.proto
│ │ ├── auth_service.proto
│ │ ├── common.proto
│ │ ├── idp_service.proto
│ │ ├── instance_service.proto
│ │ ├── memo_service.proto
│ │ ├── shortcut_service.proto
│ │ └── user_service.proto
│ ├── buf.gen.yaml
│ ├── buf.yaml
│ ├── gen/
│ │ ├── api/
│ │ │ └── v1/
│ │ │ ├── apiv1connect/
│ │ │ │ ├── attachment_service.connect.go
│ │ │ │ ├── auth_service.connect.go
│ │ │ │ ├── idp_service.connect.go
│ │ │ │ ├── instance_service.connect.go
│ │ │ │ ├── memo_service.connect.go
│ │ │ │ ├── shortcut_service.connect.go
│ │ │ │ └── user_service.connect.go
│ │ │ ├── attachment_service.pb.go
│ │ │ ├── attachment_service.pb.gw.go
│ │ │ ├── attachment_service_grpc.pb.go
│ │ │ ├── auth_service.pb.go
│ │ │ ├── auth_service.pb.gw.go
│ │ │ ├── auth_service_grpc.pb.go
│ │ │ ├── common.pb.go
│ │ │ ├── idp_service.pb.go
│ │ │ ├── idp_service.pb.gw.go
│ │ │ ├── idp_service_grpc.pb.go
│ │ │ ├── instance_service.pb.go
│ │ │ ├── instance_service.pb.gw.go
│ │ │ ├── instance_service_grpc.pb.go
│ │ │ ├── memo_service.pb.go
│ │ │ ├── memo_service.pb.gw.go
│ │ │ ├── memo_service_grpc.pb.go
│ │ │ ├── shortcut_service.pb.go
│ │ │ ├── shortcut_service.pb.gw.go
│ │ │ ├── shortcut_service_grpc.pb.go
│ │ │ ├── user_service.pb.go
│ │ │ ├── user_service.pb.gw.go
│ │ │ └── user_service_grpc.pb.go
│ │ ├── openapi.yaml
│ │ └── store/
│ │ ├── attachment.pb.go
│ │ ├── idp.pb.go
│ │ ├── inbox.pb.go
│ │ ├── instance_setting.pb.go
│ │ ├── memo.pb.go
│ │ └── user_setting.pb.go
│ └── store/
│ ├── attachment.proto
│ ├── idp.proto
│ ├── inbox.proto
│ ├── instance_setting.proto
│ ├── memo.proto
│ └── user_setting.proto
├── scripts/
│ ├── Dockerfile
│ ├── build.sh
│ ├── compose.yaml
│ ├── entrypoint.sh
│ ├── entrypoint_test.sh
│ └── install.sh
├── server/
│ ├── auth/
│ │ ├── authenticator.go
│ │ ├── context.go
│ │ ├── extract.go
│ │ ├── token.go
│ │ └── token_test.go
│ ├── router/
│ │ ├── api/
│ │ │ └── v1/
│ │ │ ├── acl_config.go
│ │ │ ├── acl_config_test.go
│ │ │ ├── attachment_exif_test.go
│ │ │ ├── attachment_service.go
│ │ │ ├── auth_service.go
│ │ │ ├── auth_service_client_info_test.go
│ │ │ ├── common.go
│ │ │ ├── connect_handler.go
│ │ │ ├── connect_interceptors.go
│ │ │ ├── connect_services.go
│ │ │ ├── header_carrier.go
│ │ │ ├── health_service.go
│ │ │ ├── idp_service.go
│ │ │ ├── instance_service.go
│ │ │ ├── memo_attachment_service.go
│ │ │ ├── memo_relation_service.go
│ │ │ ├── memo_service.go
│ │ │ ├── memo_service_converter.go
│ │ │ ├── memo_service_filter.go
│ │ │ ├── memo_share_service.go
│ │ │ ├── reaction_service.go
│ │ │ ├── resource_name.go
│ │ │ ├── shortcut_service.go
│ │ │ ├── sse_handler.go
│ │ │ ├── sse_hub.go
│ │ │ ├── sse_hub_test.go
│ │ │ ├── test/
│ │ │ │ ├── attachment_service_test.go
│ │ │ │ ├── auth_test.go
│ │ │ │ ├── idp_service_test.go
│ │ │ │ ├── instance_admin_cache_test.go
│ │ │ │ ├── instance_service_test.go
│ │ │ │ ├── memo_attachment_service_test.go
│ │ │ │ ├── memo_relation_service_test.go
│ │ │ │ ├── memo_service_test.go
│ │ │ │ ├── memo_share_service_test.go
│ │ │ │ ├── reaction_service_test.go
│ │ │ │ ├── shortcut_service_test.go
│ │ │ │ ├── sse_handler_test.go
│ │ │ │ ├── test_helper.go
│ │ │ │ ├── user_notification_test.go
│ │ │ │ ├── user_service_registration_test.go
│ │ │ │ └── user_service_stats_test.go
│ │ │ ├── user_service.go
│ │ │ ├── user_service_stats.go
│ │ │ └── v1.go
│ │ ├── fileserver/
│ │ │ ├── README.md
│ │ │ ├── fileserver.go
│ │ │ └── fileserver_test.go
│ │ ├── frontend/
│ │ │ └── frontend.go
│ │ ├── mcp/
│ │ │ ├── README.md
│ │ │ ├── mcp.go
│ │ │ ├── prompts.go
│ │ │ ├── resources_memo.go
│ │ │ ├── tools_attachment.go
│ │ │ ├── tools_memo.go
│ │ │ ├── tools_reaction.go
│ │ │ ├── tools_relation.go
│ │ │ └── tools_tag.go
│ │ └── rss/
│ │ └── rss.go
│ ├── runner/
│ │ ├── memopayload/
│ │ │ └── runner.go
│ │ └── s3presign/
│ │ └── runner.go
│ └── server.go
├── store/
│ ├── attachment.go
│ ├── cache/
│ │ ├── cache.go
│ │ └── cache_test.go
│ ├── cache.go
│ ├── common.go
│ ├── db/
│ │ ├── db.go
│ │ ├── mysql/
│ │ │ ├── attachment.go
│ │ │ ├── common.go
│ │ │ ├── idp.go
│ │ │ ├── inbox.go
│ │ │ ├── instance_setting.go
│ │ │ ├── memo.go
│ │ │ ├── memo_relation.go
│ │ │ ├── memo_share.go
│ │ │ ├── mysql.go
│ │ │ ├── reaction.go
│ │ │ ├── user.go
│ │ │ └── user_setting.go
│ │ ├── postgres/
│ │ │ ├── attachment.go
│ │ │ ├── common.go
│ │ │ ├── idp.go
│ │ │ ├── inbox.go
│ │ │ ├── instance_setting.go
│ │ │ ├── memo.go
│ │ │ ├── memo_relation.go
│ │ │ ├── memo_share.go
│ │ │ ├── postgres.go
│ │ │ ├── reaction.go
│ │ │ ├── user.go
│ │ │ ├── user_setting.go
│ │ │ └── user_setting_test.go
│ │ └── sqlite/
│ │ ├── attachment.go
│ │ ├── common.go
│ │ ├── functions.go
│ │ ├── idp.go
│ │ ├── inbox.go
│ │ ├── instance_setting.go
│ │ ├── memo.go
│ │ ├── memo_relation.go
│ │ ├── memo_share.go
│ │ ├── reaction.go
│ │ ├── sqlite.go
│ │ ├── user.go
│ │ └── user_setting.go
│ ├── driver.go
│ ├── idp.go
│ ├── inbox.go
│ ├── instance_setting.go
│ ├── memo.go
│ ├── memo_relation.go
│ ├── memo_share.go
│ ├── migration/
│ │ ├── mysql/
│ │ │ ├── 0.17/
│ │ │ │ ├── 00__inbox.sql
│ │ │ │ └── 01__delete_activity.sql
│ │ │ ├── 0.18/
│ │ │ │ ├── 00__extend_text.sql
│ │ │ │ ├── 01__webhook.sql
│ │ │ │ └── 02__user_setting.sql
│ │ │ ├── 0.19/
│ │ │ │ └── 00__add_resource_name.sql
│ │ │ ├── 0.20/
│ │ │ │ └── 00__reaction.sql
│ │ │ ├── 0.21/
│ │ │ │ ├── 00__user_description.sql
│ │ │ │ └── 01__rename_uid.sql
│ │ │ ├── 0.22/
│ │ │ │ ├── 00__resource_storage_type.sql
│ │ │ │ ├── 01__memo_tags.sql
│ │ │ │ ├── 02__memo_payload.sql
│ │ │ │ └── 03__drop_tag.sql
│ │ │ ├── 0.23/
│ │ │ │ └── 00__reactions.sql
│ │ │ ├── 0.24/
│ │ │ │ ├── 00__memo.sql
│ │ │ │ ├── 01__memo_pinned.sql
│ │ │ │ └── 02__s3_reference_length.sql
│ │ │ ├── 0.25/
│ │ │ │ └── 00__remove_webhook.sql
│ │ │ ├── 0.26/
│ │ │ │ ├── 00__rename_resource_to_attachment.sql
│ │ │ │ ├── 01__drop_memo_organizer.sql
│ │ │ │ └── 02__migrate_host_to_admin.sql
│ │ │ ├── 0.27/
│ │ │ │ ├── 00__migrate_storage_setting.sql
│ │ │ │ ├── 01__add_idp_uid.sql
│ │ │ │ ├── 02__migrate_inbox_message_payload.sql
│ │ │ │ ├── 03__drop_activity.sql
│ │ │ │ └── 04__memo_share.sql
│ │ │ └── LATEST.sql
│ │ ├── postgres/
│ │ │ ├── 0.19/
│ │ │ │ └── 00__add_resource_name.sql
│ │ │ ├── 0.20/
│ │ │ │ └── 00__reaction.sql
│ │ │ ├── 0.21/
│ │ │ │ ├── 00__user_description.sql
│ │ │ │ └── 01__rename_uid.sql
│ │ │ ├── 0.22/
│ │ │ │ ├── 00__resource_storage_type.sql
│ │ │ │ ├── 01__memo_tags.sql
│ │ │ │ ├── 02__memo_payload.sql
│ │ │ │ └── 03__drop_tag.sql
│ │ │ ├── 0.23/
│ │ │ │ └── 00__reactions.sql
│ │ │ ├── 0.24/
│ │ │ │ ├── 00__memo.sql
│ │ │ │ └── 01__memo_pinned.sql
│ │ │ ├── 0.25/
│ │ │ │ └── 00__remove_webhook.sql
│ │ │ ├── 0.26/
│ │ │ │ ├── 00__rename_resource_to_attachment.sql
│ │ │ │ ├── 01__drop_memo_organizer.sql
│ │ │ │ └── 02__migrate_host_to_admin.sql
│ │ │ ├── 0.27/
│ │ │ │ ├── 00__migrate_storage_setting.sql
│ │ │ │ ├── 01__add_idp_uid.sql
│ │ │ │ ├── 02__migrate_inbox_message_payload.sql
│ │ │ │ ├── 03__drop_activity.sql
│ │ │ │ └── 04__memo_share.sql
│ │ │ └── LATEST.sql
│ │ └── sqlite/
│ │ ├── 0.10/
│ │ │ └── 00__activity.sql
│ │ ├── 0.11/
│ │ │ ├── 00__user_avatar.sql
│ │ │ ├── 01__idp.sql
│ │ │ └── 02__storage.sql
│ │ ├── 0.12/
│ │ │ ├── 00__user_setting.sql
│ │ │ ├── 01__system_setting.sql
│ │ │ ├── 03__resource_internal_path.sql
│ │ │ └── 04__resource_public_id.sql
│ │ ├── 0.13/
│ │ │ ├── 00__memo_relation.sql
│ │ │ └── 01__remove_memo_organizer_id.sql
│ │ ├── 0.14/
│ │ │ ├── 00__drop_resource_public_id.sql
│ │ │ └── 01__create_indexes.sql
│ │ ├── 0.15/
│ │ │ └── 00__drop_user_open_id.sql
│ │ ├── 0.16/
│ │ │ ├── 00__add_memo_id_to_resource.sql
│ │ │ └── 01__drop_shortcut_table.sql
│ │ ├── 0.17/
│ │ │ ├── 00__inbox.sql
│ │ │ └── 01__delete_activities.sql
│ │ ├── 0.18/
│ │ │ ├── 00__webhook.sql
│ │ │ └── 01__user_setting.sql
│ │ ├── 0.19/
│ │ │ └── 00__add_resource_name.sql
│ │ ├── 0.2/
│ │ │ ├── 00__user_role.sql
│ │ │ └── 01__memo_visibility.sql
│ │ ├── 0.20/
│ │ │ └── 00__reaction.sql
│ │ ├── 0.21/
│ │ │ ├── 00__user_description.sql
│ │ │ └── 01__rename_uid.sql
│ │ ├── 0.22/
│ │ │ ├── 00__resource_storage_type.sql
│ │ │ ├── 01__memo_tags.sql
│ │ │ ├── 02__memo_payload.sql
│ │ │ └── 03__drop_tag.sql
│ │ ├── 0.23/
│ │ │ └── 00__reactions.sql
│ │ ├── 0.24/
│ │ │ ├── 00__memo.sql
│ │ │ └── 01__memo_pinned.sql
│ │ ├── 0.25/
│ │ │ └── 00__remove_webhook.sql
│ │ ├── 0.26/
│ │ │ ├── 00__rename_resource_to_attachment.sql
│ │ │ ├── 01__drop_memo_organizer.sql
│ │ │ ├── 02__drop_indexes.sql
│ │ │ ├── 03__alter_user_role.sql
│ │ │ └── 04__migrate_host_to_admin.sql
│ │ ├── 0.27/
│ │ │ ├── 00__migrate_storage_setting.sql
│ │ │ ├── 01__add_idp_uid.sql
│ │ │ ├── 02__migrate_inbox_message_payload.sql
│ │ │ ├── 03__drop_activity.sql
│ │ │ └── 04__memo_share.sql
│ │ ├── 0.3/
│ │ │ └── 00__memo_visibility_protected.sql
│ │ ├── 0.4/
│ │ │ └── 00__user_setting.sql
│ │ ├── 0.5/
│ │ │ ├── 00__regenerate_foreign_keys.sql
│ │ │ ├── 01__memo_resource.sql
│ │ │ ├── 02__system_setting.sql
│ │ │ └── 03__resource_extermal_link.sql
│ │ ├── 0.6/
│ │ │ └── 00__recreate_triggers.sql
│ │ ├── 0.7/
│ │ │ ├── 00__remove_fk.sql
│ │ │ └── 01__remove_triggers.sql
│ │ ├── 0.8/
│ │ │ ├── 00__migration_history.sql
│ │ │ └── 01__user_username.sql
│ │ ├── 0.9/
│ │ │ └── 00__tag.sql
│ │ └── LATEST.sql
│ ├── migrator.go
│ ├── reaction.go
│ ├── seed/
│ │ ├── DEMO_DATA_GUIDE.md
│ │ └── sqlite/
│ │ └── 01__dump.sql
│ ├── store.go
│ ├── test/
│ │ ├── README.md
│ │ ├── attachment_filter_test.go
│ │ ├── attachment_test.go
│ │ ├── containers.go
│ │ ├── filter_helpers_test.go
│ │ ├── idp_test.go
│ │ ├── inbox_test.go
│ │ ├── instance_setting_test.go
│ │ ├── main_test.go
│ │ ├── memo_filter_test.go
│ │ ├── memo_relation_test.go
│ │ ├── memo_test.go
│ │ ├── migrator_test.go
│ │ ├── reaction_test.go
│ │ ├── store.go
│ │ ├── user_setting_test.go
│ │ └── user_test.go
│ ├── user.go
│ └── user_setting.go
└── web/
├── .gitignore
├── biome.json
├── components.json
├── docs/
│ └── auth-architecture.md
├── index.html
├── package.json
├── public/
│ └── site.webmanifest
├── src/
│ ├── App.tsx
│ ├── auth-state.ts
│ ├── components/
│ │ ├── ActivityCalendar/
│ │ │ ├── CalendarCell.tsx
│ │ │ ├── MonthCalendar.tsx
│ │ │ ├── YearCalendar.tsx
│ │ │ ├── constants.ts
│ │ │ ├── hooks.ts
│ │ │ ├── index.ts
│ │ │ ├── types.ts
│ │ │ ├── useCalendar.ts
│ │ │ └── utils.ts
│ │ ├── AttachmentIcon.tsx
│ │ ├── AuthFooter.tsx
│ │ ├── ChangeMemberPasswordDialog.tsx
│ │ ├── ConfirmDialog/
│ │ │ ├── README.md
│ │ │ └── index.tsx
│ │ ├── CreateAccessTokenDialog.tsx
│ │ ├── CreateIdentityProviderDialog.tsx
│ │ ├── CreateShortcutDialog.tsx
│ │ ├── CreateUserDialog.tsx
│ │ ├── CreateWebhookDialog.tsx
│ │ ├── DateTimeInput.tsx
│ │ ├── Empty.tsx
│ │ ├── ErrorBoundary.tsx
│ │ ├── Inbox/
│ │ │ └── MemoCommentMessage.tsx
│ │ ├── LearnMore.tsx
│ │ ├── LocaleSelect.tsx
│ │ ├── MemoActionMenu/
│ │ │ ├── MemoActionMenu.tsx
│ │ │ ├── hooks.ts
│ │ │ ├── index.ts
│ │ │ └── types.ts
│ │ ├── MemoAttachment.tsx
│ │ ├── MemoContent/
│ │ │ ├── CodeBlock.tsx
│ │ │ ├── ConditionalComponent.tsx
│ │ │ ├── MermaidBlock.tsx
│ │ │ ├── Table.tsx
│ │ │ ├── Tag.tsx
│ │ │ ├── TaskListItem.tsx
│ │ │ ├── constants.ts
│ │ │ ├── hooks.ts
│ │ │ ├── index.tsx
│ │ │ ├── markdown/
│ │ │ │ ├── Blockquote.tsx
│ │ │ │ ├── Heading.tsx
│ │ │ │ ├── HorizontalRule.tsx
│ │ │ │ ├── Image.tsx
│ │ │ │ ├── InlineCode.tsx
│ │ │ │ ├── Link.tsx
│ │ │ │ ├── List.tsx
│ │ │ │ ├── Paragraph.tsx
│ │ │ │ ├── README.md
│ │ │ │ ├── index.ts
│ │ │ │ └── types.ts
│ │ │ ├── types.ts
│ │ │ └── utils.ts
│ │ ├── MemoDetailSidebar/
│ │ │ ├── MemoDetailSidebar.tsx
│ │ │ ├── MemoDetailSidebarDrawer.tsx
│ │ │ └── index.ts
│ │ ├── MemoDisplaySettingMenu.tsx
│ │ ├── MemoEditor/
│ │ │ ├── Editor/
│ │ │ │ ├── SlashCommands.tsx
│ │ │ │ ├── SuggestionsPopup.tsx
│ │ │ │ ├── TagSuggestions.tsx
│ │ │ │ ├── commands.ts
│ │ │ │ ├── index.tsx
│ │ │ │ ├── shortcuts.ts
│ │ │ │ ├── useListCompletion.ts
│ │ │ │ └── useSuggestions.ts
│ │ │ ├── README.md
│ │ │ ├── Toolbar/
│ │ │ │ ├── InsertMenu.tsx
│ │ │ │ ├── VisibilitySelector.tsx
│ │ │ │ └── index.ts
│ │ │ ├── components/
│ │ │ │ ├── AttachmentList.tsx
│ │ │ │ ├── EditorContent.tsx
│ │ │ │ ├── EditorMetadata.tsx
│ │ │ │ ├── EditorToolbar.tsx
│ │ │ │ ├── FocusModeOverlay.tsx
│ │ │ │ ├── LinkMemoDialog.tsx
│ │ │ │ ├── LocationDialog.tsx
│ │ │ │ ├── LocationDisplay.tsx
│ │ │ │ ├── RelationList.tsx
│ │ │ │ ├── TimestampPopover.tsx
│ │ │ │ └── index.ts
│ │ │ ├── constants.ts
│ │ │ ├── hooks/
│ │ │ │ ├── index.ts
│ │ │ │ ├── useAutoSave.ts
│ │ │ │ ├── useBlobUrls.ts
│ │ │ │ ├── useDragAndDrop.ts
│ │ │ │ ├── useFileUpload.ts
│ │ │ │ ├── useFocusMode.ts
│ │ │ │ ├── useKeyboard.ts
│ │ │ │ ├── useLinkMemo.ts
│ │ │ │ ├── useLocation.ts
│ │ │ │ └── useMemoInit.ts
│ │ │ ├── index.tsx
│ │ │ ├── services/
│ │ │ │ ├── cacheService.ts
│ │ │ │ ├── errorService.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── memoService.ts
│ │ │ │ ├── uploadService.ts
│ │ │ │ └── validationService.ts
│ │ │ ├── state/
│ │ │ │ ├── actions.ts
│ │ │ │ ├── context.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── reducer.ts
│ │ │ │ └── types.ts
│ │ │ └── types/
│ │ │ ├── attachment.ts
│ │ │ ├── components.ts
│ │ │ ├── context.ts
│ │ │ ├── index.ts
│ │ │ └── insert-menu.ts
│ │ ├── MemoExplorer/
│ │ │ ├── MemoExplorer.tsx
│ │ │ ├── MemoExplorerDrawer.tsx
│ │ │ ├── ShortcutsSection.tsx
│ │ │ ├── TagsSection.tsx
│ │ │ └── index.ts
│ │ ├── MemoFilters.tsx
│ │ ├── MemoPreview/
│ │ │ ├── MemoPreview.tsx
│ │ │ └── index.ts
│ │ ├── MemoReactionListView/
│ │ │ ├── MemoReactionListView.tsx
│ │ │ ├── ReactionSelector.tsx
│ │ │ ├── ReactionView.tsx
│ │ │ ├── hooks.ts
│ │ │ └── index.ts
│ │ ├── MemoRelationForceGraph/
│ │ │ ├── MemoRelationForceGraph.tsx
│ │ │ ├── index.ts
│ │ │ ├── types.ts
│ │ │ └── utils.ts
│ │ ├── MemoResource.tsx
│ │ ├── MemoSharePanel.tsx
│ │ ├── MemoView/
│ │ │ ├── MemoView.tsx
│ │ │ ├── MemoViewContext.tsx
│ │ │ ├── components/
│ │ │ │ ├── MemoBody.tsx
│ │ │ │ ├── MemoCommentListView.tsx
│ │ │ │ ├── MemoHeader.tsx
│ │ │ │ ├── MemoSnippetLink.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── metadata/
│ │ │ │ ├── AttachmentCard.tsx
│ │ │ │ ├── AttachmentList.tsx
│ │ │ │ ├── LocationDisplay.tsx
│ │ │ │ ├── RelationCard.tsx
│ │ │ │ ├── RelationList.tsx
│ │ │ │ ├── SectionHeader.tsx
│ │ │ │ └── index.ts
│ │ │ ├── constants.ts
│ │ │ ├── hooks/
│ │ │ │ ├── index.ts
│ │ │ │ ├── useImagePreview.ts
│ │ │ │ ├── useMemoActions.ts
│ │ │ │ └── useMemoHandlers.ts
│ │ │ ├── index.ts
│ │ │ └── types.ts
│ │ ├── MemosLogo.tsx
│ │ ├── MobileHeader.tsx
│ │ ├── Navigation.tsx
│ │ ├── NavigationDrawer.tsx
│ │ ├── PagedMemoList/
│ │ │ ├── PagedMemoList.tsx
│ │ │ └── index.ts
│ │ ├── PasswordSignInForm.tsx
│ │ ├── PreviewImageDialog.tsx
│ │ ├── RequiredBadge.tsx
│ │ ├── SearchBar.tsx
│ │ ├── Settings/
│ │ │ ├── AccessTokenSection.tsx
│ │ │ ├── InstanceSection.tsx
│ │ │ ├── MemberSection.tsx
│ │ │ ├── MemoRelatedSettings.tsx
│ │ │ ├── MyAccountSection.tsx
│ │ │ ├── PreferencesSection.tsx
│ │ │ ├── SSOSection.tsx
│ │ │ ├── SectionMenuItem.tsx
│ │ │ ├── SettingGroup.tsx
│ │ │ ├── SettingRow.tsx
│ │ │ ├── SettingSection.tsx
│ │ │ ├── SettingTable.tsx
│ │ │ ├── StorageSection.tsx
│ │ │ └── WebhookSection.tsx
│ │ ├── Skeleton.tsx
│ │ ├── StatisticsView/
│ │ │ ├── MonthNavigator.tsx
│ │ │ ├── StatisticsView.tsx
│ │ │ └── index.ts
│ │ ├── TagTree.tsx
│ │ ├── ThemeSelect.tsx
│ │ ├── UpdateAccountDialog.tsx
│ │ ├── UpdateCustomizedProfileDialog.tsx
│ │ ├── UserAvatar.tsx
│ │ ├── UserMemoMap/
│ │ │ ├── UserMemoMap.tsx
│ │ │ └── index.ts
│ │ ├── UserMenu.tsx
│ │ ├── VisibilityIcon.tsx
│ │ ├── kit/
│ │ │ ├── OverflowTip.tsx
│ │ │ └── SquareDiv.tsx
│ │ ├── map/
│ │ │ ├── LocationPicker.tsx
│ │ │ ├── index.ts
│ │ │ ├── map-utils.tsx
│ │ │ └── useReverseGeocoding.ts
│ │ └── ui/
│ │ ├── badge.tsx
│ │ ├── button.tsx
│ │ ├── checkbox.tsx
│ │ ├── dialog.tsx
│ │ ├── dropdown-menu.tsx
│ │ ├── input.tsx
│ │ ├── label.tsx
│ │ ├── popover.tsx
│ │ ├── radio-group.tsx
│ │ ├── select.tsx
│ │ ├── separator.tsx
│ │ ├── sheet.tsx
│ │ ├── switch.tsx
│ │ ├── textarea.tsx
│ │ ├── tooltip.tsx
│ │ └── visually-hidden.tsx
│ ├── connect.ts
│ ├── contexts/
│ │ ├── AuthContext.tsx
│ │ ├── InstanceContext.tsx
│ │ ├── MemoFilterContext.tsx
│ │ └── ViewContext.tsx
│ ├── helpers/
│ │ ├── consts.ts
│ │ ├── resource-names.ts
│ │ └── utils.ts
│ ├── hooks/
│ │ ├── index.ts
│ │ ├── useAsyncEffect.ts
│ │ ├── useAttachmentQueries.ts
│ │ ├── useCurrentUser.ts
│ │ ├── useDateFilterNavigation.ts
│ │ ├── useDialog.ts
│ │ ├── useFilteredMemoStats.ts
│ │ ├── useInstanceQueries.ts
│ │ ├── useLiveMemoRefresh.ts
│ │ ├── useLoading.ts
│ │ ├── useMediaQuery.ts
│ │ ├── useMemoFilters.ts
│ │ ├── useMemoQueries.ts
│ │ ├── useMemoShareQueries.ts
│ │ ├── useMemoSorting.ts
│ │ ├── useNavigateTo.ts
│ │ ├── useTokenRefreshOnFocus.ts
│ │ ├── useUserLocale.ts
│ │ ├── useUserQueries.ts
│ │ └── useUserTheme.ts
│ ├── i18n.ts
│ ├── index.css
│ ├── layouts/
│ │ ├── MainLayout.tsx
│ │ └── RootLayout.tsx
│ ├── lib/
│ │ ├── calendar-utils.ts
│ │ ├── error.ts
│ │ ├── query-client.ts
│ │ └── utils.ts
│ ├── locales/
│ │ ├── ar.json
│ │ ├── ca.json
│ │ ├── cs.json
│ │ ├── de.json
│ │ ├── en-GB.json
│ │ ├── en.json
│ │ ├── es.json
│ │ ├── fa.json
│ │ ├── fr.json
│ │ ├── gl.json
│ │ ├── hi.json
│ │ ├── hr.json
│ │ ├── hu.json
│ │ ├── id.json
│ │ ├── it.json
│ │ ├── ja.json
│ │ ├── ka-GE.json
│ │ ├── ko.json
│ │ ├── mr.json
│ │ ├── nb.json
│ │ ├── nl.json
│ │ ├── pl.json
│ │ ├── pt-BR.json
│ │ ├── pt-PT.json
│ │ ├── ru.json
│ │ ├── sl.json
│ │ ├── sv.json
│ │ ├── th.json
│ │ ├── tr.json
│ │ ├── uk.json
│ │ ├── vi.json
│ │ ├── zh-Hans.json
│ │ └── zh-Hant.json
│ ├── main.tsx
│ ├── pages/
│ │ ├── AdminSignIn.tsx
│ │ ├── Archived.tsx
│ │ ├── Attachments.tsx
│ │ ├── AuthCallback.tsx
│ │ ├── Explore.tsx
│ │ ├── Home.tsx
│ │ ├── Inboxes.tsx
│ │ ├── MemoDetail.tsx
│ │ ├── NotFound.tsx
│ │ ├── PermissionDenied.tsx
│ │ ├── Setting.tsx
│ │ ├── SharedMemo.tsx
│ │ ├── SignIn.tsx
│ │ ├── SignUp.tsx
│ │ └── UserProfile.tsx
│ ├── router/
│ │ ├── index.tsx
│ │ └── routes.ts
│ ├── themes/
│ │ ├── COLOR_GUIDE.md
│ │ ├── default-dark.css
│ │ ├── default.css
│ │ └── paper.css
│ ├── types/
│ │ ├── common.d.ts
│ │ ├── common.ts
│ │ ├── i18n.d.ts
│ │ ├── markdown.ts
│ │ ├── modules/
│ │ │ └── setting.d.ts
│ │ ├── proto/
│ │ │ ├── api/
│ │ │ │ └── v1/
│ │ │ │ ├── attachment_service_pb.ts
│ │ │ │ ├── auth_service_pb.ts
│ │ │ │ ├── common_pb.ts
│ │ │ │ ├── idp_service_pb.ts
│ │ │ │ ├── instance_service_pb.ts
│ │ │ │ ├── memo_service_pb.ts
│ │ │ │ ├── shortcut_service_pb.ts
│ │ │ │ └── user_service_pb.ts
│ │ │ └── google/
│ │ │ ├── api/
│ │ │ │ ├── annotations_pb.ts
│ │ │ │ ├── client_pb.ts
│ │ │ │ ├── field_behavior_pb.ts
│ │ │ │ ├── http_pb.ts
│ │ │ │ ├── launch_stage_pb.ts
│ │ │ │ └── resource_pb.ts
│ │ │ └── type/
│ │ │ └── color_pb.ts
│ │ ├── statistics.ts
│ │ └── view.d.ts
│ └── utils/
│ ├── attachment.ts
│ ├── auth-redirect.ts
│ ├── format.ts
│ ├── i18n.ts
│ ├── markdown-list-detection.ts
│ ├── markdown-manipulation.ts
│ ├── memo.ts
│ ├── oauth.ts
│ ├── remark-plugins/
│ │ ├── remark-disable-setext.ts
│ │ ├── remark-preserve-type.ts
│ │ └── remark-tag.ts
│ ├── theme.ts
│ ├── user.ts
│ └── uuid.ts
├── tsconfig.json
└── vite.config.mts
================================================
FILE CONTENTS
================================================
================================================
FILE: .dockerignore
================================================
web/node_modules
web/dist
.git
.github
build/
tmp/
memos
*.md
.gitignore
.golangci.yaml
.dockerignore
docs/
.DS_Store
================================================
FILE: .github/FUNDING.yml
================================================
github: usememos
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: Bug Report
description: Something isn't working as expected
type: Bug
body:
- type: markdown
attributes:
value: |
Thanks for reporting a bug! Please fill out the form below so we can reproduce and fix the issue.
**Before submitting**, please search [existing issues](https://github.com/usememos/memos/issues) to avoid duplicates.
- type: checkboxes
id: pre-check
attributes:
label: Pre-submission Checklist
options:
- label: I have searched existing issues and confirmed this bug has not been reported
required: true
- label: I can reproduce this bug on the latest version or the [demo site](https://demo.usememos.com)
required: true
- label: This is a bug, not a question (use [Discussions](https://github.com/usememos/memos/discussions) for questions)
required: true
- type: input
id: version
attributes:
label: Memos Version
description: Find this in **Settings > System > About** or via the `--version` flag
placeholder: "v0.25.2"
validations:
required: true
- type: dropdown
id: deployment
attributes:
label: Deployment Method
options:
- Docker
- Pre-built binary
- Built from source
validations:
required: true
- type: dropdown
id: database
attributes:
label: Database
options:
- SQLite
- PostgreSQL
- MySQL
validations:
required: true
- type: input
id: browser-os
attributes:
label: Browser & OS
description: e.g. Chrome 120 on macOS 15, Firefox 130 on Ubuntu 24.04
placeholder: "Chrome 120 on macOS 15"
validations:
required: false
- 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: Minimal steps to reliably reproduce the issue
placeholder: |
1. Go to '...'
2. Click on '...'
3. See error
validations:
required: true
- type: textarea
id: expected-behavior
attributes:
label: Expected Behavior
description: What did you expect to happen instead?
placeholder: I expected...
validations:
required: true
- type: textarea
id: additional-context
attributes:
label: Screenshots, Logs & Additional Context
description: Attach screenshots, browser console errors, or server logs if available
placeholder: Drag and drop images here, or paste error logs...
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: Questions & Support
url: https://github.com/usememos/memos/discussions
about: Ask questions or get help in GitHub Discussions — please don't open issues for questions
- name: Documentation
url: https://www.usememos.com/docs
about: Check the documentation before opening an issue
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: Feature Request
description: Suggest a new feature or improvement
type: Feature
body:
- type: markdown
attributes:
value: |
Thanks for suggesting a feature! Please fill out the form below so we can understand your idea.
**Before submitting**, please search [existing issues](https://github.com/usememos/memos/issues?q=label%3Aenhancement) to avoid duplicates.
- type: checkboxes
id: pre-check
attributes:
label: Pre-submission Checklist
options:
- label: I have searched existing issues and confirmed this feature has not been requested
required: true
- label: This is a feature request, not a bug report or question
required: true
- type: dropdown
id: feature-type
attributes:
label: Feature Area
options:
- User Interface (UI)
- User Experience (UX)
- API / Backend
- Integrations / Plugins
- Security / Privacy
- Performance
- Other
validations:
required: true
- type: textarea
id: problem-statement
attributes:
label: Problem or Use Case
description: What problem does this feature solve? Why do you need it?
placeholder: |
I often need to... but currently there's no way to...
validations:
required: true
- type: textarea
id: proposed-solution
attributes:
label: Proposed Solution
description: Describe what you'd like to happen
placeholder: |
It would be great if Memos could...
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives Considered
description: Have you considered any workarounds or alternative approaches?
placeholder: |
I've tried... but it doesn't work well because...
- type: textarea
id: additional-context
attributes:
label: Additional Context
description: Mockups, screenshots, examples from other apps, or any other context
placeholder: Drag and drop images here...
- type: checkboxes
id: contribution
attributes:
label: Contribution
description: Would you be willing to help implement this feature?
options:
- label: I'm willing to submit a pull request for this feature
================================================
FILE: .github/workflows/backend-tests.yml
================================================
name: Backend Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
paths:
- "go.mod"
- "go.sum"
- "**.go"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
GO_VERSION: "1.26.1"
jobs:
static-checks:
name: Static Checks
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
cache: true
cache-dependency-path: go.sum
- name: Verify go.mod is tidy
run: |
go mod tidy -go=${{ env.GO_VERSION }}
git diff --exit-code
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v9
with:
version: v2.11.3
args: --timeout=3m
tests:
name: Tests (${{ matrix.test-group }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
test-group: [store, server, plugin, other]
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
cache: true
cache-dependency-path: go.sum
- name: Run tests
run: |
case "${{ matrix.test-group }}" in
store)
# Run store tests for all drivers (sqlite, mysql, postgres)
go test -v -coverprofile=coverage.out -covermode=atomic ./store/...
;;
server)
go test -v -race -coverprofile=coverage.out -covermode=atomic ./server/...
;;
plugin)
go test -v -race -coverprofile=coverage.out -covermode=atomic ./plugin/...
;;
other)
go test -v -race -coverprofile=coverage.out -covermode=atomic \
./cmd/... ./internal/... ./proto/...
;;
esac
env:
DRIVER: ${{ matrix.test-group == 'store' && '' || 'sqlite' }}
- name: Upload coverage
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: codecov/codecov-action@v5
with:
files: ./coverage.out
flags: ${{ matrix.test-group }}
fail_ci_if_error: false
================================================
FILE: .github/workflows/build-canary-image.yml
================================================
name: Build Canary Image
on:
push:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.repository }}
cancel-in-progress: true
jobs:
build-frontend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4.2.0
with:
version: 10
- uses: actions/setup-node@v6
with:
node-version: "24"
cache: pnpm
cache-dependency-path: "web/pnpm-lock.yaml"
- name: Get pnpm store directory
id: pnpm-cache
shell: bash
run: echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- name: Setup pnpm cache
uses: actions/cache@v5
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('web/pnpm-lock.yaml') }}
restore-keys: ${{ runner.os }}-pnpm-store-
- run: pnpm install --frozen-lockfile
working-directory: web
- name: Run frontend build
run: pnpm release
working-directory: web
- name: Upload frontend artifacts
uses: actions/upload-artifact@v6
with:
name: frontend-dist
path: server/router/frontend/dist
retention-days: 1
build-push:
needs: build-frontend
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm64
steps:
- uses: actions/checkout@v6
- name: Download frontend artifacts
uses: actions/download-artifact@v7
with:
name: frontend-dist
path: server/router/frontend/dist
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
context: .
file: ./scripts/Dockerfile
platforms: ${{ matrix.platform }}
cache-from: type=gha,scope=build-${{ matrix.platform }}
cache-to: type=gha,mode=max,scope=build-${{ matrix.platform }}
outputs: type=image,name=neosmemo/memos,push-by-digest=true,name-canonical=true,push=true
- name: Export digest
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v6
with:
name: digests-${{ strategy.job-index }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
merge:
needs: build-push
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Download digests
uses: actions/download-artifact@v7
with:
pattern: digests-*
merge-multiple: true
path: /tmp/digests
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: |
neosmemo/memos
ghcr.io/usememos/memos
flavor: |
latest=false
tags: |
type=raw,value=canary
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Create manifest list and push
working-directory: /tmp/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf 'neosmemo/memos@sha256:%s ' *)
env:
DOCKER_METADATA_OUTPUT_JSON: ${{ steps.meta.outputs.json }}
- name: Inspect images
run: |
docker buildx imagetools inspect neosmemo/memos:canary
docker buildx imagetools inspect ghcr.io/usememos/memos:canary
================================================
FILE: .github/workflows/demo-deploy.yml
================================================
name: Demo Deploy
on:
workflow_dispatch:
jobs:
deploy-demo:
runs-on: ubuntu-latest
steps:
- name: Trigger Render Deploy
run: |
curl -X POST "${{ secrets.RENDER_DEPLOY_HOOK }}" \
-H "Content-Type: application/json" \
-d '{"trigger": "github_action"}'
- name: Deployment Status
run: echo "Demo deployment triggered successfully on Render"
================================================
FILE: .github/workflows/frontend-tests.yml
================================================
name: Frontend Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
paths:
- "web/**"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
NODE_VERSION: "24"
PNPM_VERSION: "10"
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: pnpm
cache-dependency-path: web/pnpm-lock.yaml
- name: Install dependencies
working-directory: web
run: pnpm install --frozen-lockfile
- name: Run lint
working-directory: web
run: pnpm lint
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: pnpm
cache-dependency-path: web/pnpm-lock.yaml
- name: Install dependencies
working-directory: web
run: pnpm install --frozen-lockfile
- name: Build frontend
working-directory: web
run: pnpm build
================================================
FILE: .github/workflows/proto-linter.yml
================================================
name: Proto Linter
on:
push:
branches: [main]
pull_request:
branches: [main]
paths:
- "proto/**"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
name: Lint Protos
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup buf
uses: bufbuild/buf-setup-action@v1
with:
github_token: ${{ github.token }}
- name: Run buf lint
uses: bufbuild/buf-lint-action@v1
with:
input: proto
- name: Check buf format
run: |
if [[ $(buf format -d) ]]; then
echo "❌ Proto files are not formatted. Run 'buf format -w' to fix."
exit 1
fi
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
push:
tags:
- "v*.*.*"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
GO_VERSION: "1.26.1"
NODE_VERSION: "24"
PNPM_VERSION: "10"
ARTIFACT_RETENTION_DAYS: 60
ARTIFACT_PREFIX: memos
jobs:
prepare:
name: Extract Version
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
tag: ${{ steps.version.outputs.tag }}
steps:
- name: Extract version
id: version
env:
REF_NAME: ${{ github.ref_name }}
EVENT_NAME: ${{ github.event_name }}
run: |
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
echo "tag=" >> "$GITHUB_OUTPUT"
echo "version=manual-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "tag=${REF_NAME}" >> "$GITHUB_OUTPUT"
echo "version=${REF_NAME#v}" >> "$GITHUB_OUTPUT"
build-frontend:
name: Build Frontend
needs: prepare
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup pnpm
uses: pnpm/action-setup@v4.2.0
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: pnpm
cache-dependency-path: web/pnpm-lock.yaml
- name: Get pnpm store directory
id: pnpm-cache
shell: bash
run: echo "STORE_PATH=$(pnpm store path)" >> "$GITHUB_OUTPUT"
- name: Setup pnpm cache
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('web/pnpm-lock.yaml') }}
restore-keys: ${{ runner.os }}-pnpm-store-
- name: Install dependencies
working-directory: web
run: pnpm install --frozen-lockfile
- name: Build frontend release assets
working-directory: web
run: pnpm release
- name: Upload frontend artifacts
uses: actions/upload-artifact@v4
with:
name: frontend-dist
path: server/router/frontend/dist
retention-days: 1
build-binaries:
name: Build ${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.goarm && format('v{0}', matrix.goarm) || '' }}
needs: [prepare, build-frontend]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- goos: linux
goarch: amd64
- goos: linux
goarch: arm64
- goos: linux
goarch: arm
goarm: "7"
- goos: darwin
goarch: amd64
- goos: darwin
goarch: arm64
- goos: windows
goarch: amd64
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
cache: true
- name: Download frontend artifacts
uses: actions/download-artifact@v4
with:
name: frontend-dist
path: server/router/frontend/dist
- name: Build binary
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
GOARM: ${{ matrix.goarm }}
CGO_ENABLED: "0"
run: |
output_name="memos"
if [ "$GOOS" = "windows" ]; then
output_name="memos.exe"
fi
mkdir -p build
go build \
-trimpath \
-ldflags="-s -w -X github.com/usememos/memos/internal/version.Version=${{ needs.prepare.outputs.version }} -extldflags '-static'" \
-tags netgo,osusergo \
-o "build/${output_name}" \
./cmd/memos
- name: Package binary
env:
VERSION: ${{ needs.prepare.outputs.version }}
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
GOARM: ${{ matrix.goarm }}
run: |
cd build
package_name="${ARTIFACT_PREFIX}_${VERSION}_${GOOS}_${GOARCH}"
if [ -n "$GOARM" ]; then
package_name="${package_name}v${GOARM}"
fi
if [ "$GOOS" = "windows" ]; then
artifact_name="${package_name}.zip"
zip -q "${artifact_name}" memos.exe
else
artifact_name="${package_name}.tar.gz"
tar czf "${artifact_name}" memos
fi
echo "artifact_name=${artifact_name}" >> "$GITHUB_ENV"
- name: Upload binary artifact
uses: actions/upload-artifact@v4
with:
name: ${{ env.artifact_name }}
path: build/${{ env.artifact_name }}
retention-days: ${{ env.ARTIFACT_RETENTION_DAYS }}
checksums:
name: Generate Checksums
needs: [prepare, build-binaries]
runs-on: ubuntu-latest
steps:
- name: Download binary artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
pattern: ${{ env.ARTIFACT_PREFIX }}_*
merge-multiple: true
- name: Generate checksums
working-directory: artifacts
run: sha256sum * > checksums.txt
- name: Upload checksum artifact
uses: actions/upload-artifact@v4
with:
name: checksums
path: artifacts/checksums.txt
retention-days: ${{ env.ARTIFACT_RETENTION_DAYS }}
release:
name: Publish GitHub Release
needs: [prepare, build-binaries, checksums]
if: github.event_name != 'workflow_dispatch'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download binary artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
pattern: ${{ env.ARTIFACT_PREFIX }}_*
merge-multiple: true
- name: Download checksum artifact
uses: actions/download-artifact@v4
with:
name: checksums
path: artifacts
- name: Publish release assets
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.prepare.outputs.tag }}
name: ${{ needs.prepare.outputs.tag }}
generate_release_notes: true
files: artifacts/*
build-push:
name: Build Image ${{ matrix.platform }}
needs: [prepare, build-frontend]
if: github.event_name != 'workflow_dispatch'
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm/v7
- linux/arm64
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Download frontend artifacts
uses: actions/download-artifact@v4
with:
name: frontend-dist
path: server/router/frontend/dist
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
context: .
file: ./scripts/Dockerfile
platforms: ${{ matrix.platform }}
build-args: |
VERSION=${{ needs.prepare.outputs.version }}
COMMIT=${{ github.sha }}
cache-from: type=gha,scope=release-${{ matrix.platform }}
cache-to: type=gha,mode=max,scope=release-${{ matrix.platform }}
outputs: type=image,name=neosmemo/memos,push-by-digest=true,name-canonical=true,push=true
- name: Export digest
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-${{ strategy.job-index }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
merge-images:
name: Publish Stable Image Tags
needs: [prepare, build-push]
if: github.event_name != 'workflow_dispatch'
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
pattern: digests-*
merge-multiple: true
path: /tmp/digests
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
- name: Create manifest list and push
working-directory: /tmp/digests
run: |
version="${{ needs.prepare.outputs.version }}"
major_minor=$(echo "$version" | cut -d. -f1,2)
docker buildx imagetools create \
-t "neosmemo/memos:${version}" \
-t "neosmemo/memos:${major_minor}" \
-t "neosmemo/memos:stable" \
-t "ghcr.io/usememos/memos:${version}" \
-t "ghcr.io/usememos/memos:${major_minor}" \
-t "ghcr.io/usememos/memos:stable" \
$(printf 'neosmemo/memos@sha256:%s ' *)
- name: Inspect images
run: |
docker buildx imagetools inspect neosmemo/memos:${{ needs.prepare.outputs.version }}
docker buildx imagetools inspect neosmemo/memos:stable
================================================
FILE: .github/workflows/stale.yml
================================================
name: Close Stale
on:
schedule:
- cron: "0 */8 * * *" # Every 8 hours
jobs:
close-stale:
name: Close Stale Issues and PRs
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- name: Mark and close stale issues and PRs
uses: actions/stale@v10.1.1
with:
# Issues: mark stale after 14 days of inactivity, close after 3 more days
days-before-issue-stale: 14
days-before-issue-close: 3
# Pull requests: mark stale after 14 days of inactivity, close after 3 more days
days-before-pr-stale: 14
days-before-pr-close: 3
================================================
FILE: .gitignore
================================================
# temp folder
tmp
# Frontend asset
web/dist
# Build artifacts
build/
bin/
memos
# Plan/design documents
docs/plans/
.DS_Store
# Jetbrains
.idea
# Docker Compose Environment File
.env
dist
# VSCode settings
.vscode
# Git worktrees
.worktrees/
================================================
FILE: .golangci.yaml
================================================
version: "2"
linters:
enable:
- revive
- govet
- staticcheck
- misspell
- gocritic
- sqlclosecheck
- rowserrcheck
- nilerr
- godot
- forbidigo
- mirror
- bodyclose
disable:
- errcheck
settings:
exhaustive:
explicit-exhaustive-switch: false
staticcheck:
checks:
- all
- -ST1000
- -ST1003
- -ST1021
- -QF1003
revive:
# Default to run all linters so that new rules in the future could automatically be added to the static check.
enable-all-rules: true
rules:
# The following rules are too strict and make coding harder. We do not enable them for now.
- name: file-header
disabled: true
- name: line-length-limit
disabled: true
- name: function-length
disabled: true
- name: max-public-structs
disabled: true
- name: function-result-limit
disabled: true
- name: banned-characters
disabled: true
- name: argument-limit
disabled: true
- name: cognitive-complexity
disabled: true
- name: cyclomatic
disabled: true
- name: confusing-results
disabled: true
- name: add-constant
disabled: true
- name: flag-parameter
disabled: true
- name: nested-structs
disabled: true
- name: import-shadowing
disabled: true
- name: early-return
disabled: true
- name: use-any
disabled: true
- name: exported
disabled: true
- name: unhandled-error
disabled: true
- name: if-return
disabled: true
- name: max-control-nesting
disabled: true
- name: redefines-builtin-id
disabled: true
- name: package-comments
disabled: true
gocritic:
disabled-checks:
- ifElseChain
govet:
settings:
printf: # The name of the analyzer, run `go tool vet help` to see the list of all analyzers
funcs: # Run `go tool vet help printf` to see the full configuration of `printf`.
- common.Errorf
enable-all: true
disable:
- fieldalignment
- shadow
forbidigo:
forbid:
- pattern: 'fmt\.Errorf(# Please use errors\.Wrap\|Wrapf\|Errorf instead)?'
- pattern: 'ioutil\.ReadDir(# Please use os\.ReadDir)?'
formatters:
enable:
- goimports
settings:
goimports:
local-prefixes:
- github.com/usememos/memos
================================================
FILE: AGENTS.md
================================================
# AGENTS.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Self-hosted note-taking tool. Go 1.26 backend (Echo v5, Connect RPC + gRPC-Gateway), React 18 + TypeScript 5.9 + Vite 7 frontend, Protocol Buffers API, SQLite/MySQL/PostgreSQL.
## Commands
```bash
# Backend
go run ./cmd/memos --port 8081 # Start dev server
go test ./... # Run all tests
go test -v ./store/... # Run store tests (all 3 DB drivers via TestContainers)
go test -v -race ./server/... # Run server tests with race detection
go test -v -run TestFoo ./pkg/... # Run a single test
golangci-lint run # Lint (v2, config: .golangci.yaml)
golangci-lint run --fix # Auto-fix lint issues (includes goimports)
# Frontend (cd web)
pnpm install # Install deps
pnpm dev # Dev server (:3001, proxies API to :8081)
pnpm lint # Type check + Biome lint
pnpm lint:fix # Auto-fix lint issues
pnpm format # Format code
pnpm build # Production build
pnpm release # Build to server/router/frontend/dist
# Protocol Buffers (cd proto)
buf generate # Regenerate Go + TypeScript + OpenAPI
buf lint # Lint proto files
buf format -w # Format proto files
```
## Architecture
```
cmd/memos/main.go # Cobra CLI + Viper config, server init
server/
├── server.go # Echo v5 HTTP server, background runners
├── auth/ # JWT access (15min) + refresh (30d) tokens, PAT
├── router/
│ ├── api/v1/ # 8 gRPC services (Connect + Gateway)
│ │ ├── acl_config.go # Public endpoints whitelist
│ │ ├── sse_hub.go # Server-Sent Events (live updates)
│ │ └── mcp/ # MCP server for AI assistants
│ ├── frontend/ # SPA static file serving
│ ├── fileserver/ # Native HTTP file server (thumbnails, range requests)
│ └── rss/ # RSS feeds
└── runner/ # Background: memo payload processing, S3 presign refresh
store/
├── driver.go # Database driver interface
├── store.go # Store wrapper + in-memory cache (TTL 10min, max 1000)
├── migrator.go # Migration logic (LATEST.sql for fresh, incremental for upgrades)
└── db/{sqlite,mysql,postgres}/ # Driver implementations
proto/
├── api/v1/ # Service definitions
├── store/ # Internal storage messages
└── gen/ # Generated Go, TypeScript, OpenAPI
plugin/ # scheduler, cron, email, filter (CEL), webhook,
# markdown (Goldmark), httpgetter, idp (OAuth2), storage/s3
web/src/
├── connect.ts # Connect RPC client + auth interceptor + token refresh
├── auth-state.ts # Token storage (localStorage + BroadcastChannel cross-tab)
├── contexts/ # AuthContext, InstanceContext, ViewContext, MemoFilterContext
├── hooks/ # React Query hooks (useMemoQueries, useUserQueries, etc.)
├── lib/query-client.ts # React Query v5 (staleTime: 30s, gcTime: 5min)
├── router/index.tsx # Route definitions
├── components/ # UI components (Radix UI primitives, MemoEditor, Settings, etc.)
├── themes/ # CSS themes (default, dark, paper) — OKLch color tokens
└── pages/ # Page components
```
## Conventions
### Go
- **Errors:** `errors.Wrap(err, "context")` from `github.com/pkg/errors`. Never `fmt.Errorf` (lint-enforced via forbidigo).
- **gRPC errors:** `status.Errorf(codes.X, "message")` from service methods.
- **Imports:** stdlib, then third-party, then local (`github.com/usememos/memos`). Enforced by goimports (runs as golangci-lint formatter).
- **Comments:** All exported functions must have doc comments (godot enforced).
### Frontend
- **Imports:** Use `@/` alias for absolute imports.
- **Formatting:** Biome — 140 char lines, double quotes, always semicolons, 2-space indent.
- **State:** Server data via React Query hooks (`hooks/`). Client state via React Context (`contexts/`).
- **Styling:** Tailwind CSS v4 (`@tailwindcss/vite`), `cn()` utility (clsx + tailwind-merge), CVA for variants.
### Database & Proto
- **DB changes:** Migration files for all 3 drivers + update `LATEST.sql`.
- **Proto changes:** Run `buf generate`. Generated code: `proto/gen/` and `web/src/types/proto/`.
- **Public endpoints:** Add to `server/router/api/v1/acl_config.go`.
## CI/CD
- **backend-tests.yml:** Go 1.26.1, golangci-lint v2.4.0, tests parallelized by group (store, server, plugin, other)
- **frontend-tests.yml:** Node 24, pnpm 10, lint + build
- **proto-linter.yml:** buf lint + format check
- **Docker:** Multi-stage (`scripts/Dockerfile`), Alpine 3.21, non-root user, port 5230, multi-arch (amd64/arm64/arm/v7)
================================================
FILE: CLAUDE.md
================================================
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
See `AGENTS.md` for full architecture, workflows, conventions, and patterns.
================================================
FILE: CODEOWNERS
================================================
# These owners will be the default owners for everything in the repo.
* @usememos/moderators
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2025 Memos
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<div align="center">
<p>
<span>Featured Sponsor:</span>
<a href="https://go.warp.dev/memos" target="_blank" rel="noopener">
<b>Warp</b>
<span>— The AI-powered terminal built for speed and collaboration</span>
</a>
</p>
<a href="https://go.warp.dev/memos" target="_blank" rel="noopener">
<img alt="Warp sponsorship" height="196" src="https://raw.githubusercontent.com/warpdotdev/brand-assets/refs/heads/main/Github/Sponsor/Warp-Github-LG-03.png">
</a>
</div>
# Memos
<img align="right" height="96px" src="https://raw.githubusercontent.com/usememos/.github/refs/heads/main/assets/logo-rounded.png" alt="Memos" />
Open-source, self-hosted note-taking tool built for quick capture. Markdown-native, lightweight, and fully yours.
[](https://usememos.com)
[](https://demo.usememos.com/)
[](https://usememos.com/docs)
[](https://discord.gg/tfPJa4UmAv)
[](https://hub.docker.com/r/neosmemo/memos)
<img src="https://raw.githubusercontent.com/usememos/.github/refs/heads/main/assets/demo.png" alt="Memos Demo Screenshot" height="512" />
### 💎 Featured Sponsors
[**Warp** — The AI-powered terminal built for speed and collaboration](https://go.warp.dev/memos)
<a href="https://go.warp.dev/memos" target="_blank" rel="noopener">
<img src="https://raw.githubusercontent.com/warpdotdev/brand-assets/refs/heads/main/Logos/Warp-Wordmark-Black.png" alt="Warp - The AI-powered terminal built for speed and collaboration" height="44" />
</a>
<p></p>
[**TestMu AI** - The world’s first full-stack Agentic AI Quality Engineering platform](https://www.testmuai.com/?utm_medium=sponsor&utm_source=memos)
<a href="https://www.testmuai.com/?utm_medium=sponsor&utm_source=memos" target="_blank" rel="noopener">
<img src="https://usememos.com/sponsors/testmu.svg" alt="TestMu AI" height="36" />
</a>
<p></p>
[**SSD Nodes** - Affordable VPS hosting for self-hosters](https://ssdnodes.com/?utm_source=memos&utm_medium=sponsor)
<a href="https://ssdnodes.com/?utm_source=memos&utm_medium=sponsor" target="_blank" rel="noopener">
<img src="https://usememos.com/sponsors/ssd-nodes.svg" alt="SSD Nodes" height="72" />
</a>
## Features
- **Instant Capture** — Timeline-first UI. Open, write, done — no folders to navigate.
- **Total Data Ownership** — Self-hosted on your infrastructure. Notes stored in Markdown, always portable. Zero telemetry.
- **Radical Simplicity** — Single Go binary, ~20MB Docker image. One command to deploy with SQLite, MySQL, or PostgreSQL.
- **Open & Extensible** — MIT-licensed with full REST and gRPC APIs for integration.
## Quick Start
### Docker (Recommended)
```bash
docker run -d \
--name memos \
-p 5230:5230 \
-v ~/.memos:/var/opt/memos \
neosmemo/memos:stable
```
Open `http://localhost:5230` and start writing!
### Native Binary
```bash
curl -fsSL https://raw.githubusercontent.com/usememos/memos/main/scripts/install.sh | sh
```
### Try the Live Demo
Don't want to install yet? Try our [live demo](https://demo.usememos.com/) first!
### Other Installation Methods
- **Docker Compose** - Recommended for production deployments
- **Pre-built Binaries** - Available for Linux, macOS, and Windows
- **Kubernetes** - Helm charts and manifests available
- **Build from Source** - For development and customization
See our [installation guide](https://usememos.com/docs/deploy) for detailed instructions.
## Contributing
Contributions are welcome — bug reports, feature suggestions, pull requests, documentation, and translations.
- [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)
- [Submit pull requests](https://github.com/usememos/memos/pulls)
- [Improve documentation](https://github.com/usememos/dotcom)
- [Help with translations](https://github.com/usememos/memos/tree/main/web/src/locales)
## Sponsors
Love Memos? [Sponsor us on GitHub](https://github.com/sponsors/usememos) to help keep the project growing!
## Star History
[](https://star-history.com/#usememos/memos&Date)
## License
Memos is open-source software licensed under the [MIT License](LICENSE). See our [Privacy Policy](https://usememos.com/privacy) for details on data handling.
---
**[Website](https://usememos.com)** • **[Documentation](https://usememos.com/docs)** • **[Demo](https://demo.usememos.com/)** • **[Discord](https://discord.gg/tfPJa4UmAv)** • **[X/Twitter](https://x.com/usememos)**
<a href="https://vercel.com/oss">
<img alt="Vercel OSS Program" src="https://vercel.com/oss/program-badge.svg" />
</a>
================================================
FILE: SECURITY.md
================================================
# Security Policy
## Project Status
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.
## Reporting Security Issues
### For All Security Concerns:
Please report via **email only**: dev@usememos.com
**DO NOT open public GitHub issues for security vulnerabilities.**
Include in your report:
- Description of the issue
- Steps to reproduce
- Affected versions
- Your assessment of severity
### What to Expect:
- We will acknowledge your report as soon as we can
- Fixes will be included in regular releases without special security advisories
- No CVEs will be assigned during the beta phase
- Credit will be given in release notes if you wish
### For Non-Security Bugs:
Use GitHub issues for functionality bugs, feature requests, and general questions.
## Philosophy
As a beta project, we prioritize:
1. **Rapid iteration** over lengthy disclosure timelines
2. **Quick patches** over formal security processes
3. **Transparency** about our beta status
We plan to implement formal vulnerability disclosure and CVE handling after reaching v1.0 stable.
## Self-Hosting Security
Since Memos is self-hosted software:
- Keep your instance updated to the latest release
- Don't expose your instance directly to the internet without authentication
- Use reverse proxies (nginx, Caddy) with rate limiting
- Review the deployment documentation for security best practices
Thank you for helping improve Memos!
================================================
FILE: go.mod
================================================
module github.com/usememos/memos
go 1.26.1
require (
connectrpc.com/connect v1.19.1
github.com/aws/aws-sdk-go-v2 v1.41.4
github.com/aws/aws-sdk-go-v2/config v1.32.12
github.com/aws/aws-sdk-go-v2/credentials v1.19.12
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.1
github.com/docker/docker v28.5.2+incompatible
github.com/go-sql-driver/mysql v1.9.3
github.com/google/cel-go v0.27.0
github.com/google/uuid v1.6.0
github.com/gorilla/feeds v1.2.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0
github.com/joho/godotenv v1.5.1
github.com/labstack/echo/v5 v5.0.4
github.com/lib/pq v1.11.2
github.com/lithammer/shortuuid/v4 v4.2.0
github.com/mark3labs/mcp-go v0.45.0
github.com/pkg/errors v0.9.1
github.com/spf13/cobra v1.10.2
github.com/spf13/viper v1.21.0
github.com/stretchr/testify v1.11.1
github.com/testcontainers/testcontainers-go v0.41.0
github.com/testcontainers/testcontainers-go/modules/mysql v0.41.0
github.com/testcontainers/testcontainers-go/modules/postgres v0.41.0
github.com/yuin/goldmark v1.7.16
golang.org/x/crypto v0.49.0
golang.org/x/mod v0.34.0
golang.org/x/net v0.52.0
golang.org/x/oauth2 v0.36.0
golang.org/x/sync v0.20.0
google.golang.org/genproto v0.0.0-20260316180232-0b37fe3546d5
google.golang.org/genproto/googleapis/api v0.0.0-20260316172706-e463d84ca32d
google.golang.org/grpc v1.79.2
modernc.org/sqlite v1.46.1
)
require (
cel.dev/expr v0.25.1 // indirect
dario.cat/mergo v1.0.2 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.2 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/go-connections v0.6.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/ebitengine/purego v0.10.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/invopop/jsonschema v0.13.0 // indirect
github.com/klauspost/compress v1.18.2 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.10 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/go-archive v0.2.0 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/user v0.4.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/moby/term v0.5.2 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/ncruces/go-strftime v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/shirou/gopsutil/v4 v4.26.2 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tklauser/go-sysconf v0.3.16 // indirect
github.com/tklauser/numcpus v0.11.0 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
go.opentelemetry.io/otel v1.41.0 // indirect
go.opentelemetry.io/otel/metric v1.41.0 // indirect
go.opentelemetry.io/otel/trace v1.41.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
golang.org/x/image v0.30.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c // indirect
modernc.org/libc v1.67.6 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
)
require (
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.7 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.21 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.12 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.20 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect
github.com/aws/smithy-go v1.24.2 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/disintegration/imaging v1.6.2
github.com/golang-jwt/jwt/v5 v5.3.1
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.35.0
golang.org/x/time v0.14.0 // indirect
google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v3 v3.0.1 // indirect
)
================================================
FILE: go.sum
================================================
cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14=
connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w=
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
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/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k=
github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.7 h1:3kGOqnh1pPeddVa/E37XNTaWJ8W6vrbYV9lJEkCnhuY=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.7/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI=
github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0=
github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g=
github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8=
github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.21 h1:SwGMTMLIlvDNyhMteQ6r8IJSBPlRdXX5d4idhIGbkXA=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.21/go.mod h1:UUxgWxofmOdAMuqEsSppbDtGKLfR04HGsD0HXzvhI1k=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.12 h1:qtJZ70afD3ISKWnoX3xB0J2otEqu3LqicRcDBqsj0hQ=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.12/go.mod h1:v2pNpJbRNl4vEUWEh5ytQok0zACAKfdmKS51Hotc3pQ=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.20 h1:siU1A6xjUZ2N8zjTHSXFhB9L/2OY8Dqs0xXiLjF30jA=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.20/go.mod h1:4TLZCmVJDM3FOu5P5TJP0zOlu9zWgDWU7aUxWbr+rcw=
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.1 h1:csi9NLpFZXb9fxY7rS1xVzgPRGMt7MSNWeQ6eo247kE=
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.1/go.mod h1:qXVal5H0ChqXP63t6jze5LmFalc7+ZE7wOdLtZ0LCP0=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU=
github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk=
github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=
github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
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/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
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.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/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
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/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU=
github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
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/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
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/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
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-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo=
github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw=
github.com/google/go-cmp v0.5.6/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/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
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/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.5.4 h1:Xp2aQS8uXButQdnCMWNmvx6UysWQQC+u1EoizjguY+8=
github.com/jackc/pgx/v5 v5.5.4/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo/v5 v5.0.4 h1:ll3I/O8BifjMztj9dD1vx/peZQv8cR2CTUdQK6QxGGc=
github.com/labstack/echo/v5 v5.0.4/go.mod h1:SyvlSdObGjRXeQfCCXW/sybkZdOOQZBmpKF0bvALaeo=
github.com/lib/pq v1.11.2 h1:x6gxUeu39V0BHZiugWe8LXZYZ+Utk7hSJGThs8sdzfs=
github.com/lib/pq v1.11.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
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/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mark3labs/mcp-go v0.45.0 h1:s0S8qR/9fWaQ3pHxz7pm1uQ0DrswoSnRIxKIjbiQtkc=
github.com/mark3labs/mcp-go v0.45.0/go.mod h1:YnJfOL382MIWDx1kMY+2zsRHU/q78dBg9aFb8W6Thdw=
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/mdelapenya/tlscert v0.2.0 h1:7H81W6Z/4weDvZBNOfQte5GpIMo0lGYEeWbkGp5LJHI=
github.com/mdelapenya/tlscert v0.2.0/go.mod h1:O4njj3ELLnJjGdkN7M/vIVCpZ+Cf0L6muqOG4tLSl8o=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8=
github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
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/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
github.com/shirou/gopsutil/v4 v4.26.2 h1:X8i6sicvUFih4BmYIGT1m2wwgw2VG9YgrDTi7cIRGUI=
github.com/shirou/gopsutil/v4 v4.26.2/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/testcontainers/testcontainers-go v0.41.0 h1:mfpsD0D36YgkxGj2LrIyxuwQ9i2wCKAD+ESsYM1wais=
github.com/testcontainers/testcontainers-go v0.41.0/go.mod h1:pdFrEIfaPl24zmBjerWTTYaY0M6UHsqA1YSvsoU40MI=
github.com/testcontainers/testcontainers-go/modules/mysql v0.41.0 h1:5rwejaJr5nIfw8NK99eKPX7O6k27lnSMklTj5DbYybM=
github.com/testcontainers/testcontainers-go/modules/mysql v0.41.0/go.mod h1:iMO/aFWnbjYkqHw8VPsJB3rVTOD9hKDsUtV0PvzD0DA=
github.com/testcontainers/testcontainers-go/modules/postgres v0.41.0 h1:AOtFXssrDlLm84A2sTTR/AhvJiYbrIuCO59d+Ro9Tb0=
github.com/testcontainers/testcontainers-go/modules/postgres v0.41.0/go.mod h1:k2a09UKhgSp6vNpliIY0QSgm4Hi7GXVTzWvWgUemu/8=
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
github.com/yuin/goldmark v1.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE=
github.com/yuin/goldmark v1.7.16/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c=
go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.41.0 h1:inYW9ZhgqiDqh6BioM7DVHHzEGVq76Db5897WLGZ5Go=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.41.0/go.mod h1:Izur+Wt8gClgMJqO/cZ8wdeeMryJ/xxiOVgFSSfpDTY=
go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ=
go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps=
go.opentelemetry.io/otel/sdk v1.41.0 h1:YPIEXKmiAwkGl3Gu1huk1aYWwtpRLeskpV+wPisxBp8=
go.opentelemetry.io/otel/sdk v1.41.0/go.mod h1:ahFdU0G5y8IxglBf0QBJXgSe7agzjE4GiTJ6HT9ud90=
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0=
go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis=
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
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/go.mod h1:SAEUTxCCMWSrJcCy/4HwavEsfZZJlYxeHLc6tTiAe/c=
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto v0.0.0-20260316180232-0b37fe3546d5 h1:JNfk58HZ8lfmXbYK2vx/UvsqIL59TzByCxPIX4TDmsE=
google.golang.org/genproto v0.0.0-20260316180232-0b37fe3546d5/go.mod h1:x5julN69+ED4PcFk/XWayw35O0lf/nGa4aNgODCmNmw=
google.golang.org/genproto/googleapis/api v0.0.0-20260316172706-e463d84ca32d h1:RdWlPmVySdTF0IBIZzvZJvSD0ZocPBNUsnE+uGBxj+4=
google.golang.org/genproto/googleapis/api v0.0.0-20260316172706-e463d84ca32d/go.mod h1:X2gu9Qwng7Nn009s/r3RUxqkzQNqOrAy79bluY7ojIg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c h1:xgCzyF2LFIO/0X2UAoVRiXKU5Xg6VjToG4i2/ecSswk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU=
google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc=
modernc.org/ccgo/v4 v4.30.1/go.mod h1:bIOeI1JL54Utlxn+LwrFyjCx2n2RDiYEaJVSrgdrRfM=
modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=
modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/gc/v3 v3.1.1 h1:k8T3gkXWY9sEiytKhcgyiZ2L0DTyCQ/nvX+LoCljoRE=
modernc.org/gc/v3 v3.1.1/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
modernc.org/libc v1.67.6 h1:eVOQvpModVLKOdT+LvBPjdQqfrZq+pC39BygcT+E7OI=
modernc.org/libc v1.67.6/go.mod h1:JAhxUVlolfYDErnwiqaLvUqc8nfb2r6S6slAgZOnaiE=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU=
modernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
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/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
================================================
FILE: internal/base/resource_name.go
================================================
package base
import "regexp"
var (
UIDMatcher = regexp.MustCompile("^[a-zA-Z0-9]([a-zA-Z0-9-]{0,30}[a-zA-Z0-9])?$")
)
================================================
FILE: internal/base/resource_name_test.go
================================================
package base
import (
"testing"
)
func TestUIDMatcher(t *testing.T) {
tests := []struct {
input string
expected bool
}{
{"", false},
{"-abc123", false},
{"012345678901234567890123456789", true},
{"1abc-123", true},
{"A123B456C789", true},
{"a", true},
{"ab", true},
{"a*b&c", false},
{"a--b", true},
{"a-1b-2c", true},
{"a1234567890123456789012345678901", true},
{"abc123", true},
{"abc123-", false},
}
for _, test := range tests {
t.Run(test.input, func(*testing.T) {
result := UIDMatcher.MatchString(test.input)
if result != test.expected {
t.Errorf("For input '%s', expected %v but got %v", test.input, test.expected, result)
}
})
}
}
================================================
FILE: internal/profile/profile.go
================================================
package profile
import (
"fmt"
"log/slog"
"os"
"path/filepath"
"runtime"
"strings"
"github.com/pkg/errors"
)
// Profile is the configuration to start main server.
type Profile struct {
// Demo indicates if the server is in demo mode
Demo bool
// Addr is the binding address for server
Addr string
// Port is the binding port for server
Port int
// UNIXSock is the IPC binding path. Overrides Addr and Port
UNIXSock string
// Data is the data directory
Data string
// DSN points to where memos stores its own data
DSN string
// Driver is the database driver
// sqlite, mysql
Driver string
// Version is the current version of server
Version string
// InstanceURL is the url of your memos instance.
InstanceURL string
}
func checkDataDir(dataDir string) (string, error) {
// Convert to absolute path if relative path is supplied.
if !filepath.IsAbs(dataDir) {
// Use current working directory, not the binary's directory
// This ensures we use the actual working directory where the process runs
absDir, err := filepath.Abs(dataDir)
if err != nil {
return "", err
}
dataDir = absDir
}
// Trim trailing \ or / in case user supplies
dataDir = strings.TrimRight(dataDir, "\\/")
if _, err := os.Stat(dataDir); err != nil {
return "", errors.Wrapf(err, "unable to access data folder %s", dataDir)
}
return dataDir, nil
}
func (p *Profile) Validate() error {
// Set default data directory if not specified
if p.Data == "" {
if runtime.GOOS == "windows" {
p.Data = filepath.Join(os.Getenv("ProgramData"), "memos")
} else {
// On Linux/macOS, check if /var/opt/memos exists and is writable (Docker scenario)
if info, err := os.Stat("/var/opt/memos"); err == nil && info.IsDir() {
// Check if we can write to this directory
testFile := filepath.Join("/var/opt/memos", ".write-test")
if err := os.WriteFile(testFile, []byte("test"), 0600); err == nil {
os.Remove(testFile)
p.Data = "/var/opt/memos"
} else {
// /var/opt/memos exists but is not writable, use current directory
slog.Warn("/var/opt/memos is not writable, using current directory")
p.Data = "."
}
} else {
// /var/opt/memos doesn't exist, use current directory (local development)
p.Data = "."
}
}
}
// Create data directory if it doesn't exist
if _, err := os.Stat(p.Data); os.IsNotExist(err) {
if err := os.MkdirAll(p.Data, 0770); err != nil {
slog.Error("failed to create data directory", slog.String("data", p.Data), slog.String("error", err.Error()))
return err
}
}
dataDir, err := checkDataDir(p.Data)
if err != nil {
slog.Error("failed to check dsn", slog.String("data", dataDir), slog.String("error", err.Error()))
return err
}
p.Data = dataDir
if p.Driver == "sqlite" && p.DSN == "" {
mode := "prod"
if p.Demo {
mode = "demo"
}
dbFile := fmt.Sprintf("memos_%s.db", mode)
p.DSN = filepath.Join(dataDir, dbFile)
}
return nil
}
================================================
FILE: internal/util/util.go
================================================
package util //nolint:revive // util namespace is intentional for shared helpers
import (
"crypto/rand"
"math/big"
"net/mail"
"strconv"
"strings"
"github.com/google/uuid"
)
// ConvertStringToInt32 converts a string to int32.
func ConvertStringToInt32(src string) (int32, error) {
parsed, err := strconv.ParseInt(src, 10, 32)
if err != nil {
return 0, err
}
return int32(parsed), nil
}
// HasPrefixes returns true if the string s has any of the given prefixes.
func HasPrefixes(src string, prefixes ...string) bool {
for _, prefix := range prefixes {
if strings.HasPrefix(src, prefix) {
return true
}
}
return false
}
// ValidateEmail validates the email.
func ValidateEmail(email string) bool {
if _, err := mail.ParseAddress(email); err != nil {
return false
}
return true
}
func GenUUID() string {
return uuid.New().String()
}
var letters = []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
// RandomString returns a random string with length n.
func RandomString(n int) (string, error) {
var sb strings.Builder
sb.Grow(n)
for i := 0; i < n; i++ {
// The reason for using crypto/rand instead of math/rand is that
// the former relies on hardware to generate random numbers and
// thus has a stronger source of random numbers.
randNum, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
if err != nil {
return "", err
}
if _, err := sb.WriteRune(letters[randNum.Uint64()]); err != nil {
return "", err
}
}
return sb.String(), nil
}
// ReplaceString replaces all occurrences of old in slice with new.
func ReplaceString(slice []string, old, new string) []string {
for i, s := range slice {
if s == old {
slice[i] = new
}
}
return slice
}
================================================
FILE: internal/util/util_test.go
================================================
package util //nolint:revive // util is an appropriate package name for utility functions
import (
"testing"
)
func TestValidateEmail(t *testing.T) {
tests := []struct {
email string
want bool
}{
{
email: "t@gmail.com",
want: true,
},
{
email: "@usememos.com",
want: false,
},
{
email: "1@gmail",
want: true,
},
}
for _, test := range tests {
result := ValidateEmail(test.email)
if result != test.want {
t.Errorf("Validate Email %s: got result %v, want %v.", test.email, result, test.want)
}
}
}
================================================
FILE: internal/version/version.go
================================================
package version
import (
"fmt"
"strings"
"golang.org/x/mod/semver"
)
// Version is the service current released version.
// Semantic versioning: https://semver.org/
var Version = "0.27.0"
func GetCurrentVersion() string {
return Version
}
// GetMinorVersion extracts the minor version (e.g., "0.25") from a full version string (e.g., "0.25.1").
// Returns the minor version string or empty string if the version format is invalid.
// Version format should be "major.minor.patch" (e.g., "0.25.1").
func GetMinorVersion(version string) string {
versionList := strings.Split(version, ".")
if len(versionList) < 2 {
return ""
}
// Return major.minor only (first two components)
return versionList[0] + "." + versionList[1]
}
// IsVersionGreaterOrEqualThan returns true if version is greater than or equal to target.
func IsVersionGreaterOrEqualThan(version, target string) bool {
return semver.Compare(fmt.Sprintf("v%s", version), fmt.Sprintf("v%s", target)) > -1
}
// IsVersionGreaterThan returns true if version is greater than target.
func IsVersionGreaterThan(version, target string) bool {
return semver.Compare(fmt.Sprintf("v%s", version), fmt.Sprintf("v%s", target)) > 0
}
type SortVersion []string
func (s SortVersion) Len() int {
return len(s)
}
func (s SortVersion) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s SortVersion) Less(i, j int) bool {
v1 := fmt.Sprintf("v%s", s[i])
v2 := fmt.Sprintf("v%s", s[j])
return semver.Compare(v1, v2) == -1
}
================================================
FILE: internal/version/version_test.go
================================================
package version
import (
"slices"
"testing"
"github.com/stretchr/testify/assert"
"golang.org/x/mod/semver"
)
func TestIsVersionGreaterOrEqualThan(t *testing.T) {
tests := []struct {
version string
target string
want bool
}{
{
version: "0.9.1",
target: "0.9.1",
want: true,
},
{
version: "0.10.0",
target: "0.9.1",
want: true,
},
{
version: "0.9.0",
target: "0.9.1",
want: false,
},
}
for _, test := range tests {
result := IsVersionGreaterOrEqualThan(test.version, test.target)
if result != test.want {
t.Errorf("got result %v, want %v.", result, test.want)
}
}
}
func TestIsVersionGreaterThan(t *testing.T) {
tests := []struct {
version string
target string
want bool
}{
{
version: "0.9.1",
target: "0.9.1",
want: false,
},
{
version: "0.10.0",
target: "0.8.0",
want: true,
},
{
version: "0.23",
target: "0.22",
want: true,
},
{
version: "0.8.0",
target: "0.10.0",
want: false,
},
{
version: "0.9.0",
target: "0.9.1",
want: false,
},
{
version: "0.22",
target: "0.22",
want: false,
},
}
for _, test := range tests {
result := IsVersionGreaterThan(test.version, test.target)
if result != test.want {
t.Errorf("got result %v, want %v.", result, test.want)
}
}
}
func TestSortVersion(t *testing.T) {
tests := []struct {
versionList []string
want []string
}{
{
versionList: []string{"0.9.1", "0.10.0", "0.8.0"},
want: []string{"0.8.0", "0.9.1", "0.10.0"},
},
{
versionList: []string{"1.9.1", "0.9.1", "0.10.0", "0.8.0"},
want: []string{"0.8.0", "0.9.1", "0.10.0", "1.9.1"},
},
}
for _, test := range tests {
slices.SortFunc(test.versionList, func(a, b string) int {
return semver.Compare("v"+a, "v"+b)
})
assert.Equal(t, test.versionList, test.want)
}
}
================================================
FILE: plugin/cron/README.md
================================================
Fork from https://github.com/robfig/cron
================================================
FILE: plugin/cron/chain.go
================================================
package cron
import (
"errors"
"fmt"
"runtime"
"sync"
"time"
)
// JobWrapper decorates the given Job with some behavior.
type JobWrapper func(Job) Job
// Chain is a sequence of JobWrappers that decorates submitted jobs with
// cross-cutting behaviors like logging or synchronization.
type Chain struct {
wrappers []JobWrapper
}
// NewChain returns a Chain consisting of the given JobWrappers.
func NewChain(c ...JobWrapper) Chain {
return Chain{c}
}
// Then decorates the given job with all JobWrappers in the chain.
//
// This:
//
// NewChain(m1, m2, m3).Then(job)
//
// is equivalent to:
//
// m1(m2(m3(job)))
func (c Chain) Then(j Job) Job {
for i := range c.wrappers {
j = c.wrappers[len(c.wrappers)-i-1](j)
}
return j
}
// Recover panics in wrapped jobs and log them with the provided logger.
func Recover(logger Logger) JobWrapper {
return func(j Job) Job {
return FuncJob(func() {
defer func() {
if r := recover(); r != nil {
const size = 64 << 10
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
err, ok := r.(error)
if !ok {
err = errors.New("panic: " + fmt.Sprint(r))
}
logger.Error(err, "panic", "stack", "...\n"+string(buf))
}
}()
j.Run()
})
}
}
// DelayIfStillRunning serializes jobs, delaying subsequent runs until the
// previous one is complete. Jobs running after a delay of more than a minute
// have the delay logged at Info.
func DelayIfStillRunning(logger Logger) JobWrapper {
return func(j Job) Job {
var mu sync.Mutex
return FuncJob(func() {
start := time.Now()
mu.Lock()
defer mu.Unlock()
if dur := time.Since(start); dur > time.Minute {
logger.Info("delay", "duration", dur)
}
j.Run()
})
}
}
// SkipIfStillRunning skips an invocation of the Job if a previous invocation is
// still running. It logs skips to the given logger at Info level.
func SkipIfStillRunning(logger Logger) JobWrapper {
return func(j Job) Job {
var ch = make(chan struct{}, 1)
ch <- struct{}{}
return FuncJob(func() {
select {
case v := <-ch:
defer func() { ch <- v }()
j.Run()
default:
logger.Info("skip")
}
})
}
}
================================================
FILE: plugin/cron/chain_test.go
================================================
//nolint:all
package cron
import (
"io"
"log"
"reflect"
"sync"
"testing"
"time"
)
func waitFor(t *testing.T, timeout time.Duration, fn func() bool) {
t.Helper()
deadline := time.Now().Add(timeout)
for time.Now().Before(deadline) {
if fn() {
return
}
time.Sleep(time.Millisecond)
}
t.Fatal("condition not met before timeout")
}
func appendingJob(slice *[]int, value int) Job {
var m sync.Mutex
return FuncJob(func() {
m.Lock()
*slice = append(*slice, value)
m.Unlock()
})
}
func appendingWrapper(slice *[]int, value int) JobWrapper {
return func(j Job) Job {
return FuncJob(func() {
appendingJob(slice, value).Run()
j.Run()
})
}
}
func TestChain(t *testing.T) {
var nums []int
var (
append1 = appendingWrapper(&nums, 1)
append2 = appendingWrapper(&nums, 2)
append3 = appendingWrapper(&nums, 3)
append4 = appendingJob(&nums, 4)
)
NewChain(append1, append2, append3).Then(append4).Run()
if !reflect.DeepEqual(nums, []int{1, 2, 3, 4}) {
t.Error("unexpected order of calls:", nums)
}
}
func TestChainRecover(t *testing.T) {
panickingJob := FuncJob(func() {
panic("panickingJob panics")
})
t.Run("panic exits job by default", func(*testing.T) {
defer func() {
if err := recover(); err == nil {
t.Errorf("panic expected, but none received")
}
}()
NewChain().Then(panickingJob).
Run()
})
t.Run("Recovering JobWrapper recovers", func(*testing.T) {
NewChain(Recover(PrintfLogger(log.New(io.Discard, "", 0)))).
Then(panickingJob).
Run()
})
t.Run("composed with the *IfStillRunning wrappers", func(*testing.T) {
NewChain(Recover(PrintfLogger(log.New(io.Discard, "", 0)))).
Then(panickingJob).
Run()
})
}
type countJob struct {
m sync.Mutex
started int
done int
delay time.Duration
}
func (j *countJob) Run() {
j.m.Lock()
j.started++
j.m.Unlock()
time.Sleep(j.delay)
j.m.Lock()
j.done++
j.m.Unlock()
}
func (j *countJob) Started() int {
defer j.m.Unlock()
j.m.Lock()
return j.started
}
func (j *countJob) Done() int {
defer j.m.Unlock()
j.m.Lock()
return j.done
}
func TestChainDelayIfStillRunning(t *testing.T) {
t.Run("runs immediately", func(*testing.T) {
var j countJob
wrappedJob := NewChain(DelayIfStillRunning(DiscardLogger)).Then(&j)
go wrappedJob.Run()
waitFor(t, 100*time.Millisecond, func() bool { return j.Done() == 1 })
if c := j.Done(); c != 1 {
t.Errorf("expected job run once, immediately, got %d", c)
}
})
t.Run("second run immediate if first done", func(*testing.T) {
var j countJob
wrappedJob := NewChain(DelayIfStillRunning(DiscardLogger)).Then(&j)
go func() {
go wrappedJob.Run()
time.Sleep(time.Millisecond)
go wrappedJob.Run()
}()
waitFor(t, 100*time.Millisecond, func() bool { return j.Done() == 2 })
if c := j.Done(); c != 2 {
t.Errorf("expected job run twice, immediately, got %d", c)
}
})
t.Run("second run delayed if first not done", func(*testing.T) {
var j countJob
j.delay = 10 * time.Millisecond
wrappedJob := NewChain(DelayIfStillRunning(DiscardLogger)).Then(&j)
go func() {
go wrappedJob.Run()
time.Sleep(time.Millisecond)
go wrappedJob.Run()
}()
waitFor(t, 100*time.Millisecond, func() bool { return j.Started() == 1 })
started, done := j.Started(), j.Done()
if done != 0 {
t.Error("expected first job started, but not finished, got", started, done)
}
waitFor(t, 200*time.Millisecond, func() bool { return j.Done() == 2 })
started, done = j.Started(), j.Done()
if started != 2 || done != 2 {
t.Error("expected both jobs done, got", started, done)
}
})
}
func TestChainSkipIfStillRunning(t *testing.T) {
t.Run("runs immediately", func(*testing.T) {
var j countJob
wrappedJob := NewChain(SkipIfStillRunning(DiscardLogger)).Then(&j)
go wrappedJob.Run()
time.Sleep(2 * time.Millisecond) // Give the job 2ms to complete.
if c := j.Done(); c != 1 {
t.Errorf("expected job run once, immediately, got %d", c)
}
})
t.Run("second run immediate if first done", func(*testing.T) {
var j countJob
wrappedJob := NewChain(SkipIfStillRunning(DiscardLogger)).Then(&j)
go func() {
go wrappedJob.Run()
time.Sleep(time.Millisecond)
go wrappedJob.Run()
}()
time.Sleep(3 * time.Millisecond) // Give both jobs 3ms to complete.
if c := j.Done(); c != 2 {
t.Errorf("expected job run twice, immediately, got %d", c)
}
})
t.Run("second run skipped if first not done", func(*testing.T) {
var j countJob
j.delay = 10 * time.Millisecond
wrappedJob := NewChain(SkipIfStillRunning(DiscardLogger)).Then(&j)
go func() {
go wrappedJob.Run()
time.Sleep(time.Millisecond)
go wrappedJob.Run()
}()
// After 5ms, the first job is still in progress, and the second job was
// already skipped.
time.Sleep(5 * time.Millisecond)
started, done := j.Started(), j.Done()
if started != 1 || done != 0 {
t.Error("expected first job started, but not finished, got", started, done)
}
// Verify that the first job completes and second does not run.
time.Sleep(25 * time.Millisecond)
started, done = j.Started(), j.Done()
if started != 1 || done != 1 {
t.Error("expected second job skipped, got", started, done)
}
})
t.Run("skip 10 jobs on rapid fire", func(*testing.T) {
var j countJob
j.delay = 10 * time.Millisecond
wrappedJob := NewChain(SkipIfStillRunning(DiscardLogger)).Then(&j)
for i := 0; i < 11; i++ {
go wrappedJob.Run()
}
time.Sleep(200 * time.Millisecond)
done := j.Done()
if done != 1 {
t.Error("expected 1 jobs executed, 10 jobs dropped, got", done)
}
})
t.Run("different jobs independent", func(*testing.T) {
var j1, j2 countJob
j1.delay = 10 * time.Millisecond
j2.delay = 10 * time.Millisecond
chain := NewChain(SkipIfStillRunning(DiscardLogger))
wrappedJob1 := chain.Then(&j1)
wrappedJob2 := chain.Then(&j2)
for i := 0; i < 11; i++ {
go wrappedJob1.Run()
go wrappedJob2.Run()
}
time.Sleep(100 * time.Millisecond)
var (
done1 = j1.Done()
done2 = j2.Done()
)
if done1 != 1 || done2 != 1 {
t.Error("expected both jobs executed once, got", done1, "and", done2)
}
})
}
================================================
FILE: plugin/cron/constantdelay.go
================================================
package cron
import "time"
// ConstantDelaySchedule represents a simple recurring duty cycle, e.g. "Every 5 minutes".
// It does not support jobs more frequent than once a second.
type ConstantDelaySchedule struct {
Delay time.Duration
}
// Every returns a crontab Schedule that activates once every duration.
// Delays of less than a second are not supported (will round up to 1 second).
// Any fields less than a Second are truncated.
func Every(duration time.Duration) ConstantDelaySchedule {
if duration < time.Second {
duration = time.Second
}
return ConstantDelaySchedule{
Delay: duration - time.Duration(duration.Nanoseconds())%time.Second,
}
}
// Next returns the next time this should be run.
// This rounds so that the next activation time will be on the second.
func (schedule ConstantDelaySchedule) Next(t time.Time) time.Time {
return t.Add(schedule.Delay - time.Duration(t.Nanosecond())*time.Nanosecond)
}
================================================
FILE: plugin/cron/constantdelay_test.go
================================================
//nolint:all
package cron
import (
"testing"
"time"
)
func TestConstantDelayNext(t *testing.T) {
tests := []struct {
time string
delay time.Duration
expected string
}{
// Simple cases
{"Mon Jul 9 14:45 2012", 15*time.Minute + 50*time.Nanosecond, "Mon Jul 9 15:00 2012"},
{"Mon Jul 9 14:59 2012", 15 * time.Minute, "Mon Jul 9 15:14 2012"},
{"Mon Jul 9 14:59:59 2012", 15 * time.Minute, "Mon Jul 9 15:14:59 2012"},
// Wrap around hours
{"Mon Jul 9 15:45 2012", 35 * time.Minute, "Mon Jul 9 16:20 2012"},
// Wrap around days
{"Mon Jul 9 23:46 2012", 14 * time.Minute, "Tue Jul 10 00:00 2012"},
{"Mon Jul 9 23:45 2012", 35 * time.Minute, "Tue Jul 10 00:20 2012"},
{"Mon Jul 9 23:35:51 2012", 44*time.Minute + 24*time.Second, "Tue Jul 10 00:20:15 2012"},
{"Mon Jul 9 23:35:51 2012", 25*time.Hour + 44*time.Minute + 24*time.Second, "Thu Jul 11 01:20:15 2012"},
// Wrap around months
{"Mon Jul 9 23:35 2012", 91*24*time.Hour + 25*time.Minute, "Thu Oct 9 00:00 2012"},
// Wrap around minute, hour, day, month, and year
{"Mon Dec 31 23:59:45 2012", 15 * time.Second, "Tue Jan 1 00:00:00 2013"},
// Round to nearest second on the delay
{"Mon Jul 9 14:45 2012", 15*time.Minute + 50*time.Nanosecond, "Mon Jul 9 15:00 2012"},
// Round up to 1 second if the duration is less.
{"Mon Jul 9 14:45:00 2012", 15 * time.Millisecond, "Mon Jul 9 14:45:01 2012"},
// Round to nearest second when calculating the next time.
{"Mon Jul 9 14:45:00.005 2012", 15 * time.Minute, "Mon Jul 9 15:00 2012"},
// Round to nearest second for both.
{"Mon Jul 9 14:45:00.005 2012", 15*time.Minute + 50*time.Nanosecond, "Mon Jul 9 15:00 2012"},
}
for _, c := range tests {
actual := Every(c.delay).Next(getTime(c.time))
expected := getTime(c.expected)
if actual != expected {
t.Errorf("%s, \"%s\": (expected) %v != %v (actual)", c.time, c.delay, expected, actual)
}
}
}
================================================
FILE: plugin/cron/cron.go
================================================
package cron
import (
"context"
"slices"
"sync"
"time"
)
// Cron keeps track of any number of entries, invoking the associated func as
// specified by the schedule. It may be started, stopped, and the entries may
// be inspected while running.
type Cron struct {
entries []*Entry
chain Chain
stop chan struct{}
add chan *Entry
remove chan EntryID
snapshot chan chan []Entry
running bool
logger Logger
runningMu sync.Mutex
location *time.Location
parser ScheduleParser
nextID EntryID
jobWaiter sync.WaitGroup
}
// ScheduleParser is an interface for schedule spec parsers that return a Schedule.
type ScheduleParser interface {
Parse(spec string) (Schedule, error)
}
// Job is an interface for submitted cron jobs.
type Job interface {
Run()
}
// Schedule describes a job's duty cycle.
type Schedule interface {
// Next returns the next activation time, later than the given time.
// Next is invoked initially, and then each time the job is run.
Next(time.Time) time.Time
}
// EntryID identifies an entry within a Cron instance.
type EntryID int
// Entry consists of a schedule and the func to execute on that schedule.
type Entry struct {
// ID is the cron-assigned ID of this entry, which may be used to look up a
// snapshot or remove it.
ID EntryID
// Schedule on which this job should be run.
Schedule Schedule
// Next time the job will run, or the zero time if Cron has not been
// started or this entry's schedule is unsatisfiable
Next time.Time
// Prev is the last time this job was run, or the zero time if never.
Prev time.Time
// WrappedJob is the thing to run when the Schedule is activated.
WrappedJob Job
// Job is the thing that was submitted to cron.
// It is kept around so that user code that needs to get at the job later,
// e.g. via Entries() can do so.
Job Job
}
// Valid returns true if this is not the zero entry.
func (e Entry) Valid() bool { return e.ID != 0 }
// New returns a new Cron job runner, modified by the given options.
//
// Available Settings
//
// Time Zone
// Description: The time zone in which schedules are interpreted
// Default: time.Local
//
// Parser
// Description: Parser converts cron spec strings into cron.Schedules.
// Default: Accepts this spec: https://en.wikipedia.org/wiki/Cron
//
// Chain
// Description: Wrap submitted jobs to customize behavior.
// Default: A chain that recovers panics and logs them to stderr.
//
// See "cron.With*" to modify the default behavior.
func New(opts ...Option) *Cron {
c := &Cron{
entries: nil,
chain: NewChain(),
add: make(chan *Entry),
stop: make(chan struct{}),
snapshot: make(chan chan []Entry),
remove: make(chan EntryID),
running: false,
runningMu: sync.Mutex{},
logger: DefaultLogger,
location: time.Local,
parser: standardParser,
}
for _, opt := range opts {
opt(c)
}
return c
}
// FuncJob is a wrapper that turns a func() into a cron.Job.
type FuncJob func()
func (f FuncJob) Run() { f() }
// AddFunc adds a func to the Cron to be run on the given schedule.
// The spec is parsed using the time zone of this Cron instance as the default.
// An opaque ID is returned that can be used to later remove it.
func (c *Cron) AddFunc(spec string, cmd func()) (EntryID, error) {
return c.AddJob(spec, FuncJob(cmd))
}
// AddJob adds a Job to the Cron to be run on the given schedule.
// The spec is parsed using the time zone of this Cron instance as the default.
// An opaque ID is returned that can be used to later remove it.
func (c *Cron) AddJob(spec string, cmd Job) (EntryID, error) {
schedule, err := c.parser.Parse(spec)
if err != nil {
return 0, err
}
return c.Schedule(schedule, cmd), nil
}
// Schedule adds a Job to the Cron to be run on the given schedule.
// The job is wrapped with the configured Chain.
func (c *Cron) Schedule(schedule Schedule, cmd Job) EntryID {
c.runningMu.Lock()
defer c.runningMu.Unlock()
c.nextID++
entry := &Entry{
ID: c.nextID,
Schedule: schedule,
WrappedJob: c.chain.Then(cmd),
Job: cmd,
}
if !c.running {
c.entries = append(c.entries, entry)
} else {
c.add <- entry
}
return entry.ID
}
// Entries returns a snapshot of the cron entries.
func (c *Cron) Entries() []Entry {
c.runningMu.Lock()
defer c.runningMu.Unlock()
if c.running {
replyChan := make(chan []Entry, 1)
c.snapshot <- replyChan
return <-replyChan
}
return c.entrySnapshot()
}
// Location gets the time zone location.
func (c *Cron) Location() *time.Location {
return c.location
}
// Entry returns a snapshot of the given entry, or nil if it couldn't be found.
func (c *Cron) Entry(id EntryID) Entry {
for _, entry := range c.Entries() {
if id == entry.ID {
return entry
}
}
return Entry{}
}
// Remove an entry from being run in the future.
func (c *Cron) Remove(id EntryID) {
c.runningMu.Lock()
defer c.runningMu.Unlock()
if c.running {
c.remove <- id
} else {
c.removeEntry(id)
}
}
// Start the cron scheduler in its own goroutine, or no-op if already started.
func (c *Cron) Start() {
c.runningMu.Lock()
defer c.runningMu.Unlock()
if c.running {
return
}
c.running = true
go c.runScheduler()
}
// Run the cron scheduler, or no-op if already running.
func (c *Cron) Run() {
c.runningMu.Lock()
if c.running {
c.runningMu.Unlock()
return
}
c.running = true
c.runningMu.Unlock()
c.runScheduler()
}
// runScheduler runs the scheduler.. this is private just due to the need to synchronize
// access to the 'running' state variable.
func (c *Cron) runScheduler() {
c.logger.Info("start")
// Figure out the next activation times for each entry.
now := c.now()
for _, entry := range c.entries {
entry.Next = entry.Schedule.Next(now)
c.logger.Info("schedule", "now", now, "entry", entry.ID, "next", entry.Next)
}
for {
// Determine the next entry to run.
slices.SortFunc(c.entries, func(a, b *Entry) int {
switch {
case a.Next.IsZero() && b.Next.IsZero():
return 0
case a.Next.IsZero():
return 1
case b.Next.IsZero():
return -1
case a.Next.Before(b.Next):
return -1
case b.Next.Before(a.Next):
return 1
default:
return 0
}
})
var timer *time.Timer
if len(c.entries) == 0 || c.entries[0].Next.IsZero() {
// If there are no entries yet, just sleep - it still handles new entries
// and stop requests.
timer = time.NewTimer(100000 * time.Hour)
} else {
timer = time.NewTimer(c.entries[0].Next.Sub(now))
}
for {
select {
case now = <-timer.C:
now = now.In(c.location)
c.logger.Info("wake", "now", now)
// Run every entry whose next time was less than now
for _, e := range c.entries {
if e.Next.After(now) || e.Next.IsZero() {
break
}
c.startJob(e.WrappedJob)
e.Prev = e.Next
e.Next = e.Schedule.Next(now)
c.logger.Info("run", "now", now, "entry", e.ID, "next", e.Next)
}
case newEntry := <-c.add:
timer.Stop()
now = c.now()
newEntry.Next = newEntry.Schedule.Next(now)
c.entries = append(c.entries, newEntry)
c.logger.Info("added", "now", now, "entry", newEntry.ID, "next", newEntry.Next)
case replyChan := <-c.snapshot:
replyChan <- c.entrySnapshot()
continue
case <-c.stop:
timer.Stop()
c.logger.Info("stop")
return
case id := <-c.remove:
timer.Stop()
now = c.now()
c.removeEntry(id)
c.logger.Info("removed", "entry", id)
}
break
}
}
}
// startJob runs the given job in a new goroutine.
func (c *Cron) startJob(j Job) {
c.jobWaiter.Go(func() {
j.Run()
})
}
// now returns current time in c location.
func (c *Cron) now() time.Time {
return time.Now().In(c.location)
}
// Stop stops the cron scheduler if it is running; otherwise it does nothing.
// A context is returned so the caller can wait for running jobs to complete.
func (c *Cron) Stop() context.Context {
c.runningMu.Lock()
defer c.runningMu.Unlock()
if c.running {
c.stop <- struct{}{}
c.running = false
}
ctx, cancel := context.WithCancel(context.Background())
go func() {
c.jobWaiter.Wait()
cancel()
}()
return ctx
}
// entrySnapshot returns a copy of the current cron entry list.
func (c *Cron) entrySnapshot() []Entry {
var entries = make([]Entry, len(c.entries))
for i, e := range c.entries {
entries[i] = *e
}
return entries
}
func (c *Cron) removeEntry(id EntryID) {
var entries []*Entry
for _, e := range c.entries {
if e.ID != id {
entries = append(entries, e)
}
}
c.entries = entries
}
================================================
FILE: plugin/cron/cron_test.go
================================================
//nolint:all
package cron
import (
"bytes"
"fmt"
"log"
"strings"
"sync"
"sync/atomic"
"testing"
"time"
)
// Many tests schedule a job for every second, and then wait at most a second
// for it to run. This amount is just slightly larger than 1 second to
// compensate for a few milliseconds of runtime.
const OneSecond = 1*time.Second + 50*time.Millisecond
type syncWriter struct {
wr bytes.Buffer
m sync.Mutex
}
func (sw *syncWriter) Write(data []byte) (n int, err error) {
sw.m.Lock()
n, err = sw.wr.Write(data)
sw.m.Unlock()
return
}
func (sw *syncWriter) String() string {
sw.m.Lock()
defer sw.m.Unlock()
return sw.wr.String()
}
func newBufLogger(sw *syncWriter) Logger {
return PrintfLogger(log.New(sw, "", log.LstdFlags))
}
func TestFuncPanicRecovery(t *testing.T) {
var buf syncWriter
cron := New(WithParser(secondParser),
WithChain(Recover(newBufLogger(&buf))))
cron.Start()
defer cron.Stop()
cron.AddFunc("* * * * * ?", func() {
panic("YOLO")
})
select {
case <-time.After(OneSecond):
if !strings.Contains(buf.String(), "YOLO") {
t.Error("expected a panic to be logged, got none")
}
return
}
}
type DummyJob struct{}
func (DummyJob) Run() {
panic("YOLO")
}
func TestJobPanicRecovery(t *testing.T) {
var job DummyJob
var buf syncWriter
cron := New(WithParser(secondParser),
WithChain(Recover(newBufLogger(&buf))))
cron.Start()
defer cron.Stop()
cron.AddJob("* * * * * ?", job)
select {
case <-time.After(OneSecond):
if !strings.Contains(buf.String(), "YOLO") {
t.Error("expected a panic to be logged, got none")
}
return
}
}
// Start and stop cron with no entries.
func TestNoEntries(t *testing.T) {
cron := newWithSeconds()
cron.Start()
select {
case <-time.After(OneSecond):
t.Fatal("expected cron will be stopped immediately")
case <-stop(cron):
}
}
// Start, stop, then add an entry. Verify entry doesn't run.
func TestStopCausesJobsToNotRun(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
cron := newWithSeconds()
cron.Start()
cron.Stop()
cron.AddFunc("* * * * * ?", func() { wg.Done() })
select {
case <-time.After(OneSecond):
// No job ran!
case <-wait(wg):
t.Fatal("expected stopped cron does not run any job")
}
}
// Add a job, start cron, expect it runs.
func TestAddBeforeRunning(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
cron := newWithSeconds()
cron.AddFunc("* * * * * ?", func() { wg.Done() })
cron.Start()
defer cron.Stop()
// Give cron 2 seconds to run our job (which is always activated).
select {
case <-time.After(OneSecond):
t.Fatal("expected job runs")
case <-wait(wg):
}
}
// Start cron, add a job, expect it runs.
func TestAddWhileRunning(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
cron := newWithSeconds()
cron.Start()
defer cron.Stop()
cron.AddFunc("* * * * * ?", func() { wg.Done() })
select {
case <-time.After(OneSecond):
t.Fatal("expected job runs")
case <-wait(wg):
}
}
// Test for #34. Adding a job after calling start results in multiple job invocations
func TestAddWhileRunningWithDelay(t *testing.T) {
cron := newWithSeconds()
cron.Start()
defer cron.Stop()
time.Sleep(5 * time.Second)
var calls int64
cron.AddFunc("* * * * * *", func() { atomic.AddInt64(&calls, 1) })
<-time.After(OneSecond)
if atomic.LoadInt64(&calls) != 1 {
t.Errorf("called %d times, expected 1\n", calls)
}
}
// Add a job, remove a job, start cron, expect nothing runs.
func TestRemoveBeforeRunning(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
cron := newWithSeconds()
id, _ := cron.AddFunc("* * * * * ?", func() { wg.Done() })
cron.Remove(id)
cron.Start()
defer cron.Stop()
select {
case <-time.After(OneSecond):
// Success, shouldn't run
case <-wait(wg):
t.FailNow()
}
}
// Start cron, add a job, remove it, expect it doesn't run.
func TestRemoveWhileRunning(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
cron := newWithSeconds()
cron.Start()
defer cron.Stop()
id, _ := cron.AddFunc("* * * * * ?", func() { wg.Done() })
cron.Remove(id)
select {
case <-time.After(OneSecond):
case <-wait(wg):
t.FailNow()
}
}
// Test timing with Entries.
func TestSnapshotEntries(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
cron := New()
cron.AddFunc("@every 2s", func() { wg.Done() })
cron.Start()
defer cron.Stop()
// Cron should fire in 2 seconds. After 1 second, call Entries.
select {
case <-time.After(OneSecond):
cron.Entries()
}
// Even though Entries was called, the cron should fire at the 2 second mark.
select {
case <-time.After(OneSecond):
t.Error("expected job runs at 2 second mark")
case <-wait(wg):
}
}
// Test that the entries are correctly sorted.
// Add a bunch of long-in-the-future entries, and an immediate entry, and ensure
// that the immediate entry runs immediately.
// Also: Test that multiple jobs run in the same instant.
func TestMultipleEntries(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(2)
cron := newWithSeconds()
cron.AddFunc("0 0 0 1 1 ?", func() {})
cron.AddFunc("* * * * * ?", func() { wg.Done() })
id1, _ := cron.AddFunc("* * * * * ?", func() { t.Fatal() })
id2, _ := cron.AddFunc("* * * * * ?", func() { t.Fatal() })
cron.AddFunc("0 0 0 31 12 ?", func() {})
cron.AddFunc("* * * * * ?", func() { wg.Done() })
cron.Remove(id1)
cron.Start()
cron.Remove(id2)
defer cron.Stop()
select {
case <-time.After(OneSecond):
t.Error("expected job run in proper order")
case <-wait(wg):
}
}
// Test running the same job twice.
func TestRunningJobTwice(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(2)
cron := newWithSeconds()
cron.AddFunc("0 0 0 1 1 ?", func() {})
cron.AddFunc("0 0 0 31 12 ?", func() {})
cron.AddFunc("* * * * * ?", func() { wg.Done() })
cron.Start()
defer cron.Stop()
select {
case <-time.After(2 * OneSecond):
t.Error("expected job fires 2 times")
case <-wait(wg):
}
}
func TestRunningMultipleSchedules(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(2)
cron := newWithSeconds()
cron.AddFunc("0 0 0 1 1 ?", func() {})
cron.AddFunc("0 0 0 31 12 ?", func() {})
cron.AddFunc("* * * * * ?", func() { wg.Done() })
cron.Schedule(Every(time.Minute), FuncJob(func() {}))
cron.Schedule(Every(time.Second), FuncJob(func() { wg.Done() }))
cron.Schedule(Every(time.Hour), FuncJob(func() {}))
cron.Start()
defer cron.Stop()
select {
case <-time.After(2 * OneSecond):
t.Error("expected job fires 2 times")
case <-wait(wg):
}
}
// Test that the cron is run in the local time zone (as opposed to UTC).
func TestLocalTimezone(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(2)
now := time.Now()
// FIX: Issue #205
// This calculation doesn't work in seconds 58 or 59.
// Take the easy way out and sleep.
if now.Second() >= 58 {
time.Sleep(2 * time.Second)
now = time.Now()
}
spec := fmt.Sprintf("%d,%d %d %d %d %d ?",
now.Second()+1, now.Second()+2, now.Minute(), now.Hour(), now.Day(), now.Month())
cron := newWithSeconds()
cron.AddFunc(spec, func() { wg.Done() })
cron.Start()
defer cron.Stop()
select {
case <-time.After(OneSecond * 2):
t.Error("expected job fires 2 times")
case <-wait(wg):
}
}
// Test that the cron is run in the given time zone (as opposed to local).
func TestNonLocalTimezone(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(2)
loc, err := time.LoadLocation("Atlantic/Cape_Verde")
if err != nil {
fmt.Printf("Failed to load time zone Atlantic/Cape_Verde: %+v", err)
t.Fail()
}
now := time.Now().In(loc)
// FIX: Issue #205
// This calculation doesn't work in seconds 58 or 59.
// Take the easy way out and sleep.
if now.Second() >= 58 {
time.Sleep(2 * time.Second)
now = time.Now().In(loc)
}
spec := fmt.Sprintf("%d,%d %d %d %d %d ?",
now.Second()+1, now.Second()+2, now.Minute(), now.Hour(), now.Day(), now.Month())
cron := New(WithLocation(loc), WithParser(secondParser))
cron.AddFunc(spec, func() { wg.Done() })
cron.Start()
defer cron.Stop()
select {
case <-time.After(OneSecond * 2):
t.Error("expected job fires 2 times")
case <-wait(wg):
}
}
// Test that calling stop before start silently returns without
// blocking the stop channel.
func TestStopWithoutStart(t *testing.T) {
cron := New()
cron.Stop()
}
type testJob struct {
wg *sync.WaitGroup
name string
}
func (t testJob) Run() {
t.wg.Done()
}
// Test that adding an invalid job spec returns an error
func TestInvalidJobSpec(t *testing.T) {
cron := New()
_, err := cron.AddJob("this will not parse", nil)
if err == nil {
t.Errorf("expected an error with invalid spec, got nil")
}
}
// Test blocking run method behaves as Start()
func TestBlockingRun(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
cron := newWithSeconds()
cron.AddFunc("* * * * * ?", func() { wg.Done() })
var unblockChan = make(chan struct{})
go func() {
cron.Run()
close(unblockChan)
}()
defer cron.Stop()
select {
case <-time.After(OneSecond):
t.Error("expected job fires")
case <-unblockChan:
t.Error("expected that Run() blocks")
case <-wait(wg):
}
}
// Test that double-running is a no-op
func TestStartNoop(t *testing.T) {
var tickChan = make(chan struct{}, 2)
cron := newWithSeconds()
cron.AddFunc("* * * * * ?", func() {
tickChan <- struct{}{}
})
cron.Start()
defer cron.Stop()
// Wait for the first firing to ensure the runner is going
<-tickChan
cron.Start()
<-tickChan
// Fail if this job fires again in a short period, indicating a double-run
select {
case <-time.After(time.Millisecond):
case <-tickChan:
t.Error("expected job fires exactly twice")
}
}
// Simple test using Runnables.
func TestJob(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
cron := newWithSeconds()
cron.AddJob("0 0 0 30 Feb ?", testJob{wg, "job0"})
cron.AddJob("0 0 0 1 1 ?", testJob{wg, "job1"})
job2, _ := cron.AddJob("* * * * * ?", testJob{wg, "job2"})
cron.AddJob("1 0 0 1 1 ?", testJob{wg, "job3"})
cron.Schedule(Every(5*time.Second+5*time.Nanosecond), testJob{wg, "job4"})
job5 := cron.Schedule(Every(5*time.Minute), testJob{wg, "job5"})
// Test getting an Entry pre-Start.
if actualName := cron.Entry(job2).Job.(testJob).name; actualName != "job2" {
t.Error("wrong job retrieved:", actualName)
}
if actualName := cron.Entry(job5).Job.(testJob).name; actualName != "job5" {
t.Error("wrong job retrieved:", actualName)
}
cron.Start()
defer cron.Stop()
select {
case <-time.After(OneSecond):
t.FailNow()
case <-wait(wg):
}
// Ensure the entries are in the right order.
expecteds := []string{"job2", "job4", "job5", "job1", "job3", "job0"}
var actuals []string
for _, entry := range cron.Entries() {
actuals = append(actuals, entry.Job.(testJob).name)
}
for i, expected := range expecteds {
if actuals[i] != expected {
t.Fatalf("Jobs not in the right order. (expected) %s != %s (actual)", expecteds, actuals)
}
}
// Test getting Entries.
if actualName := cron.Entry(job2).Job.(testJob).name; actualName != "job2" {
t.Error("wrong job retrieved:", actualName)
}
if actualName := cron.Entry(job5).Job.(testJob).name; actualName != "job5" {
t.Error("wrong job retrieved:", actualName)
}
}
// Issue #206
// Ensure that the next run of a job after removing an entry is accurate.
func TestScheduleAfterRemoval(t *testing.T) {
var wg1 sync.WaitGroup
var wg2 sync.WaitGroup
wg1.Add(1)
wg2.Add(1)
// The first time this job is run, set a timer and remove the other job
// 750ms later. Correct behavior would be to still run the job again in
// 250ms, but the bug would cause it to run instead 1s later.
var calls int
var mu sync.Mutex
cron := newWithSeconds()
hourJob := cron.Schedule(Every(time.Hour), FuncJob(func() {}))
cron.Schedule(Every(time.Second), FuncJob(func() {
mu.Lock()
defer mu.Unlock()
switch calls {
case 0:
wg1.Done()
calls++
case 1:
time.Sleep(750 * time.Millisecond)
cron.Remove(hourJob)
calls++
case 2:
calls++
wg2.Done()
case 3:
panic("unexpected 3rd call")
}
}))
cron.Start()
defer cron.Stop()
// the first run might be any length of time 0 - 1s, since the schedule
// rounds to the second. wait for the first run to true up.
wg1.Wait()
select {
case <-time.After(2 * OneSecond):
t.Error("expected job fires 2 times")
case <-wait(&wg2):
}
}
type ZeroSchedule struct{}
func (*ZeroSchedule) Next(time.Time) time.Time {
return time.Time{}
}
// Tests that job without time does not run
func TestJobWithZeroTimeDoesNotRun(t *testing.T) {
cron := newWithSeconds()
var calls int64
cron.AddFunc("* * * * * *", func() { atomic.AddInt64(&calls, 1) })
cron.Schedule(new(ZeroSchedule), FuncJob(func() { t.Error("expected zero task will not run") }))
cron.Start()
defer cron.Stop()
<-time.After(OneSecond)
if atomic.LoadInt64(&calls) != 1 {
t.Errorf("called %d times, expected 1\n", calls)
}
}
func TestStopAndWait(t *testing.T) {
t.Run("nothing running, returns immediately", func(*testing.T) {
cron := newWithSeconds()
cron.Start()
ctx := cron.Stop()
select {
case <-ctx.Done():
case <-time.After(time.Millisecond):
t.Error("context was not done immediately")
}
})
t.Run("repeated calls to Stop", func(*testing.T) {
cron := newWithSeconds()
cron.Start()
_ = cron.Stop()
time.Sleep(time.Millisecond)
ctx := cron.Stop()
select {
case <-ctx.Done():
case <-time.After(time.Millisecond):
t.Error("context was not done immediately")
}
})
t.Run("a couple fast jobs added, still returns immediately", func(*testing.T) {
cron := newWithSeconds()
cron.AddFunc("* * * * * *", func() {})
cron.Start()
cron.AddFunc("* * * * * *", func() {})
cron.AddFunc("* * * * * *", func() {})
cron.AddFunc("* * * * * *", func() {})
time.Sleep(time.Second)
ctx := cron.Stop()
select {
case <-ctx.Done():
case <-time.After(time.Millisecond):
t.Error("context was not done immediately")
}
})
t.Run("a couple fast jobs and a slow job added, waits for slow job", func(*testing.T) {
cron := newWithSeconds()
cron.AddFunc("* * * * * *", func() {})
cron.Start()
cron.AddFunc("* * * * * *", func() { time.Sleep(2 * time.Second) })
cron.AddFunc("* * * * * *", func() {})
time.Sleep(time.Second)
ctx := cron.Stop()
// Verify that it is not done for at least 750ms
select {
case <-ctx.Done():
t.Error("context was done too quickly immediately")
case <-time.After(750 * time.Millisecond):
// expected, because the job sleeping for 1 second is still running
}
// Verify that it IS done in the next 500ms (giving 250ms buffer)
select {
case <-ctx.Done():
// expected
case <-time.After(1500 * time.Millisecond):
t.Error("context not done after job should have completed")
}
})
t.Run("repeated calls to stop, waiting for completion and after", func(*testing.T) {
cron := newWithSeconds()
cron.AddFunc("* * * * * *", func() {})
cron.AddFunc("* * * * * *", func() { time.Sleep(2 * time.Second) })
cron.Start()
cron.AddFunc("* * * * * *", func() {})
time.Sleep(time.Second)
ctx := cron.Stop()
ctx2 := cron.Stop()
// Verify that it is not done for at least 1500ms
select {
case <-ctx.Done():
t.Error("context was done too quickly immediately")
case <-ctx2.Done():
t.Error("context2 was done too quickly immediately")
case <-time.After(1500 * time.Millisecond):
// expected, because the job sleeping for 2 seconds is still running
}
// Verify that it IS done in the next 1s (giving 500ms buffer)
select {
case <-ctx.Done():
// expected
case <-time.After(time.Second):
t.Error("context not done after job should have completed")
}
// Verify that ctx2 is also done.
select {
case <-ctx2.Done():
// expected
case <-time.After(time.Millisecond):
t.Error("context2 not done even though context1 is")
}
// Verify that a new context retrieved from stop is immediately done.
ctx3 := cron.Stop()
select {
case <-ctx3.Done():
// expected
case <-time.After(time.Millisecond):
t.Error("context not done even when cron Stop is completed")
}
})
}
func TestMultiThreadedStartAndStop(t *testing.T) {
cron := New()
go cron.Run()
time.Sleep(2 * time.Millisecond)
cron.Stop()
}
func wait(wg *sync.WaitGroup) chan bool {
ch := make(chan bool)
go func() {
wg.Wait()
ch <- true
}()
return ch
}
func stop(cron *Cron) chan bool {
ch := make(chan bool)
go func() {
cron.Stop()
ch <- true
}()
return ch
}
// newWithSeconds returns a Cron with the seconds field enabled.
func newWithSeconds() *Cron {
return New(WithParser(secondParser), WithChain())
}
================================================
FILE: plugin/cron/logger.go
================================================
package cron
import (
"io"
"log"
"os"
"strings"
"time"
)
// DefaultLogger is used by Cron if none is specified.
var DefaultLogger = PrintfLogger(log.New(os.Stdout, "cron: ", log.LstdFlags))
// DiscardLogger can be used by callers to discard all log messages.
var DiscardLogger = PrintfLogger(log.New(io.Discard, "", 0))
// Logger is the interface used in this package for logging, so that any backend
// can be plugged in. It is a subset of the github.com/go-logr/logr interface.
type Logger interface {
// Info logs routine messages about cron's operation.
Info(msg string, keysAndValues ...interface{})
// Error logs an error condition.
Error(err error, msg string, keysAndValues ...interface{})
}
// PrintfLogger wraps a Printf-based logger (such as the standard library "log")
// into an implementation of the Logger interface which logs errors only.
func PrintfLogger(l interface{ Printf(string, ...interface{}) }) Logger {
return printfLogger{l, false}
}
// VerbosePrintfLogger wraps a Printf-based logger (such as the standard library
// "log") into an implementation of the Logger interface which logs everything.
func VerbosePrintfLogger(l interface{ Printf(string, ...interface{}) }) Logger {
return printfLogger{l, true}
}
type printfLogger struct {
logger interface{ Printf(string, ...interface{}) }
logInfo bool
}
func (pl printfLogger) Info(msg string, keysAndValues ...interface{}) {
if pl.logInfo {
keysAndValues = formatTimes(keysAndValues)
pl.logger.Printf(
formatString(len(keysAndValues)),
append([]interface{}{msg}, keysAndValues...)...)
}
}
func (pl printfLogger) Error(err error, msg string, keysAndValues ...interface{}) {
keysAndValues = formatTimes(keysAndValues)
pl.logger.Printf(
formatString(len(keysAndValues)+2),
append([]interface{}{msg, "error", err}, keysAndValues...)...)
}
// formatString returns a logfmt-like format string for the number of
// key/values.
func formatString(numKeysAndValues int) string {
var sb strings.Builder
sb.WriteString("%s")
if numKeysAndValues > 0 {
sb.WriteString(", ")
}
for i := 0; i < numKeysAndValues/2; i++ {
if i > 0 {
sb.WriteString(", ")
}
sb.WriteString("%v=%v")
}
return sb.String()
}
// formatTimes formats any time.Time values as RFC3339.
func formatTimes(keysAndValues []interface{}) []interface{} {
var formattedArgs []interface{}
for _, arg := range keysAndValues {
if t, ok := arg.(time.Time); ok {
arg = t.Format(time.RFC3339)
}
formattedArgs = append(formattedArgs, arg)
}
return formattedArgs
}
================================================
FILE: plugin/cron/option.go
================================================
package cron
import (
"time"
)
// Option represents a modification to the default behavior of a Cron.
type Option func(*Cron)
// WithLocation overrides the timezone of the cron instance.
func WithLocation(loc *time.Location) Option {
return func(c *Cron) {
c.location = loc
}
}
// WithSeconds overrides the parser used for interpreting job schedules to
// include a seconds field as the first one.
func WithSeconds() Option {
return WithParser(NewParser(
Second | Minute | Hour | Dom | Month | Dow | Descriptor,
))
}
// WithParser overrides the parser used for interpreting job schedules.
func WithParser(p ScheduleParser) Option {
return func(c *Cron) {
c.parser = p
}
}
// WithChain specifies Job wrappers to apply to all jobs added to this cron.
// Refer to the Chain* functions in this package for provided wrappers.
func WithChain(wrappers ...JobWrapper) Option {
return func(c *Cron) {
c.chain = NewChain(wrappers...)
}
}
// WithLogger uses the provided logger.
func WithLogger(logger Logger) Option {
return func(c *Cron) {
c.logger = logger
}
}
================================================
FILE: plugin/cron/option_test.go
================================================
//nolint:all
package cron
import (
"log"
"strings"
"testing"
"time"
)
func TestWithLocation(t *testing.T) {
c := New(WithLocation(time.UTC))
if c.location != time.UTC {
t.Errorf("expected UTC, got %v", c.location)
}
}
func TestWithParser(t *testing.T) {
var parser = NewParser(Dow)
c := New(WithParser(parser))
if c.parser != parser {
t.Error("expected provided parser")
}
}
func TestWithVerboseLogger(t *testing.T) {
var buf syncWriter
var logger = log.New(&buf, "", log.LstdFlags)
c := New(WithLogger(VerbosePrintfLogger(logger)))
if c.logger.(printfLogger).logger != logger {
t.Error("expected provided logger")
}
c.AddFunc("@every 1s", func() {})
c.Start()
time.Sleep(OneSecond)
c.Stop()
out := buf.String()
if !strings.Contains(out, "schedule,") ||
!strings.Contains(out, "run,") {
t.Error("expected to see some actions, got:", out)
}
}
================================================
FILE: plugin/cron/parser.go
================================================
package cron
import (
"math"
"strconv"
"strings"
"time"
"github.com/pkg/errors"
)
// Configuration options for creating a parser. Most options specify which
// fields should be included, while others enable features. If a field is not
// included the parser will assume a default value. These options do not change
// the order fields are parsed in.
type ParseOption int
const (
Second ParseOption = 1 << iota // Seconds field, default 0
SecondOptional // Optional seconds field, default 0
Minute // Minutes field, default 0
Hour // Hours field, default 0
Dom // Day of month field, default *
Month // Month field, default *
Dow // Day of week field, default *
DowOptional // Optional day of week field, default *
Descriptor // Allow descriptors such as @monthly, @weekly, etc.
)
var places = []ParseOption{
Second,
Minute,
Hour,
Dom,
Month,
Dow,
}
var defaults = []string{
"0",
"0",
"0",
"*",
"*",
"*",
}
// A custom Parser that can be configured.
type Parser struct {
options ParseOption
}
// NewParser creates a Parser with custom options.
//
// It panics if more than one Optional is given, since it would be impossible to
// correctly infer which optional is provided or missing in general.
//
// Examples
//
// // Standard parser without descriptors
// specParser := NewParser(Minute | Hour | Dom | Month | Dow)
// sched, err := specParser.Parse("0 0 15 */3 *")
//
// // Same as above, just excludes time fields
// specParser := NewParser(Dom | Month | Dow)
// sched, err := specParser.Parse("15 */3 *")
//
// // Same as above, just makes Dow optional
// specParser := NewParser(Dom | Month | DowOptional)
// sched, err := specParser.Parse("15 */3")
func NewParser(options ParseOption) Parser {
optionals := 0
if options&DowOptional > 0 {
optionals++
}
if options&SecondOptional > 0 {
optionals++
}
if optionals > 1 {
panic("multiple optionals may not be configured")
}
return Parser{options}
}
// Parse returns a new crontab schedule representing the given spec.
// It returns a descriptive error if the spec is not valid.
// It accepts crontab specs and features configured by NewParser.
func (p Parser) Parse(spec string) (Schedule, error) {
if len(spec) == 0 {
return nil, errors.New("empty spec string")
}
// Extract timezone if present
var loc = time.Local
if strings.HasPrefix(spec, "TZ=") || strings.HasPrefix(spec, "CRON_TZ=") {
var err error
i := strings.Index(spec, " ")
eq := strings.Index(spec, "=")
if loc, err = time.LoadLocation(spec[eq+1 : i]); err != nil {
return nil, errors.Wrap(err, "provided bad location")
}
spec = strings.TrimSpace(spec[i:])
}
// Handle named schedules (descriptors), if configured
if strings.HasPrefix(spec, "@") {
if p.options&Descriptor == 0 {
return nil, errors.New("descriptors not enabled")
}
return parseDescriptor(spec, loc)
}
// Split on whitespace.
fields := strings.Fields(spec)
// Validate & fill in any omitted or optional fields
var err error
fields, err = normalizeFields(fields, p.options)
if err != nil {
return nil, err
}
field := func(field string, r bounds) uint64 {
if err != nil {
return 0
}
var bits uint64
bits, err = getField(field, r)
return bits
}
var (
second = field(fields[0], seconds)
minute = field(fields[1], minutes)
hour = field(fields[2], hours)
dayofmonth = field(fields[3], dom)
month = field(fields[4], months)
dayofweek = field(fields[5], dow)
)
if err != nil {
return nil, err
}
return &SpecSchedule{
Second: second,
Minute: minute,
Hour: hour,
Dom: dayofmonth,
Month: month,
Dow: dayofweek,
Location: loc,
}, nil
}
// normalizeFields takes a subset set of the time fields and returns the full set
// with defaults (zeroes) populated for unset fields.
//
// As part of performing this function, it also validates that the provided
// fields are compatible with the configured options.
func normalizeFields(fields []string, options ParseOption) ([]string, error) {
// Validate optionals & add their field to options
optionals := 0
if options&SecondOptional > 0 {
options |= Second
optionals++
}
if options&DowOptional > 0 {
options |= Dow
optionals++
}
if optionals > 1 {
return nil, errors.New("multiple optionals may not be configured")
}
// Figure out how many fields we need
max := 0
for _, place := range places {
if options&place > 0 {
max++
}
}
min := max - optionals
// Validate number of fields
if count := len(fields); count < min || count > max {
if min == max {
return nil, errors.New("incorrect number of fields")
}
return nil, errors.New("incorrect number of fields, expected " + strconv.Itoa(min) + "-" + strconv.Itoa(max))
}
// Populate the optional field if not provided
if min < max && len(fields) == min {
switch {
case options&DowOptional > 0:
fields = append(fields, defaults[5]) // TODO: improve access to default
case options&SecondOptional > 0:
fields = append([]string{defaults[0]}, fields...)
default:
return nil, errors.New("unexpected optional field")
}
}
// Populate all fields not part of options with their defaults
n := 0
expandedFields := make([]string, len(places))
copy(expandedFields, defaults)
for i, place := range places {
if options&place > 0 {
expandedFields[i] = fields[n]
n++
}
}
return expandedFields, nil
}
var standardParser = NewParser(
Minute | Hour | Dom | Month | Dow | Descriptor,
)
// ParseStandard returns a new crontab schedule representing the given
// standardSpec (https://en.wikipedia.org/wiki/Cron). It requires 5 entries
// representing: minute, hour, day of month, month and day of week, in that
// order. It returns a descriptive error if the spec is not valid.
//
// It accepts
// - Standard crontab specs, e.g. "* * * * ?"
// - Descriptors, e.g. "@midnight", "@every 1h30m"
func ParseStandard(standardSpec string) (Schedule, error) {
return standardParser.Parse(standardSpec)
}
// getField returns an Int with the bits set representing all of the times that
// the field represents or error parsing field value. A "field" is a comma-separated
// list of "ranges".
func getField(field string, r bounds) (uint64, error) {
var bits uint64
ranges := strings.FieldsFunc(field, func(r rune) bool { return r == ',' })
for _, expr := range ranges {
bit, err := getRange(expr, r)
if err != nil {
return bits, err
}
bits |= bit
}
return bits, nil
}
// getRange returns the bits indicated by the given expression:
//
// number | number "-" number [ "/" number ]
//
// or error parsing range.
func getRange(expr string, r bounds) (uint64, error) {
var (
start, end, step uint
rangeAndStep = strings.Split(expr, "/")
lowAndHigh = strings.Split(rangeAndStep[0], "-")
singleDigit = len(lowAndHigh) == 1
err error
)
var extra uint64
if lowAndHigh[0] == "*" || lowAndHigh[0] == "?" {
start = r.min
end = r.max
extra = starBit
} else {
start, err = parseIntOrName(lowAndHigh[0], r.names)
if err != nil {
return 0, err
}
switch len(lowAndHigh) {
case 1:
end = start
case 2:
end, err = parseIntOrName(lowAndHigh[1], r.names)
if err != nil {
return 0, err
}
default:
return 0, errors.New("too many hyphens: " + expr)
}
}
switch len(rangeAndStep) {
case 1:
step = 1
case 2:
step, err = mustParseInt(rangeAndStep[1])
if err != nil {
return 0, err
}
// Special handling: "N/step" means "N-max/step".
if singleDigit {
end = r.max
}
if step > 1 {
extra = 0
}
default:
return 0, errors.New("too many slashes: " + expr)
}
if start < r.min {
return 0, errors.New("beginning of range below minimum: " + expr)
}
if end > r.max {
return 0, errors.New("end of range above maximum: " + expr)
}
if start > end {
return 0, errors.New("beginning of range after end: " + expr)
}
if step == 0 {
return 0, errors.New("step cannot be zero: " + expr)
}
return getBits(start, end, step) | extra, nil
}
// parseIntOrName returns the (possibly-named) integer contained in expr.
func parseIntOrName(expr string, names map[string]uint) (uint, error) {
if names != nil {
if namedInt, ok := names[strings.ToLower(expr)]; ok {
return namedInt, nil
}
}
return mustParseInt(expr)
}
// mustParseInt parses the given expression as an int or returns an error.
func mustParseInt(expr string) (uint, error) {
num, err := strconv.Atoi(expr)
if err != nil {
return 0, errors.Wrap(err, "failed to parse number")
}
if num < 0 {
return 0, errors.New("number must be positive")
}
return uint(num), nil
}
// getBits sets all bits in the range [min, max], modulo the given step size.
func getBits(min, max, step uint) uint64 {
var bits uint64
// If step is 1, use shifts.
if step == 1 {
return ^(math.MaxUint64 << (max + 1)) & (math.MaxUint64 << min)
}
// Else, use a simple loop.
for i := min; i <= max; i += step {
bits |= 1 << i
}
return bits
}
// all returns all bits within the given bounds.
func all(r bounds) uint64 {
return getBits(r.min, r.max, 1) | starBit
}
// parseDescriptor returns a predefined schedule for the expression, or error if none matches.
func parseDescriptor(descriptor string, loc *time.Location) (Schedule, error) {
switch descriptor {
case "@yearly", "@annually":
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: 1 << hours.min,
Dom: 1 << dom.min,
Month: 1 << months.min,
Dow: all(dow),
Location: loc,
}, nil
case "@monthly":
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: 1 << hours.min,
Dom: 1 << dom.min,
Month: all(months),
Dow: all(dow),
Location: loc,
}, nil
case "@weekly":
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: 1 << hours.min,
Dom: all(dom),
Month: all(months),
Dow: 1 << dow.min,
Location: loc,
}, nil
case "@daily", "@midnight":
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: 1 << hours.min,
Dom: all(dom),
Month: all(months),
Dow: all(dow),
Location: loc,
}, nil
case "@hourly":
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: all(hours),
Dom: all(dom),
Month: all(months),
Dow: all(dow),
Location: loc,
}, nil
default:
// Continue to check @every prefix below
}
const every = "@every "
if strings.HasPrefix(descriptor, every) {
duration, err := time.ParseDuration(descriptor[len(every):])
if err != nil {
return nil, errors.Wrap(err, "failed to parse duration")
}
return Every(duration), nil
}
return nil, errors.New("unrecognized descriptor: " + descriptor)
}
================================================
FILE: plugin/cron/parser_test.go
================================================
//nolint:all
package cron
import (
"reflect"
"strings"
"testing"
"time"
)
var secondParser = NewParser(Second | Minute | Hour | Dom | Month | DowOptional | Descriptor)
func TestRange(t *testing.T) {
zero := uint64(0)
ranges := []struct {
expr string
min, max uint
expected uint64
err string
}{
{"5", 0, 7, 1 << 5, ""},
{"0", 0, 7, 1 << 0, ""},
{"7", 0, 7, 1 << 7, ""},
{"5-5", 0, 7, 1 << 5, ""},
{"5-6", 0, 7, 1<<5 | 1<<6, ""},
{"5-7", 0, 7, 1<<5 | 1<<6 | 1<<7, ""},
{"5-6/2", 0, 7, 1 << 5, ""},
{"5-7/2", 0, 7, 1<<5 | 1<<7, ""},
{"5-7/1", 0, 7, 1<<5 | 1<<6 | 1<<7, ""},
{"*", 1, 3, 1<<1 | 1<<2 | 1<<3 | starBit, ""},
{"*/2", 1, 3, 1<<1 | 1<<3, ""},
{"5--5", 0, 0, zero, "too many hyphens"},
{"jan-x", 0, 0, zero, `failed to parse number: strconv.Atoi: parsing "jan": invalid syntax`},
{"2-x", 1, 5, zero, `failed to parse number: strconv.Atoi: parsing "x": invalid syntax`},
{"*/-12", 0, 0, zero, "number must be positive"},
{"*//2", 0, 0, zero, "too many slashes"},
{"1", 3, 5, zero, "below minimum"},
{"6", 3, 5, zero, "above maximum"},
{"5-3", 3, 5, zero, "beginning of range after end: 5-3"},
{"*/0", 0, 0, zero, "step cannot be zero: */0"},
}
for _, c := range ranges {
actual, err := getRange(c.expr, bounds{c.min, c.max, nil})
if len(c.err) != 0 && (err == nil || !strings.Contains(err.Error(), c.err)) {
t.Errorf("%s => expected %v, got %v", c.expr, c.err, err)
}
if len(c.err) == 0 && err != nil {
t.Errorf("%s => unexpected error %v", c.expr, err)
}
if actual != c.expected {
t.Errorf("%s => expected %d, got %d", c.expr, c.expected, actual)
}
}
}
func TestField(t *testing.T) {
fields := []struct {
expr string
min, max uint
expected uint64
}{
{"5", 1, 7, 1 << 5},
{"5,6", 1, 7, 1<<5 | 1<<6},
{"5,6,7", 1, 7, 1<<5 | 1<<6 | 1<<7},
{"1,5-7/2,3", 1, 7, 1<<1 | 1<<5 | 1<<7 | 1<<3},
}
for _, c := range fields {
actual, _ := getField(c.expr, bounds{c.min, c.max, nil})
if actual != c.expected {
t.Errorf("%s => expected %d, got %d", c.expr, c.expected, actual)
}
}
}
func TestAll(t *testing.T) {
allBits := []struct {
r bounds
expected uint64
}{
{minutes, 0xfffffffffffffff}, // 0-59: 60 ones
{hours, 0xffffff}, // 0-23: 24 ones
{dom, 0xfffffffe}, // 1-31: 31 ones, 1 zero
{months, 0x1ffe}, // 1-12: 12 ones, 1 zero
{dow, 0x7f}, // 0-6: 7 ones
}
for _, c := range allBits {
actual := all(c.r) // all() adds the starBit, so compensate for that..
if c.expected|starBit != actual {
t.Errorf("%d-%d/%d => expected %b, got %b",
c.r.min, c.r.max, 1, c.expected|starBit, actual)
}
}
}
func TestBits(t *testing.T) {
bits := []struct {
min, max, step uint
expected uint64
}{
{0, 0, 1, 0x1},
{1, 1, 1, 0x2},
{1, 5, 2, 0x2a}, // 101010
{1, 4, 2, 0xa}, // 1010
}
for _, c := range bits {
actual := getBits(c.min, c.max, c.step)
if c.expected != actual {
t.Errorf("%d-%d/%d => expected %b, got %b",
c.min, c.max, c.step, c.expected, actual)
}
}
}
func TestParseScheduleErrors(t *testing.T) {
var tests = []struct{ expr, err string }{
{"* 5 j * * *", `failed to parse number: strconv.Atoi: parsing "j": invalid syntax`},
{"@every Xm", "failed to parse duration"},
{"@unrecognized", "unrecognized descriptor"},
{"* * * *", "incorrect number of fields, expected 5-6"},
{"", "empty spec string"},
}
for _, c := range tests {
actual, err := secondParser.Parse(c.expr)
if err == nil || !strings.Contains(err.Error(), c.err) {
t.Errorf("%s => expected %v, got %v", c.expr, c.err, err)
}
if actual != nil {
t.Errorf("expected nil schedule on error, got %v", actual)
}
}
}
func TestParseSchedule(t *testing.T) {
tokyo, _ := time.LoadLocation("Asia/Tokyo")
entries := []struct {
parser Parser
expr string
expected Schedule
}{
{secondParser, "0 5 * * * *", every5min(time.Local)},
{standardParser, "5 * * * *", every5min(time.Local)},
{secondParser, "CRON_TZ=UTC 0 5 * * * *", every5min(time.UTC)},
{standardParser, "CRON_TZ=UTC 5 * * * *", every5min(time.UTC)},
{secondParser, "CRON_TZ=Asia/Tokyo 0 5 * * * *", every5min(tokyo)},
{secondParser, "@every 5m", ConstantDelaySchedule{5 * time.Minute}},
{secondParser, "@midnight", midnight(time.Local)},
{secondParser, "TZ=UTC @midnight", midnight(time.UTC)},
{secondParser, "TZ=Asia/Tokyo @midnight", midnight(tokyo)},
{secondParser, "@yearly", annual(time.Local)},
{secondParser, "@annually", annual(time.Local)},
{
parser: secondParser,
expr: "* 5 * * * *",
expected: &SpecSchedule{
Second: all(seconds),
Minute: 1 << 5,
Hour: all(hours),
Dom: all(dom),
Month: all(months),
Dow: all(dow),
Location: time.Local,
},
},
}
for _, c := range entries {
actual, err := c.parser.Parse(c.expr)
if err != nil {
t.Errorf("%s => unexpected error %v", c.expr, err)
}
if !reflect.DeepEqual(actual, c.expected) {
t.Errorf("%s => expected %b, got %b", c.expr, c.expected, actual)
}
}
}
func TestOptionalSecondSchedule(t *testing.T) {
parser := NewParser(SecondOptional | Minute | Hour | Dom | Month | Dow | Descriptor)
entries := []struct {
expr string
expected Schedule
}{
{"0 5 * * * *", every5min(time.Local)},
{"5 5 * * * *", every5min5s(time.Local)},
{"5 * * * *", every5min(time.Local)},
}
for _, c := range entries {
actual, err := parser.Parse(c.expr)
if err != nil {
t.Errorf("%s => unexpected error %v", c.expr, err)
}
if !reflect.DeepEqual(actual, c.expected) {
t.Errorf("%s => expected %b, got %b", c.expr, c.expected, actual)
}
}
}
func TestNormalizeFields(t *testing.T) {
tests := []struct {
name string
input []string
options ParseOption
expected []string
}{
{
"AllFields_NoOptional",
[]string{"0", "5", "*", "*", "*", "*"},
Second | Minute | Hour | Dom | Month | Dow | Descriptor,
[]string{"0", "5", "*", "*", "*", "*"},
},
{
"AllFields_SecondOptional_Provided",
[]string{"0", "5", "*", "*", "*", "*"},
SecondOptional | Minute | Hour | Dom | Month | Dow | Descriptor,
[]string{"0", "5", "*", "*", "*", "*"},
},
{
"AllFields_SecondOptional_NotProvided",
[]string{"5", "*", "*", "*", "*"},
SecondOptional | Minute | Hour | Dom | Month | Dow | Descriptor,
[]string{"0", "5", "*", "*", "*", "*"},
},
{
"SubsetFields_NoOptional",
[]string{"5", "15", "*"},
Hour | Dom | Month,
[]string{"0", "0", "5", "15", "*", "*"},
},
{
"SubsetFields_DowOptional_Provided",
[]string{"5", "15", "*", "4"},
Hour | Dom | Month | DowOptional,
[]string{"0", "0", "5", "15", "*", "4"},
},
{
"SubsetFields_DowOptional_NotProvided",
[]string{"5", "15", "*"},
Hour | Dom | Month | DowOptional,
[]string{"0", "0", "5", "15", "*", "*"},
},
{
"SubsetFields_SecondOptional_NotProvided",
[]string{"5", "15", "*"},
SecondOptional | Hour | Dom | Month,
[]string{"0", "0", "5", "15", "*", "*"},
},
}
for _, test := range tests {
t.Run(test.name, func(*testing.T) {
actual, err := normalizeFields(test.input, test.options)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if !reflect.DeepEqual(actual, test.expected) {
t.Errorf("expected %v, got %v", test.expected, actual)
}
})
}
}
func TestNormalizeFields_Errors(t *testing.T) {
tests := []struct {
name string
input []string
options ParseOption
err string
}{
{
"TwoOptionals",
[]string{"0", "5", "*", "*", "*", "*"},
SecondOptional | Minute | Hour | Dom | Month | DowOptional,
"",
},
{
"TooManyFields",
[]string{"0", "5", "*", "*"},
SecondOptional | Minute | Hour,
"",
},
{
"NoFields",
[]string{},
SecondOptional | Minute | Hour,
"",
},
{
"TooFewFields",
[]string{"*"},
SecondOptional | Minute | Hour,
"",
},
}
for _, test := range tests {
t.Run(test.name, func(*testing.T) {
actual, err := normalizeFields(test.input, test.options)
if err == nil {
t.Errorf("expected an error, got none. results: %v", actual)
}
if !strings.Contains(err.Error(), test.err) {
t.Errorf("expected error %q, got %q", test.err, err.Error())
}
})
}
}
func TestStandardSpecSchedule(t *testing.T) {
entries := []struct {
expr string
expected Schedule
err string
}{
{
expr: "5 * * * *",
expected: &SpecSchedule{1 << seconds.min, 1 << 5, all(hours), all(dom), all(months), all(dow), time.Local},
},
{
expr: "@every 5m",
expected: ConstantDelaySchedule{time.Duration(5) * time.Minute},
},
{
expr: "5 j * * *",
err: `failed to parse number: strconv.Atoi: parsing "j": invalid syntax`,
},
{
expr: "* * * *",
err: "incorrect number of fields",
},
}
for _, c := range entries {
actual, err := ParseStandard(c.expr)
if len(c.err) != 0 && (err == nil || !strings.Contains(err.Error(), c.err)) {
t.Errorf("%s => expected %v, got %v", c.expr, c.err, err)
}
if len(c.err) == 0 && err != nil {
t.Errorf("%s => unexpected error %v", c.expr, err)
}
if !reflect.DeepEqual(actual, c.expected) {
t.Errorf("%s => expected %b, got %b", c.expr, c.expected, actual)
}
}
}
func TestNoDescriptorParser(t *testing.T) {
parser := NewParser(Minute | Hour)
_, err := parser.Parse("@every 1m")
if err == nil {
t.Error("expected an error, got none")
}
}
func every5min(loc *time.Location) *SpecSchedule {
return &SpecSchedule{1 << 0, 1 << 5, all(hours), all(dom), all(months), all(dow), loc}
}
func every5min5s(loc *time.Location) *SpecSchedule {
return &SpecSchedule{1 << 5, 1 << 5, all(hours), all(dom), all(months), all(dow), loc}
}
func midnight(loc *time.Location) *SpecSchedule {
return &SpecSchedule{1, 1, 1, all(dom), all(months), all(dow), loc}
}
func annual(loc *time.Location) *SpecSchedule {
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: 1 << hours.min,
Dom: 1 << dom.min,
Month: 1 << months.min,
Dow: all(dow),
Location: loc,
}
}
================================================
FILE: plugin/cron/spec.go
================================================
package cron
import "time"
// SpecSchedule specifies a duty cycle (to the second granularity), based on a
// traditional crontab specification. It is computed initially and stored as bit sets.
type SpecSchedule struct {
Second, Minute, Hour, Dom, Month, Dow uint64
// Override location for this schedule.
Location *time.Location
}
// bounds provides a range of acceptable values (plus a map of name to value).
type bounds struct {
min, max uint
names map[string]uint
}
// The bounds for each field.
var (
seconds = bounds{0, 59, nil}
minutes = bounds{0, 59, nil}
hours = bounds{0, 23, nil}
dom = bounds{1, 31, nil}
months = bounds{1, 12, map[string]uint{
"jan": 1,
"feb": 2,
"mar": 3,
"apr": 4,
"may": 5,
"jun": 6,
"jul": 7,
"aug": 8,
"sep": 9,
"oct": 10,
"nov": 11,
"dec": 12,
}}
dow = bounds{0, 6, map[string]uint{
"sun": 0,
"mon": 1,
"tue": 2,
"wed": 3,
"thu": 4,
"fri": 5,
"sat": 6,
}}
)
const (
// Set the top bit if a star was included in the expression.
starBit = 1 << 63
)
// Next returns the next time this schedule is activated, greater than the given
// time. If no time can be found to satisfy the schedule, return the zero time.
func (s *SpecSchedule) Next(t time.Time) time.Time {
// General approach
//
// For Month, Day, Hour, Minute, Second:
// Check if the time value matches. If yes, continue to the next field.
// If the field doesn't match the schedule, then increment the field until it matches.
// While incrementing the field, a wrap-around brings it back to the beginning
// of the field list (since it is necessary to re-verify previous field
// values)
// Convert the given time into the schedule's timezone, if one is specified.
// Save the original timezone so we can convert back after we find a time.
// Note that schedules without a time zone specified (time.Local) are treated
// as local to the time provided.
origLocation := t.Location()
loc := s.Location
if loc == time.Local {
loc = t.Location()
}
if s.Location != time.Local {
t = t.In(s.Location)
}
// Start at the earliest possible time (the upcoming second).
t = t.Add(1*time.Second - time.Duration(t.Nanosecond())*time.Nanosecond)
// This flag indicates whether a field has been incremented.
added := false
// If no time is found within five years, return zero.
yearLimit := t.Year() + 5
WRAP:
if t.Year() > yearLimit {
return time.Time{}
}
// Find the first applicable month.
// If it's this month, then do nothing.
for 1<<uint(t.Month())&s.Month == 0 {
// If we have to add a month, reset the other parts to 0.
if !added {
added = true
// Otherwise, set the date at the beginning (since the current time is irrelevant).
t = time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, loc)
}
t = t.AddDate(0, 1, 0)
// Wrapped around.
if t.Month() == time.January {
goto WRAP
}
}
// Now get a day in that month.
//
// NOTE: This causes issues for daylight savings regimes where midnight does
// not exist. For example: Sao Paulo has DST that transforms midnight on
// 11/3 into 1am. Handle that by noticing when the Hour ends up != 0.
for !dayMatches(s, t) {
if !added {
added = true
t = time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, loc)
}
t = t.AddDate(0, 0, 1)
// Notice if the hour is no longer midnight due to DST.
// Add an hour if it's 23, subtract an hour if it's 1.
if t.Hour() != 0 {
if t.Hour() > 12 {
t = t.Add(time.Duration(24-t.Hour()) * time.Hour)
} else {
t = t.Add(time.Duration(-t.Hour()) * time.Hour)
}
}
if t.Day() == 1 {
goto WRAP
}
}
for 1<<uint(t.Hour())&s.Hour == 0 {
if !added {
added = true
t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), 0, 0, 0, loc)
}
t = t.Add(1 * time.Hour)
if t.Hour() == 0 {
goto WRAP
}
}
for 1<<uint(t.Minute())&s.Minute == 0 {
if !added {
added = true
t = t.Truncate(time.Minute)
}
t = t.Add(1 * time.Minute)
if t.Minute() == 0 {
goto WRAP
}
}
for 1<<uint(t.Second())&s.Second == 0 {
if !added {
added = true
t = t.Truncate(time.Second)
}
t = t.Add(1 * time.Second)
if t.Second() == 0 {
goto WRAP
}
}
return t.In(origLocation)
}
// dayMatches returns true if the schedule's day-of-week and day-of-month
// restrictions are satisfied by the given time.
func dayMatches(s *SpecSchedule, t time.Time) bool {
var (
domMatch = 1<<uint(t.Day())&s.Dom > 0
dowMatch = 1<<uint(t.Weekday())&s.Dow > 0
)
if s.Dom&starBit > 0 || s.Dow&starBit > 0 {
return domMatch && dowMatch
}
return domMatch || dowMatch
}
================================================
FILE: plugin/cron/spec_test.go
================================================
//nolint:all
package cron
import (
"strings"
"testing"
"time"
)
func TestActivation(t *testing.T) {
tests := []struct {
time, spec string
expected bool
}{
// Every fifteen minutes.
{"Mon Jul 9 15:00 2012", "0/15 * * * *", true},
{"Mon Jul 9 15:45 2012", "0/15 * * * *", true},
{"Mon Jul 9 15:40 2012", "0/15 * * * *", false},
// Every fifteen minutes, starting at 5 minutes.
{"Mon Jul 9 15:05 2012", "5/15 * * * *", true},
{"Mon Jul 9 15:20 2012", "5/15 * * * *", true},
{"Mon Jul 9 15:50 2012", "5/15 * * * *", true},
// Named months
{"Sun Jul 15 15:00 2012", "0/15 * * Jul *", true},
{"Sun Jul 15 15:00 2012", "0/15 * * Jun *", false},
// Everything set.
{"Sun Jul 15 08:30 2012", "30 08 ? Jul Sun", true},
{"Sun Jul 15 08:30 2012", "30 08 15 Jul ?", true},
{"Mon Jul 16 08:30 2012", "30 08 ? Jul Sun", false},
{"Mon Jul 16 08:30 2012", "30 08 15 Jul ?", false},
// Predefined schedules
{"Mon Jul 9 15:00 2012", "@hourly", true},
{"Mon Jul 9 15:04 2012", "@hourly", false},
{"Mon Jul 9 15:00 2012", "@daily", false},
{"Mon Jul 9 00:00 2012", "@daily", true},
{"Mon Jul 9 00:00 2012", "@weekly", false},
{"Sun Jul 8 00:00 2012", "@weekly", true},
{"Sun Jul 8 01:00 2012", "@weekly", false},
{"Sun Jul 8 00:00 2012", "@monthly", false},
{"Sun Jul 1 00:00 2012", "@monthly", true},
// Test interaction of DOW and DOM.
// If both are restricted, then only one needs to match.
{"Sun Jul 15 00:00 2012", "* * 1,15 * Sun", true},
{"Fri Jun 15 00:00 2012", "* * 1,15 * Sun", true},
{"Wed Aug 1 00:00 2012", "* * 1,15 * Sun", true},
{"Sun Jul 15 00:00 2012", "* * */10 * Sun", true}, // verifies #70
// However, if one has a star, then both need to match.
{"Sun Jul 15 00:00 2012", "* * * * Mon", false},
{"Mon Jul 9 00:00 2012", "* * 1,15 * *", false},
{"Sun Jul 15 00:00 2012", "* * 1,15 * *", true},
{"Sun Jul 15 00:00 2012", "* * */2 * Sun", true},
}
for _, test := range tests {
sched, err := ParseStandard(test.spec)
if err != nil {
t.Error(err)
continue
}
actual := sched.Next(getTime(test.time).Add(-1 * time.Second))
expected := getTime(test.time)
if test.expected && expected != actual || !test.expected && expected == actual {
t.Errorf("Fail evaluating %s on %s: (expected) %s != %s (actual)",
test.spec, test.time, expected, actual)
}
}
}
func TestNext(t *testing.T) {
runs := []struct {
time, spec string
expected string
}{
// Simple cases
{"Mon Jul 9 14:45 2012", "0 0/15 * * * *", "Mon Jul 9 15:00 2012"},
{"Mon Jul 9 14:59 2012", "0 0/15 * * * *", "Mon Jul 9 15:00 2012"},
{"Mon Jul 9 14:59:59 2012", "0 0/15 * * * *", "Mon Jul 9 15:00 2012"},
// Wrap around hours
{"Mon Jul 9 15:45 2012", "0 20-35/15 * * * *", "Mon Jul 9 16:20 2012"},
// Wrap around days
{"Mon Jul 9 23:46 2012", "0 */15 * * * *", "Tue Jul 10 00:00 2012"},
{"Mon Jul 9 23:45 2012", "0 20-35/15 * * * *", "Tue Jul 10 00:20 2012"},
{"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 * * * *", "Tue Jul 10 00:20:15 2012"},
{"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 1/2 * * *", "Tue Jul 10 01:20:15 2012"},
{"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 10-12 * * *", "Tue Jul 10 10:20:15 2012"},
{"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 1/2 */2 * *", "Thu Jul 11 01:20:15 2012"},
{"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 * 9-20 * *", "Wed Jul 10 00:20:15 2012"},
{"Mon Jul 9 23:35:51 2012", "15/35 20-35/15 * 9-20 Jul *", "Wed Jul 10 00:20:15 2012"},
// Wrap around months
{"Mon Jul 9 23:35 2012", "0 0 0 9 Apr-Oct ?", "Thu Aug 9 00:00 2012"},
{"Mon Jul 9 23:35 2012", "0 0 0 */5 Apr,Aug,Oct Mon", "Tue Aug 1 00:00 2012"},
{"Mon Jul 9 23:35 2012", "0 0 0 */5 Oct Mon", "Mon Oct 1 00:00 2012"},
// Wrap around years
{"Mon Jul 9 23:35 2012", "0 0 0 * Feb Mon", "Mon Feb 4 00:00 2013"},
{"Mon Jul 9 23:35 2012", "0 0 0 * Feb Mon/2", "Fri Feb 1 00:00 2013"},
// Wrap around minute, hour, day, month, and year
{"Mon Dec 31 23:59:45 2012", "0 * * * * *", "Tue Jan 1 00:00:00 2013"},
// Leap year
{"Mon Jul 9 23:35 2012", "0 0 0 29 Feb ?", "Mon Feb 29 00:00 2016"},
// Daylight savings time 2am EST (-5) -> 3am EDT (-4)
{"2012-03-11T00:00:00-0500", "TZ=America/New_York 0 30 2 11 Mar ?", "2013-03-11T02:30:00-0400"},
// hourly job
{"2012-03-11T00:00:00-0500", "TZ=America/New_York 0 0 * * * ?", "2012-03-11T01:00:00-0500"},
{"2012-03-11T01:00:00-0500", "TZ=America/New_York 0 0 * * * ?", "2012-03-11T03:00:00-0400"},
{"2012-03-11T03:00:00-0400", "TZ=America/New_York 0 0 * * * ?", "2012-03-11T04:00:00-0400"},
{"2012-03-11T04:00:00-0400", "TZ=America/New_York 0 0 * * * ?", "2012-03-11T05:00:00-0400"},
// hourly job using CRON_TZ
{"2012-03-11T00:00:00-0500", "CRON_TZ=America/New_York 0 0 * * * ?", "2012-03-11T01:00:00-0500"},
{"2012-03-11T01:00:00-0500", "CRON_TZ=America/New_York 0 0 * * * ?", "2012-03-11T03:00:00-0400"},
{"2012-03-11T03:00:00-0400", "CRON_TZ=America/New_York 0 0 * * * ?", "2012-03-11T04:00:00-0400"},
{"2012-03-11T04:00:00-0400", "CRON_TZ=America/New_York 0 0 * * * ?", "2012-03-11T05:00:00-0400"},
// 1am nightly job
{"2012-03-11T00:00:00-0500", "TZ=America/New_York 0 0 1 * * ?", "2012-03-11T01:00:00-0500"},
{"2012-03-11T01:00:00-0500", "TZ=America/New_York 0 0 1 * * ?", "2012-03-12T01:00:00-0400"},
// 2am nightly job (skipped)
{"2012-03-11T00:00:00-0500", "TZ=America/New_York 0 0 2 * * ?", "2012-03-12T02:00:00-0400"},
// Daylight savings time 2am EDT (-4) => 1am EST (-5)
{"2012-11-04T00:00:00-0400", "TZ=America/New_York 0 30 2 04 Nov ?", "2012-11-04T02:30:00-0500"},
{"2012-11-04T01:45:00-0400", "TZ=America/New_York 0 30 1 04 Nov ?", "2012-11-04T01:30:00-0500"},
// hourly job
{"2012-11-04T00:00:00-0400", "TZ=America/New_York 0 0 * * * ?", "2012-11-04T01:00:00-0400"},
{"2012-11-04T01:00:00-0400", "TZ=America/New_York 0 0 * * * ?", "2012-11-04T01:00:00-0500"},
{"2012-11-04T01:00:00-0500", "TZ=America/New_York 0 0 * * * ?", "2012-11-04T02:00:00-0500"},
// 1am nightly job (runs twice)
{"2012-11-04T00:00:00-0400", "TZ=America/New_York 0 0 1 * * ?", "2012-11-04T01:00:00-0400"},
{"2012-11-04T01:00:00-0400", "TZ=America/New_York 0 0 1 * * ?", "2012-11-04T01:00:00-0500"},
{"2012-11-04T01:00:00-0500", "TZ=America/New_York 0 0 1 * * ?", "2012-11-05T01:00:00-0500"},
// 2am nightly job
{"2012-11-04T00:00:00-0400", "TZ=America/New_York 0 0 2 * * ?", "2012-11-04T02:00:00-0500"},
{"2012-11-04T02:00:00-0500", "TZ=America/New_York 0 0 2 * * ?", "2012-11-05T02:00:00-0500"},
// 3am nightly job
{"2012-11-04T00:00:00-0400", "TZ=America/New_York 0 0 3 * * ?", "2012-11-04T03:00:00-0500"},
{"2012-11-04T03:00:00-0500", "TZ=America/New_York 0 0 3 * * ?", "2012-11-05T03:00:00-0500"},
// hourly job
{"TZ=America/New_York 2012-11-04T00:00:00-0400", "0 0 * * * ?", "2012-11-04T01:00:00-0400"},
{"TZ=America/New_York 2012-11-04T01:00:00-0400", "0 0 * * * ?", "2012-11-04T01:00:00-0500"},
{"TZ=America/New_York 2012-11-04T01:00:00-0500", "0 0 * * * ?", "2012-11-04T02:00:00-0500"},
// 1am nightly job (runs twice)
{"TZ=America/New_York 2012-11-04T00:00:00-0400", "0 0 1 * * ?", "2012-11-04T01:00:00-0400"},
{"TZ=America/New_York 2012-11-04T01:00:00-0400", "0 0 1 * * ?", "2012-11-04T01:00:00-0500"},
{"TZ=America/New_York 2012-11-04T01:00:00-0500", "0 0 1 * * ?", "2012-11-05T01:00:00-0500"},
// 2am nightly job
{"TZ=America/New_York 2012-11-04T00:00:00-0400", "0 0 2 * * ?", "2012-11-04T02:00:00-0500"},
{"TZ=America/New_York 2012-11-04T02:00:00-0500", "0 0 2 * * ?", "2012-11-05T02:00:00-0500"},
// 3am nightly job
{"TZ=America/New_York 2012-11-04T00:00:00-0400", "0 0 3 * * ?", "2012-11-04T03:00:00-0500"},
{"TZ=America/New_York 2012-11-04T03:00:00-0500", "0 0 3 * * ?", "2012-11-05T03:00:00-0500"},
// Unsatisfiable
{"Mon Jul 9 23:35 2012", "0 0 0 30 Feb ?", ""},
{"Mon Jul 9 23:35 2012", "0 0 0 31 Apr ?", ""},
// Monthly job
{"TZ=America/New_York 2012-11-04T00:00:00-0400", "0 0 3 3 * ?", "2012-12-03T03:00:00-0500"},
// Test the scenario of DST resulting in midnight not being a valid time.
// https://github.com/robfig/cron/issues/157
{"2018-10-17T05:00:00-0400", "TZ=America/Sao_Paulo 0 0 9 10 * ?", "2018-11-10T06:00:00-0500"},
{"2018-02-14T05:00:00-0500", "TZ=America/Sao_Paulo 0 0 9 22 * ?", "2018-02-22T07:00:00-0500"},
}
for _, c := range runs {
sched, err := secondParser.Parse(c.spec)
if err != nil {
t.Error(err)
continue
}
actual := sched.Next(getTime(c.time))
expected := getTime(c.expected)
if !actual.Equal(expected) {
t.Errorf("%s, \"%s\": (expected) %v != %v (actual)", c.time, c.spec, expected, actual)
}
}
}
func TestErrors(t *testing.T) {
invalidSpecs := []string{
"xyz",
"60 0 * * *",
"0 60 * * *",
"0 0 * * XYZ",
}
for _, spec := range invalidSpecs {
_, err := ParseStandard(spec)
if err == nil {
t.Error("expected an error parsing: ", spec)
}
}
}
func getTime(value string) time.Time {
if value == "" {
return time.Time{}
}
var location = time.Local
if strings.HasPrefix(value, "TZ=") {
parts := strings.Fields(value)
loc, err := time.LoadLocation(parts[0][len("TZ="):])
if err != nil {
panic("could not parse location:" + err.Error())
}
location = loc
value = parts[1]
}
var layouts = []string{
"Mon Jan 2 15:04 2006",
"Mon Jan 2 15:04:05 2006",
}
for _, layout := range layouts {
if t, err := time.ParseInLocation(layout, value, location); err == nil {
return t
}
}
if t, err := time.ParseInLocation("2006-01-02T15:04:05-0700", value, location); err == nil {
return t
}
panic("could not parse time value " + value)
}
func TestNextWithTz(t *testing.T) {
runs := []struct {
time, spec string
expected string
}{
// Failing tests
{"2016-01-03T13:09:03+0530", "14 14 * * *", "2016-01-03T14:14:00+0530"},
{"2016-01-03T04:09:03+0530", "14 14 * * ?", "2016-01-03T14:14:00+0530"},
// Passing tests
{"2016-01-03T14:09:03+0530", "14 14 * * *", "2016-01-03T14:14:00+0530"},
{"2016-01-03T14:00:00+0530", "14 14 * * ?", "2016-01-03T14:14:00+0530"},
}
for _, c := range runs {
sched, err := ParseStandard(c.spec)
if err != nil {
t.Error(err)
continue
}
actual := sched.Next(getTimeTZ(c.time))
expected := getTimeTZ(c.expected)
if !actual.Equal(expected) {
t.Errorf("%s, \"%s\": (expected) %v != %v (actual)", c.time, c.spec, expected, actual)
}
}
}
func getTimeTZ(value string) time.Time {
if value == "" {
return time.Time{}
}
t, err := time.Parse("Mon Jan 2 15:04 2006", value)
if err != nil {
t, err = time.Parse("Mon Jan 2 15:04:05 2006", value)
if err != nil {
t, err = time.Parse("2006-01-02T15:04:05-0700", value)
if err != nil {
panic(err)
}
}
}
return t
}
// https://github.com/robfig/cron/issues/144
func TestSlash0NoHang(t *testing.T) {
schedule := "TZ=America/New_York 15/0 * * * *"
_, err := ParseStandard(schedule)
if err == nil {
t.Error("expected an error on 0 increment")
}
}
================================================
FILE: plugin/email/README.md
================================================
# Email Plugin
SMTP email sending functionality for self-hosted Memos instances.
## Overview
This plugin provides a simple, reliable email sending interface following industry-standard SMTP protocols. It's designed for self-hosted environments where instance administrators configure their own email service, similar to platforms like GitHub, GitLab, and Discourse.
## Features
- Standard SMTP protocol support
- TLS/STARTTLS and SSL/TLS encryption
- HTML and plain text emails
- Multiple recipients (To, Cc, Bcc)
- Synchronous and asynchronous sending
- Detailed error reporting with context
- Works with all major email providers
- Reply-To header support
- RFC 5322 compliant message formatting
## Quick Start
### 1. Configure SMTP Settings
```go
import "github.com/usememos/memos/plugin/email"
config := &email.Config{
SMTPHost: "smtp.gmail.com",
SMTPPort: 587,
SMTPUsername: "your-email@gmail.com",
SMTPPassword: "your-app-password",
FromEmail: "noreply@yourdomain.com",
FromName: "Memos",
UseTLS: true,
}
```
### 2. Create and Send Email
```go
message := &email.Message{
To: []string{"user@example.com"},
Subject: "Welcome to Memos!",
Body: "Thanks for signing up.",
IsHTML: false,
}
// Synchronous send (waits for result)
err := email.Send(config, message)
if err != nil {
log.Printf("Failed to send email: %v", err)
}
// Asynchronous send (returns immediately)
email.SendAsync(config, message)
```
## Provider Configuration
### Gmail
Requires an [App Password](https://support.google.com/accounts/answer/185833) (2FA must be enabled):
```go
config := &email.Config{
SMTPHost: "smtp.gmail.com",
SMTPPort: 587,
SMTPUsername: "your-email@gmail.com",
SMTPPassword: "your-16-char-app-password",
FromEmail: "your-email@gmail.com",
FromName: "Memos",
UseTLS: true,
}
```
**Alternative (SSL):**
```go
config := &email.Config{
SMTPHost: "smtp.gmail.com",
SMTPPort: 465,
SMTPUsername: "your-email@gmail.com",
SMTPPassword: "your-16-char-app-password",
FromEmail: "your-email@gmail.com",
FromName: "Memos",
UseSSL: true,
}
```
### SendGrid
```go
config := &email.Config{
SMTPHost: "smtp.sendgrid.net",
SMTPPort: 587,
SMTPUsername: "apikey",
SMTPPassword: "your-sendgrid-api-key",
FromEmail: "noreply@yourdomain.com",
FromName: "Memos",
UseTLS: true,
}
```
### AWS SES
```go
config := &email.Config{
SMTPHost: "email-smtp.us-east-1.amazonaws.com",
SMTPPort: 587,
SMTPUsername: "your-smtp-username",
SMTPPassword: "your-smtp-password",
FromEmail: "verified@yourdomain.com",
FromName: "Memos",
UseTLS: true,
}
```
**Note:** Replace `us-east-1` with your AWS region. Email must be verified in SES.
### Mailgun
```go
config := &email.Config{
SMTPHost: "smtp.mailgun.org",
SMTPPort: 587,
SMTPUsername: "postmaster@yourdomain.com",
SMTPPassword: "your-mailgun-smtp-password",
FromEmail: "noreply@yourdomain.com",
FromName: "Memos",
UseTLS: true,
}
```
### Self-Hosted SMTP (Postfix, Exim, etc.)
```go
config := &email.Config{
SMTPHost: "mail.yourdomain.com",
SMTPPort: 587,
SMTPUsername: "username",
SMTPPassword: "password",
FromEmail: "noreply@yourdomain.com",
FromName: "Memos",
UseTLS: true,
}
```
## HTML Emails
```go
message := &email.Message{
To: []string{"user@example.com"},
Subject: "Welcome to Memos!",
Body: `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body style="font-family: Arial, sans-serif;">
<h1 style="color: #333;">Welcome to Memos!</h1>
<p>We're excited to have you on board.</p>
<a href="https://yourdomain.com" style="background-color: #4CAF50; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px;">Get Started</a>
</body>
</html>
`,
IsHTML: true,
}
email.Send(config, message)
```
## Multiple Recipients
```go
message := &email.Message{
To: []string{"user1@example.com", "user2@example.com"},
Cc: []string{"manager@example.com"},
Bcc: []string{"admin@example.com"},
Subject: "Team Update",
Body: "Important team announcement...",
ReplyTo: "support@yourdomain.com",
}
email.Send(config, message)
```
## Testing
### Run Tests
```bash
# All tests
go test ./plugin/email/... -v
# With coverage
go test ./plugin/email/... -v -cover
# With race detector
go test ./plugin/email/... -race
```
### Manual Testing
Create a simple test program:
```go
package main
import (
"log"
"github.com/usememos/memos/plugin/email"
)
func main() {
config := &email.Config{
SMTPHost: "smtp.gmail.com",
SMTPPort: 587,
SMTPUsername: "your-email@gmail.com",
SMTPPassword: "your-app-password",
FromEmail: "your-email@gmail.com",
FromName: "Test",
UseTLS: true,
}
message := &email.Message{
To: []string{"recipient@example.com"},
Subject: "Test Email",
Body: "This is a test email from Memos email plugin.",
}
if err := email.Send(config, message); err != nil {
log.Fatalf("Failed to send email: %v", err)
}
log.Println("Email sent successfully!")
}
```
## Security Best Practices
### 1. Use TLS/SSL Encryption
Always enable encryption in production:
```go
// STARTTLS (port 587) - Recommended
config.UseTLS = true
// SSL/TLS (port 465)
config.UseSSL = true
```
### 2. Secure Credential Storage
Never hardcode credentials. Use environment variables:
```go
import "os"
config := &email.Config{
SMTPHost: os.Getenv("SMTP_HOST"),
SMTPPort: 587,
SMTPUsername: os.Getenv("SMTP_USERNAME"),
SMTPPassword: os.Getenv("SMTP_PASSWORD"),
FromEmail: os.Getenv("SMTP_FROM_EMAIL"),
UseTLS: true,
}
```
### 3. Use App-Specific Passwords
For Gmail and similar services, use app passwords instead of your main account password.
### 4. Validate and Sanitize Input
Always validate email addresses and sanitize content:
```go
// Validate before sending
if err := message.Validate(); err != nil {
return err
}
```
### 5. Implement Rate Limiting
Prevent abuse by limiting email sending:
```go
// Example using golang.org/x/time/rate
limiter := rate.NewLimiter(rate.Every(time.Second), 10) // 10 emails per second
if !limiter.Allow() {
return errors.New("rate limit exceeded")
}
```
### 6. Monitor and Log
Log email sending activity for security monitoring:
```go
if err := email.Send(config, message); err != nil {
slog.Error("Email send failed",
slog.String("recipient", message.To[0]),
slog.Any("error", err))
}
```
## Common Ports
| Port | Protocol | Security | Use Case |
|------|----------|----------|----------|
| **587** | SMTP + STARTTLS | Encrypted | **Recommended** for most providers |
| **465** | SMTP over SSL/TLS | Encrypted | Alternative secure option |
| **25** | SMTP | Unencrypted | Legacy, often blocked by ISPs |
| **2525** | SMTP + STARTTLS | Encrypted | Alternative when 587 is blocked |
**Port 587 (STARTTLS)** is the recommended standard for modern SMTP:
```go
config := &email.Config{
SMTPPort: 587,
UseTLS: true,
}
```
**Port 465 (SSL/TLS)** is the alternative:
```go
config := &email.Config{
SMTPPort: 465,
UseSSL: true,
}
```
## Error Handling
The package provides detailed, contextual errors:
```go
err := email.Send(config, message)
if err != nil {
// Error messages include context:
switch {
case strings.Contains(err.Error(), "invalid email configuration"):
// Configuration error (missing host, invalid port, etc.)
log.Printf("Configuration error: %v", err)
case strings.Contains(err.Error(), "invalid email message"):
// Message validation error (missing recipients, subject, body)
log.Printf("Message error: %v", err)
case strings.Contains(err.Error(), "authentication failed"):
// SMTP authentication failed (wrong credentials)
log.Printf("Auth error: %v", err)
case strings.Contains(err.Error(), "failed to connect"):
// Network/connection error
log.Printf("Connection error: %v", err)
case strings.Contains(err.Error(), "recipient rejected"):
// SMTP server rejected recipient
log.Printf("Recipient error: %v", err)
default:
log.Printf("Unknown error: %v", err)
}
}
```
### Common Error Messages
```
❌ "invalid email configuration: SMTP host is required"
→ Fix: Set config.SMTPHost
❌ "invalid email configuration: SMTP port must be between 1 and 65535"
→ Fix: Set valid config.SMTPPort (usually 587 or 465)
❌ "invalid email configuration: from email is required"
→ Fix: Set config.FromEmail
❌ "invalid email message: at least one recipient is required"
→ Fix: Set message.To with at least one email address
❌ "invalid email message: subject is required"
→ Fix: Set message.Subject
❌ "invalid email message: body is required"
→ Fix: Set message.Body
❌ "SMTP authentication failed"
→ Fix: Check credentials (username/password)
❌ "failed to connect to SMTP server"
→ Fix: Verify host/port, check firewall, ensure TLS/SSL settings match server
```
### Async Error Handling
For async sending, errors are logged automatically:
```go
email.SendAsync(config, message)
// Errors logged as:
// [WARN] Failed to send email asynchronously recipients=user@example.com error=...
```
## Dependencies
### Required
- **Go 1.25+**
- Standard library: `net/smtp`, `crypto/tls`
- `github.com/pkg/errors` - Error wrapping with context
### No External SMTP Libraries
This plugin uses Go's standard `net/smtp` library for maximum compatibility and minimal dependencies.
## API Reference
### Types
#### `Config`
```go
type Config struct {
SMTPHost string // SMTP server hostname
SMTPPort int // SMTP server port
SMTPUsername string // SMTP auth username
SMTPPassword string // SMTP auth password
FromEmail string // From email address
FromName string // From display name (optional)
UseTLS bool // Enable STARTTLS (port 587)
UseSSL bool // Enable SSL/TLS (port 465)
}
```
#### `Message`
```go
type Message struct {
To []string // Recipients
Cc []string // CC recipients (optional)
Bcc []string // BCC recipients (optional)
Subject string // Email subject
Body string // Email body (plain text or HTML)
IsHTML bool // true for HTML, false for plain text
ReplyTo string // Reply-To address (optional)
}
```
### Functions
#### `Send(config *Config, message *Message) error`
Sends an email synchronously. Blocks until email is sent or error occurs.
#### `SendAsync(config *Config, message *Message)`
Sends an email asynchronously in a goroutine. Returns immediately. Errors are logged.
#### `NewClient(config *Config) *Client`
Creates a new SMTP client for advanced usage.
#### `Client.Send(message *Message) error`
Sends email using the client's configuration.
## Architecture
```
plugin/email/
├── config.go # SMTP configuration types
├── message.go # Email message types and formatting
├── client.go # SMTP client implementation
├── email.go # High-level Send/SendAsync API
├── doc.go # Package documentation
└── *_test.go # Unit tests
```
## License
Part of the Memos project. See main repository for license details.
## Contributing
This plugin follows the Memos contribution guidelines. Please ensure:
1. All code is tested (TDD approach)
2. Tests pass: `go test ./plugin/email/... -v`
3. Code is formatted: `go fmt ./plugin/email/...`
4. No linting errors: `golangci-lint run ./plugin/email/...`
## Support
For issues and questions:
- Memos GitHub Issues: https://github.com/usememos/memos/issues
- Memos Documentation: https://usememos.com/docs
## Roadmap
Future enhancements may include:
- Email template system
- Attachment support
- Inline image embedding
- Email queuing system
- Delivery status tracking
- Bounce handling
================================================
FILE: plugin/email/client.go
================================================
package email
import (
"crypto/tls"
"net/smtp"
"github.com/pkg/errors"
)
// Client represents an SMTP email client.
type Client struct {
config *Config
}
// NewClient creates a new email client with the given configuration.
func NewClient(config *Config) *Client {
return &Client{
config: config,
}
}
// validateConfig validates the client configuration.
func (c *Client) validateConfig() error {
if c.config == nil {
return errors.New("email configuration is required")
}
return c.config.Validate()
}
// createAuth creates an SMTP auth mechanism if credentials are provided.
func (c *Client) createAuth() smtp.Auth {
if c.config.SMTPUsername == "" && c.config.SMTPPassword == "" {
return nil
}
return smtp.PlainAuth("", c.config.SMTPUsername, c.config.SMTPPassword, c.config.SMTPHost)
}
// createTLSConfig creates a TLS configuration for secure connections.
func (c *Client) createTLSConfig() *tls.Config {
return &tls.Config{
ServerName: c.config.SMTPHost,
MinVersion: tls.VersionTLS12,
}
}
// Send sends an email message via SMTP.
func (c *Client) Send(message *Message) error {
// Validate configuration
if err := c.validateConfig(); err != nil {
return errors.Wrap(err, "invalid email configuration")
}
// Validate message
if message == nil {
return errors.New("message is required")
}
if err := message.Validate(); err != nil {
return errors.Wrap(err, "invalid email message")
}
// Format the message
body := message.Format(c.config.FromEmail, c.config.FromName)
// Get all recipients
recipients := message.GetAllRecipients()
// Create auth
auth := c.createAuth()
// Send based on encryption type
if c.config.UseSSL {
return c.sendWithSSL(auth, recipients, body)
}
return c.sendWithTLS(auth, recipients, body)
}
// sendWithTLS sends email using STARTTLS (port 587).
func (c *Client) sendWithTLS(auth smtp.Auth, recipients []string, body string) error {
serverAddr := c.config.GetServerAddress()
if c.config.UseTLS {
// Use STARTTLS
return smtp.SendMail(serverAddr, auth, c.config.FromEmail, recipients, []byte(body))
}
// Send without encryption (not recommended)
return smtp.SendMail(serverAddr, auth, c.config.FromEmail, recipients, []byte(body))
}
// sendWithSSL sends email using SSL/TLS (port 465).
func (c *Client) sendWithSSL(auth smtp.Auth, recipients []string, body string) error {
serverAddr := c.config.GetServerAddress()
// Create TLS connection
tlsConfig := c.createTLSConfig()
conn, err := tls.Dial("tcp", serverAddr, tlsConfig)
if err != nil {
return errors.Wrapf(err, "failed to connect to SMTP server with SSL: %s", serverAddr)
}
defer conn.Close()
// Create SMTP client
client, err := smtp.NewClient(conn, c.config.SMTPHost)
if err != nil {
return errors.Wrap(err, "failed to create SMTP client")
}
defer client.Quit()
// Authenticate
if auth != nil {
if err := client.Auth(auth); err != nil {
return errors.Wrap(err, "SMTP authentication failed")
}
}
// Set sender
if err := client.Mail(c.config.FromEmail); err != nil {
return errors.Wrap(err, "failed to set sender")
}
// Set recipients
for _, recipient := range recipients {
if err := client.Rcpt(recipient); err != nil {
return errors.Wrapf(err, "failed to set recipient: %s", recipient)
}
}
// Send message body
writer, err := client.Data()
if err != nil {
return errors.Wrap(err, "failed to send DATA command")
}
if _, err := writer.Write([]byte(body)); err != nil {
return errors.Wrap(err, "failed to write message body")
}
if err := writer.Close(); err != nil {
return errors.Wrap(err, "failed to close message writer")
}
return nil
}
================================================
FILE: plugin/email/client_test.go
================================================
package email
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewClient(t *testing.T) {
config := &Config{
SMTPHost: "smtp.example.com",
SMTPPort: 587,
SMTPUsername: "user@example.com",
SMTPPassword: "password",
FromEmail: "noreply@example.com",
FromName: "Test App",
UseTLS: true,
}
client := NewClient(config)
assert.NotNil(t, client)
assert.Equal(t, config, client.config)
}
func TestClientValidateConfig(t *testing.T) {
tests := []struct {
name string
config *Config
wantErr bool
}{
{
name: "valid config",
config: &Config{
SMTPHost: "smtp.example.com",
SMTPPort: 587,
FromEmail: "test@example.com",
},
wantErr: false,
},
{
name: "nil config",
config: nil,
wantErr: true,
},
{
name: "invalid config",
config: &Config{
SMTPHost: "",
SMTPPort: 587,
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
client := NewClient(tt.config)
err := client.validateConfig()
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestClientSendValidation(t *testing.T) {
config := &Config{
SMTPHost: "smtp.example.com",
SMTPPort: 587,
FromEmail: "test@example.com",
}
client := NewClient(config)
tests := []struct {
name string
message *Message
wantErr bool
}{
{
name: "valid message",
message: &Message{
To: []string{"recipient@example.com"},
Subject: "Test",
Body: "Test body",
},
wantErr: false, // Will fail on actual send, but passes validation
},
{
name: "nil message",
message: nil,
wantErr: true,
},
{
name: "invalid message",
message: &Message{
To: []string{},
Subject: "Test",
Body: "Test",
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := client.Send(tt.message)
// We expect validation errors for invalid messages
// For valid messages, we'll get connection errors (which is expected in tests)
if tt.wantErr {
assert.Error(t, err)
// Should fail validation before attempting connection
assert.NotContains(t, err.Error(), "dial")
}
// Note: We don't assert NoError for valid messages because
// we don't have a real SMTP server in tests
})
}
}
================================================
FILE: plugin/email/config.go
================================================
package email
import (
"fmt"
"github.com/pkg/errors"
)
// Config represents the SMTP configuration for email sending.
// These settings should be provided by the self-hosted instance administrator.
type Config struct {
// SMTPHost is the SMTP server hostname (e.g., "smtp.gmail.com")
SMTPHost string
// SMTPPort is the SMTP server port (common: 587 for TLS, 465 for SSL, 25 for unencrypted)
SMTPPort int
// SMTPUsername is the SMTP authentication username (usually the email address)
SMTPUsername string
// SMTPPassword is the SMTP authentication password or app-specific password
SMTPPassword string
// FromEmail is the email address that will appear in the "From" field
FromEmail string
// FromName is the display name that will appear in the "From" field
FromName string
// UseTLS enables STARTTLS encryption (recommended for port 587)
UseTLS bool
// UseSSL enables SSL/TLS encryption (for port 465)
UseSSL bool
}
// Validate checks if the configuration is valid.
func (c *Config) Validate() error {
if c.SMTPHost == "" {
return errors.New("SMTP host is required")
}
if c.SMTPPort <= 0 || c.SMTPPort > 65535 {
return errors.New("SMTP port must be between 1 and 65535")
}
if c.FromEmail == "" {
return errors.New("from email is required")
}
return nil
}
// GetServerAddress returns the SMTP server address in the format "host:port".
func (c *Config) GetServerAddress() string {
return fmt.Sprintf("%s:%d", c.SMTPHost, c.SMTPPort)
}
================================================
FILE: plugin/email/config_test.go
================================================
package email
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestConfigValidation(t *testing.T) {
tests := []struct {
name string
config *Config
wantErr bool
}{
{
name: "valid config",
config: &Config{
SMTPHost: "smtp.gmail.com",
SMTPPort: 587,
SMTPUsername: "user@example.com",
SMTPPassword: "password",
FromEmail: "noreply@example.com",
FromName: "Memos",
},
wantErr: false,
},
{
name: "missing host",
config: &Config{
SMTPPort: 587,
SMTPUsername: "user@example.com",
SMTPPassword: "password",
FromEmail: "noreply@example.com",
},
wantErr: true,
},
{
name: "invalid port",
config: &Config{
SMTPHost: "smtp.gmail.com",
SMTPPort: 0,
SMTPUsername: "user@example.com",
SMTPPassword: "password",
FromEmail: "noreply@example.com",
},
wantErr: true,
},
{
name: "missing from email",
config: &Config{
SMTPHost: "smtp.gmail.com",
SMTPPort: 587,
SMTPUsername: "user@example.com",
SMTPPassword: "password",
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.config.Validate()
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestConfigGetServerAddress(t *testing.T) {
config := &Config{
SMTPHost: "smtp.gmail.com",
SMTPPort: 587,
}
expected := "smtp.gmail.com:587"
assert.Equal(t, expected, config.GetServerAddress())
}
================================================
FILE: plugin/email/doc.go
================================================
// Package email provides SMTP email sending functionality for self-hosted Memos instances.
//
// This package is designed for self-hosted environments where instance administrators
// configure their own SMTP servers. It follows industry-standard patterns used by
// platforms like GitHub, GitLab, and Discourse.
//
// # Configuration
//
// The package requires SMTP server configuration provided by the instance administrator:
//
// config := &email.Config{
// SMTPHost: "smtp.gmail.com",
// SMTPPort: 587,
// SMTPUsername: "your-email@gmail.com",
// SMTPPassword: "your-app-password",
// FromEmail: "noreply@yourdomain.com",
// FromName: "Memos Notifications",
// UseTLS: true,
// }
//
// # Common SMTP Settings
//
// Gmail (requires App Password):
// - Host: smtp.gmail.com
// - Port: 587 (TLS) or 465 (SSL)
// - Username: your-email@gmail.com
// - UseTLS: true (for port 587) or UseSSL: true (for port 465)
//
// SendGrid:
// - Host: smtp.sendgrid.net
// - Port: 587
// - Username: apikey
// - Password: your-sendgrid-api-key
// - UseTLS: true
//
// AWS SES:
// - Host: email-smtp.[region].amazonaws.com
// - Port: 587
// - Username: your-smtp-username
// - Password: your-smtp-password
// - UseTLS: true
//
// Mailgun:
// - Host: smtp.mailgun.org
// - Port: 587
// - Username: your-mailgun-smtp-username
// - Password: your-mailgun-smtp-password
// - UseTLS: true
//
// # Sending Email
//
// Synchronous (waits for completion):
//
// message := &email.Message{
// To: []string{"user@example.com"},
// Subject: "Welcome to Memos",
// Body: "Thank you for joining!",
// IsHTML: false,
// }
//
// err := email.Send(config, message)
// if err != nil {
// // Handle error
// }
//
// Asynchronous (returns immediately):
//
// email.SendAsync(config, message)
// // Errors are logged but not returned
//
// # HTML Email
//
// message := &email.Message{
// To: []string{"user@example.com"},
// Subject: "Welcome!",
// Body: "<html><body><h1>Welcome to Memos!</h1></body></html>",
// IsHTML: true,
// }
//
// # Security Considerations
//
// - Always use TLS (port 587) or SSL (port 465) for production
// - Store SMTP credentials securely (environment variables or secrets management)
// - Use app-specific passwords for services like Gmail
// - Validate and sanitize email content to prevent injection attacks
// - Rate limit email sending to prevent abuse
//
// # Error Handling
//
// The package returns descriptive errors for common issues:
// - Configuration validation errors (missing host, invalid port, etc.)
// - Message validation errors (missing recipients, subject, or body)
// - Connection errors (cannot reach SMTP server)
// - Authentication errors (invalid credentials)
// - SMTP protocol errors (recipient rejected, etc.)
//
// All errors are wrapped with context using github.com/pkg/errors for better debugging.
package email
================================================
FILE: plugin/email/email.go
================================================
package email
import (
"log/slog"
"github.com/pkg/errors"
)
// Send sends an email synchronously.
// Returns an error if the email fails to send.
func Send(config *Config, message *Message) error {
if config == nil {
return errors.New("email configuration is required")
}
if message == nil {
return errors.New("email message is required")
}
client := NewClient(config)
return client.Send(message)
}
// SendAsync sends an email
gitextract_vixwurpk/
├── .dockerignore
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ ├── config.yml
│ │ └── feature_request.yml
│ └── workflows/
│ ├── backend-tests.yml
│ ├── build-canary-image.yml
│ ├── demo-deploy.yml
│ ├── frontend-tests.yml
│ ├── proto-linter.yml
│ ├── release.yml
│ └── stale.yml
├── .gitignore
├── .golangci.yaml
├── AGENTS.md
├── CLAUDE.md
├── CODEOWNERS
├── LICENSE
├── README.md
├── SECURITY.md
├── go.mod
├── go.sum
├── internal/
│ ├── base/
│ │ ├── resource_name.go
│ │ └── resource_name_test.go
│ ├── profile/
│ │ └── profile.go
│ ├── util/
│ │ ├── util.go
│ │ └── util_test.go
│ └── version/
│ ├── version.go
│ └── version_test.go
├── plugin/
│ ├── cron/
│ │ ├── README.md
│ │ ├── chain.go
│ │ ├── chain_test.go
│ │ ├── constantdelay.go
│ │ ├── constantdelay_test.go
│ │ ├── cron.go
│ │ ├── cron_test.go
│ │ ├── logger.go
│ │ ├── option.go
│ │ ├── option_test.go
│ │ ├── parser.go
│ │ ├── parser_test.go
│ │ ├── spec.go
│ │ └── spec_test.go
│ ├── email/
│ │ ├── README.md
│ │ ├── client.go
│ │ ├── client_test.go
│ │ ├── config.go
│ │ ├── config_test.go
│ │ ├── doc.go
│ │ ├── email.go
│ │ ├── email_test.go
│ │ ├── message.go
│ │ └── message_test.go
│ ├── filter/
│ │ ├── MAINTENANCE.md
│ │ ├── README.md
│ │ ├── engine.go
│ │ ├── helpers.go
│ │ ├── ir.go
│ │ ├── parser.go
│ │ ├── render.go
│ │ └── schema.go
│ ├── httpgetter/
│ │ ├── html_meta.go
│ │ ├── html_meta_test.go
│ │ ├── http_getter.go
│ │ ├── image.go
│ │ └── util.go
│ ├── idp/
│ │ ├── idp.go
│ │ └── oauth2/
│ │ ├── oauth2.go
│ │ └── oauth2_test.go
│ ├── markdown/
│ │ ├── ast/
│ │ │ └── tag.go
│ │ ├── extensions/
│ │ │ └── tag.go
│ │ ├── markdown.go
│ │ ├── markdown_test.go
│ │ ├── parser/
│ │ │ ├── tag.go
│ │ │ └── tag_test.go
│ │ └── renderer/
│ │ ├── markdown_renderer.go
│ │ └── markdown_renderer_test.go
│ ├── scheduler/
│ │ ├── README.md
│ │ ├── doc.go
│ │ ├── example_test.go
│ │ ├── integration_test.go
│ │ ├── job.go
│ │ ├── job_test.go
│ │ ├── middleware.go
│ │ ├── middleware_test.go
│ │ ├── parser.go
│ │ ├── parser_test.go
│ │ ├── scheduler.go
│ │ └── scheduler_test.go
│ ├── storage/
│ │ └── s3/
│ │ └── s3.go
│ └── webhook/
│ ├── validate.go
│ ├── webhook.go
│ └── webhook_test.go
├── proto/
│ ├── README.md
│ ├── api/
│ │ └── v1/
│ │ ├── README.md
│ │ ├── attachment_service.proto
│ │ ├── auth_service.proto
│ │ ├── common.proto
│ │ ├── idp_service.proto
│ │ ├── instance_service.proto
│ │ ├── memo_service.proto
│ │ ├── shortcut_service.proto
│ │ └── user_service.proto
│ ├── buf.gen.yaml
│ ├── buf.yaml
│ ├── gen/
│ │ ├── api/
│ │ │ └── v1/
│ │ │ ├── apiv1connect/
│ │ │ │ ├── attachment_service.connect.go
│ │ │ │ ├── auth_service.connect.go
│ │ │ │ ├── idp_service.connect.go
│ │ │ │ ├── instance_service.connect.go
│ │ │ │ ├── memo_service.connect.go
│ │ │ │ ├── shortcut_service.connect.go
│ │ │ │ └── user_service.connect.go
│ │ │ ├── attachment_service.pb.go
│ │ │ ├── attachment_service.pb.gw.go
│ │ │ ├── attachment_service_grpc.pb.go
│ │ │ ├── auth_service.pb.go
│ │ │ ├── auth_service.pb.gw.go
│ │ │ ├── auth_service_grpc.pb.go
│ │ │ ├── common.pb.go
│ │ │ ├── idp_service.pb.go
│ │ │ ├── idp_service.pb.gw.go
│ │ │ ├── idp_service_grpc.pb.go
│ │ │ ├── instance_service.pb.go
│ │ │ ├── instance_service.pb.gw.go
│ │ │ ├── instance_service_grpc.pb.go
│ │ │ ├── memo_service.pb.go
│ │ │ ├── memo_service.pb.gw.go
│ │ │ ├── memo_service_grpc.pb.go
│ │ │ ├── shortcut_service.pb.go
│ │ │ ├── shortcut_service.pb.gw.go
│ │ │ ├── shortcut_service_grpc.pb.go
│ │ │ ├── user_service.pb.go
│ │ │ ├── user_service.pb.gw.go
│ │ │ └── user_service_grpc.pb.go
│ │ ├── openapi.yaml
│ │ └── store/
│ │ ├── attachment.pb.go
│ │ ├── idp.pb.go
│ │ ├── inbox.pb.go
│ │ ├── instance_setting.pb.go
│ │ ├── memo.pb.go
│ │ └── user_setting.pb.go
│ └── store/
│ ├── attachment.proto
│ ├── idp.proto
│ ├── inbox.proto
│ ├── instance_setting.proto
│ ├── memo.proto
│ └── user_setting.proto
├── scripts/
│ ├── Dockerfile
│ ├── build.sh
│ ├── compose.yaml
│ ├── entrypoint.sh
│ ├── entrypoint_test.sh
│ └── install.sh
├── server/
│ ├── auth/
│ │ ├── authenticator.go
│ │ ├── context.go
│ │ ├── extract.go
│ │ ├── token.go
│ │ └── token_test.go
│ ├── router/
│ │ ├── api/
│ │ │ └── v1/
│ │ │ ├── acl_config.go
│ │ │ ├── acl_config_test.go
│ │ │ ├── attachment_exif_test.go
│ │ │ ├── attachment_service.go
│ │ │ ├── auth_service.go
│ │ │ ├── auth_service_client_info_test.go
│ │ │ ├── common.go
│ │ │ ├── connect_handler.go
│ │ │ ├── connect_interceptors.go
│ │ │ ├── connect_services.go
│ │ │ ├── header_carrier.go
│ │ │ ├── health_service.go
│ │ │ ├── idp_service.go
│ │ │ ├── instance_service.go
│ │ │ ├── memo_attachment_service.go
│ │ │ ├── memo_relation_service.go
│ │ │ ├── memo_service.go
│ │ │ ├── memo_service_converter.go
│ │ │ ├── memo_service_filter.go
│ │ │ ├── memo_share_service.go
│ │ │ ├── reaction_service.go
│ │ │ ├── resource_name.go
│ │ │ ├── shortcut_service.go
│ │ │ ├── sse_handler.go
│ │ │ ├── sse_hub.go
│ │ │ ├── sse_hub_test.go
│ │ │ ├── test/
│ │ │ │ ├── attachment_service_test.go
│ │ │ │ ├── auth_test.go
│ │ │ │ ├── idp_service_test.go
│ │ │ │ ├── instance_admin_cache_test.go
│ │ │ │ ├── instance_service_test.go
│ │ │ │ ├── memo_attachment_service_test.go
│ │ │ │ ├── memo_relation_service_test.go
│ │ │ │ ├── memo_service_test.go
│ │ │ │ ├── memo_share_service_test.go
│ │ │ │ ├── reaction_service_test.go
│ │ │ │ ├── shortcut_service_test.go
│ │ │ │ ├── sse_handler_test.go
│ │ │ │ ├── test_helper.go
│ │ │ │ ├── user_notification_test.go
│ │ │ │ ├── user_service_registration_test.go
│ │ │ │ └── user_service_stats_test.go
│ │ │ ├── user_service.go
│ │ │ ├── user_service_stats.go
│ │ │ └── v1.go
│ │ ├── fileserver/
│ │ │ ├── README.md
│ │ │ ├── fileserver.go
│ │ │ └── fileserver_test.go
│ │ ├── frontend/
│ │ │ └── frontend.go
│ │ ├── mcp/
│ │ │ ├── README.md
│ │ │ ├── mcp.go
│ │ │ ├── prompts.go
│ │ │ ├── resources_memo.go
│ │ │ ├── tools_attachment.go
│ │ │ ├── tools_memo.go
│ │ │ ├── tools_reaction.go
│ │ │ ├── tools_relation.go
│ │ │ └── tools_tag.go
│ │ └── rss/
│ │ └── rss.go
│ ├── runner/
│ │ ├── memopayload/
│ │ │ └── runner.go
│ │ └── s3presign/
│ │ └── runner.go
│ └── server.go
├── store/
│ ├── attachment.go
│ ├── cache/
│ │ ├── cache.go
│ │ └── cache_test.go
│ ├── cache.go
│ ├── common.go
│ ├── db/
│ │ ├── db.go
│ │ ├── mysql/
│ │ │ ├── attachment.go
│ │ │ ├── common.go
│ │ │ ├── idp.go
│ │ │ ├── inbox.go
│ │ │ ├── instance_setting.go
│ │ │ ├── memo.go
│ │ │ ├── memo_relation.go
│ │ │ ├── memo_share.go
│ │ │ ├── mysql.go
│ │ │ ├── reaction.go
│ │ │ ├── user.go
│ │ │ └── user_setting.go
│ │ ├── postgres/
│ │ │ ├── attachment.go
│ │ │ ├── common.go
│ │ │ ├── idp.go
│ │ │ ├── inbox.go
│ │ │ ├── instance_setting.go
│ │ │ ├── memo.go
│ │ │ ├── memo_relation.go
│ │ │ ├── memo_share.go
│ │ │ ├── postgres.go
│ │ │ ├── reaction.go
│ │ │ ├── user.go
│ │ │ ├── user_setting.go
│ │ │ └── user_setting_test.go
│ │ └── sqlite/
│ │ ├── attachment.go
│ │ ├── common.go
│ │ ├── functions.go
│ │ ├── idp.go
│ │ ├── inbox.go
│ │ ├── instance_setting.go
│ │ ├── memo.go
│ │ ├── memo_relation.go
│ │ ├── memo_share.go
│ │ ├── reaction.go
│ │ ├── sqlite.go
│ │ ├── user.go
│ │ └── user_setting.go
│ ├── driver.go
│ ├── idp.go
│ ├── inbox.go
│ ├── instance_setting.go
│ ├── memo.go
│ ├── memo_relation.go
│ ├── memo_share.go
│ ├── migration/
│ │ ├── mysql/
│ │ │ ├── 0.17/
│ │ │ │ ├── 00__inbox.sql
│ │ │ │ └── 01__delete_activity.sql
│ │ │ ├── 0.18/
│ │ │ │ ├── 00__extend_text.sql
│ │ │ │ ├── 01__webhook.sql
│ │ │ │ └── 02__user_setting.sql
│ │ │ ├── 0.19/
│ │ │ │ └── 00__add_resource_name.sql
│ │ │ ├── 0.20/
│ │ │ │ └── 00__reaction.sql
│ │ │ ├── 0.21/
│ │ │ │ ├── 00__user_description.sql
│ │ │ │ └── 01__rename_uid.sql
│ │ │ ├── 0.22/
│ │ │ │ ├── 00__resource_storage_type.sql
│ │ │ │ ├── 01__memo_tags.sql
│ │ │ │ ├── 02__memo_payload.sql
│ │ │ │ └── 03__drop_tag.sql
│ │ │ ├── 0.23/
│ │ │ │ └── 00__reactions.sql
│ │ │ ├── 0.24/
│ │ │ │ ├── 00__memo.sql
│ │ │ │ ├── 01__memo_pinned.sql
│ │ │ │ └── 02__s3_reference_length.sql
│ │ │ ├── 0.25/
│ │ │ │ └── 00__remove_webhook.sql
│ │ │ ├── 0.26/
│ │ │ │ ├── 00__rename_resource_to_attachment.sql
│ │ │ │ ├── 01__drop_memo_organizer.sql
│ │ │ │ └── 02__migrate_host_to_admin.sql
│ │ │ ├── 0.27/
│ │ │ │ ├── 00__migrate_storage_setting.sql
│ │ │ │ ├── 01__add_idp_uid.sql
│ │ │ │ ├── 02__migrate_inbox_message_payload.sql
│ │ │ │ ├── 03__drop_activity.sql
│ │ │ │ └── 04__memo_share.sql
│ │ │ └── LATEST.sql
│ │ ├── postgres/
│ │ │ ├── 0.19/
│ │ │ │ └── 00__add_resource_name.sql
│ │ │ ├── 0.20/
│ │ │ │ └── 00__reaction.sql
│ │ │ ├── 0.21/
│ │ │ │ ├── 00__user_description.sql
│ │ │ │ └── 01__rename_uid.sql
│ │ │ ├── 0.22/
│ │ │ │ ├── 00__resource_storage_type.sql
│ │ │ │ ├── 01__memo_tags.sql
│ │ │ │ ├── 02__memo_payload.sql
│ │ │ │ └── 03__drop_tag.sql
│ │ │ ├── 0.23/
│ │ │ │ └── 00__reactions.sql
│ │ │ ├── 0.24/
│ │ │ │ ├── 00__memo.sql
│ │ │ │ └── 01__memo_pinned.sql
│ │ │ ├── 0.25/
│ │ │ │ └── 00__remove_webhook.sql
│ │ │ ├── 0.26/
│ │ │ │ ├── 00__rename_resource_to_attachment.sql
│ │ │ │ ├── 01__drop_memo_organizer.sql
│ │ │ │ └── 02__migrate_host_to_admin.sql
│ │ │ ├── 0.27/
│ │ │ │ ├── 00__migrate_storage_setting.sql
│ │ │ │ ├── 01__add_idp_uid.sql
│ │ │ │ ├── 02__migrate_inbox_message_payload.sql
│ │ │ │ ├── 03__drop_activity.sql
│ │ │ │ └── 04__memo_share.sql
│ │ │ └── LATEST.sql
│ │ └── sqlite/
│ │ ├── 0.10/
│ │ │ └── 00__activity.sql
│ │ ├── 0.11/
│ │ │ ├── 00__user_avatar.sql
│ │ │ ├── 01__idp.sql
│ │ │ └── 02__storage.sql
│ │ ├── 0.12/
│ │ │ ├── 00__user_setting.sql
│ │ │ ├── 01__system_setting.sql
│ │ │ ├── 03__resource_internal_path.sql
│ │ │ └── 04__resource_public_id.sql
│ │ ├── 0.13/
│ │ │ ├── 00__memo_relation.sql
│ │ │ └── 01__remove_memo_organizer_id.sql
│ │ ├── 0.14/
│ │ │ ├── 00__drop_resource_public_id.sql
│ │ │ └── 01__create_indexes.sql
│ │ ├── 0.15/
│ │ │ └── 00__drop_user_open_id.sql
│ │ ├── 0.16/
│ │ │ ├── 00__add_memo_id_to_resource.sql
│ │ │ └── 01__drop_shortcut_table.sql
│ │ ├── 0.17/
│ │ │ ├── 00__inbox.sql
│ │ │ └── 01__delete_activities.sql
│ │ ├── 0.18/
│ │ │ ├── 00__webhook.sql
│ │ │ └── 01__user_setting.sql
│ │ ├── 0.19/
│ │ │ └── 00__add_resource_name.sql
│ │ ├── 0.2/
│ │ │ ├── 00__user_role.sql
│ │ │ └── 01__memo_visibility.sql
│ │ ├── 0.20/
│ │ │ └── 00__reaction.sql
│ │ ├── 0.21/
│ │ │ ├── 00__user_description.sql
│ │ │ └── 01__rename_uid.sql
│ │ ├── 0.22/
│ │ │ ├── 00__resource_storage_type.sql
│ │ │ ├── 01__memo_tags.sql
│ │ │ ├── 02__memo_payload.sql
│ │ │ └── 03__drop_tag.sql
│ │ ├── 0.23/
│ │ │ └── 00__reactions.sql
│ │ ├── 0.24/
│ │ │ ├── 00__memo.sql
│ │ │ └── 01__memo_pinned.sql
│ │ ├── 0.25/
│ │ │ └── 00__remove_webhook.sql
│ │ ├── 0.26/
│ │ │ ├── 00__rename_resource_to_attachment.sql
│ │ │ ├── 01__drop_memo_organizer.sql
│ │ │ ├── 02__drop_indexes.sql
│ │ │ ├── 03__alter_user_role.sql
│ │ │ └── 04__migrate_host_to_admin.sql
│ │ ├── 0.27/
│ │ │ ├── 00__migrate_storage_setting.sql
│ │ │ ├── 01__add_idp_uid.sql
│ │ │ ├── 02__migrate_inbox_message_payload.sql
│ │ │ ├── 03__drop_activity.sql
│ │ │ └── 04__memo_share.sql
│ │ ├── 0.3/
│ │ │ └── 00__memo_visibility_protected.sql
│ │ ├── 0.4/
│ │ │ └── 00__user_setting.sql
│ │ ├── 0.5/
│ │ │ ├── 00__regenerate_foreign_keys.sql
│ │ │ ├── 01__memo_resource.sql
│ │ │ ├── 02__system_setting.sql
│ │ │ └── 03__resource_extermal_link.sql
│ │ ├── 0.6/
│ │ │ └── 00__recreate_triggers.sql
│ │ ├── 0.7/
│ │ │ ├── 00__remove_fk.sql
│ │ │ └── 01__remove_triggers.sql
│ │ ├── 0.8/
│ │ │ ├── 00__migration_history.sql
│ │ │ └── 01__user_username.sql
│ │ ├── 0.9/
│ │ │ └── 00__tag.sql
│ │ └── LATEST.sql
│ ├── migrator.go
│ ├── reaction.go
│ ├── seed/
│ │ ├── DEMO_DATA_GUIDE.md
│ │ └── sqlite/
│ │ └── 01__dump.sql
│ ├── store.go
│ ├── test/
│ │ ├── README.md
│ │ ├── attachment_filter_test.go
│ │ ├── attachment_test.go
│ │ ├── containers.go
│ │ ├── filter_helpers_test.go
│ │ ├── idp_test.go
│ │ ├── inbox_test.go
│ │ ├── instance_setting_test.go
│ │ ├── main_test.go
│ │ ├── memo_filter_test.go
│ │ ├── memo_relation_test.go
│ │ ├── memo_test.go
│ │ ├── migrator_test.go
│ │ ├── reaction_test.go
│ │ ├── store.go
│ │ ├── user_setting_test.go
│ │ └── user_test.go
│ ├── user.go
│ └── user_setting.go
└── web/
├── .gitignore
├── biome.json
├── components.json
├── docs/
│ └── auth-architecture.md
├── index.html
├── package.json
├── public/
│ └── site.webmanifest
├── src/
│ ├── App.tsx
│ ├── auth-state.ts
│ ├── components/
│ │ ├── ActivityCalendar/
│ │ │ ├── CalendarCell.tsx
│ │ │ ├── MonthCalendar.tsx
│ │ │ ├── YearCalendar.tsx
│ │ │ ├── constants.ts
│ │ │ ├── hooks.ts
│ │ │ ├── index.ts
│ │ │ ├── types.ts
│ │ │ ├── useCalendar.ts
│ │ │ └── utils.ts
│ │ ├── AttachmentIcon.tsx
│ │ ├── AuthFooter.tsx
│ │ ├── ChangeMemberPasswordDialog.tsx
│ │ ├── ConfirmDialog/
│ │ │ ├── README.md
│ │ │ └── index.tsx
│ │ ├── CreateAccessTokenDialog.tsx
│ │ ├── CreateIdentityProviderDialog.tsx
│ │ ├── CreateShortcutDialog.tsx
│ │ ├── CreateUserDialog.tsx
│ │ ├── CreateWebhookDialog.tsx
│ │ ├── DateTimeInput.tsx
│ │ ├── Empty.tsx
│ │ ├── ErrorBoundary.tsx
│ │ ├── Inbox/
│ │ │ └── MemoCommentMessage.tsx
│ │ ├── LearnMore.tsx
│ │ ├── LocaleSelect.tsx
│ │ ├── MemoActionMenu/
│ │ │ ├── MemoActionMenu.tsx
│ │ │ ├── hooks.ts
│ │ │ ├── index.ts
│ │ │ └── types.ts
│ │ ├── MemoAttachment.tsx
│ │ ├── MemoContent/
│ │ │ ├── CodeBlock.tsx
│ │ │ ├── ConditionalComponent.tsx
│ │ │ ├── MermaidBlock.tsx
│ │ │ ├── Table.tsx
│ │ │ ├── Tag.tsx
│ │ │ ├── TaskListItem.tsx
│ │ │ ├── constants.ts
│ │ │ ├── hooks.ts
│ │ │ ├── index.tsx
│ │ │ ├── markdown/
│ │ │ │ ├── Blockquote.tsx
│ │ │ │ ├── Heading.tsx
│ │ │ │ ├── HorizontalRule.tsx
│ │ │ │ ├── Image.tsx
│ │ │ │ ├── InlineCode.tsx
│ │ │ │ ├── Link.tsx
│ │ │ │ ├── List.tsx
│ │ │ │ ├── Paragraph.tsx
│ │ │ │ ├── README.md
│ │ │ │ ├── index.ts
│ │ │ │ └── types.ts
│ │ │ ├── types.ts
│ │ │ └── utils.ts
│ │ ├── MemoDetailSidebar/
│ │ │ ├── MemoDetailSidebar.tsx
│ │ │ ├── MemoDetailSidebarDrawer.tsx
│ │ │ └── index.ts
│ │ ├── MemoDisplaySettingMenu.tsx
│ │ ├── MemoEditor/
│ │ │ ├── Editor/
│ │ │ │ ├── SlashCommands.tsx
│ │ │ │ ├── SuggestionsPopup.tsx
│ │ │ │ ├── TagSuggestions.tsx
│ │ │ │ ├── commands.ts
│ │ │ │ ├── index.tsx
│ │ │ │ ├── shortcuts.ts
│ │ │ │ ├── useListCompletion.ts
│ │ │ │ └── useSuggestions.ts
│ │ │ ├── README.md
│ │ │ ├── Toolbar/
│ │ │ │ ├── InsertMenu.tsx
│ │ │ │ ├── VisibilitySelector.tsx
│ │ │ │ └── index.ts
│ │ │ ├── components/
│ │ │ │ ├── AttachmentList.tsx
│ │ │ │ ├── EditorContent.tsx
│ │ │ │ ├── EditorMetadata.tsx
│ │ │ │ ├── EditorToolbar.tsx
│ │ │ │ ├── FocusModeOverlay.tsx
│ │ │ │ ├── LinkMemoDialog.tsx
│ │ │ │ ├── LocationDialog.tsx
│ │ │ │ ├── LocationDisplay.tsx
│ │ │ │ ├── RelationList.tsx
│ │ │ │ ├── TimestampPopover.tsx
│ │ │ │ └── index.ts
│ │ │ ├── constants.ts
│ │ │ ├── hooks/
│ │ │ │ ├── index.ts
│ │ │ │ ├── useAutoSave.ts
│ │ │ │ ├── useBlobUrls.ts
│ │ │ │ ├── useDragAndDrop.ts
│ │ │ │ ├── useFileUpload.ts
│ │ │ │ ├── useFocusMode.ts
│ │ │ │ ├── useKeyboard.ts
│ │ │ │ ├── useLinkMemo.ts
│ │ │ │ ├── useLocation.ts
│ │ │ │ └── useMemoInit.ts
│ │ │ ├── index.tsx
│ │ │ ├── services/
│ │ │ │ ├── cacheService.ts
│ │ │ │ ├── errorService.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── memoService.ts
│ │ │ │ ├── uploadService.ts
│ │ │ │ └── validationService.ts
│ │ │ ├── state/
│ │ │ │ ├── actions.ts
│ │ │ │ ├── context.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── reducer.ts
│ │ │ │ └── types.ts
│ │ │ └── types/
│ │ │ ├── attachment.ts
│ │ │ ├── components.ts
│ │ │ ├── context.ts
│ │ │ ├── index.ts
│ │ │ └── insert-menu.ts
│ │ ├── MemoExplorer/
│ │ │ ├── MemoExplorer.tsx
│ │ │ ├── MemoExplorerDrawer.tsx
│ │ │ ├── ShortcutsSection.tsx
│ │ │ ├── TagsSection.tsx
│ │ │ └── index.ts
│ │ ├── MemoFilters.tsx
│ │ ├── MemoPreview/
│ │ │ ├── MemoPreview.tsx
│ │ │ └── index.ts
│ │ ├── MemoReactionListView/
│ │ │ ├── MemoReactionListView.tsx
│ │ │ ├── ReactionSelector.tsx
│ │ │ ├── ReactionView.tsx
│ │ │ ├── hooks.ts
│ │ │ └── index.ts
│ │ ├── MemoRelationForceGraph/
│ │ │ ├── MemoRelationForceGraph.tsx
│ │ │ ├── index.ts
│ │ │ ├── types.ts
│ │ │ └── utils.ts
│ │ ├── MemoResource.tsx
│ │ ├── MemoSharePanel.tsx
│ │ ├── MemoView/
│ │ │ ├── MemoView.tsx
│ │ │ ├── MemoViewContext.tsx
│ │ │ ├── components/
│ │ │ │ ├── MemoBody.tsx
│ │ │ │ ├── MemoCommentListView.tsx
│ │ │ │ ├── MemoHeader.tsx
│ │ │ │ ├── MemoSnippetLink.tsx
│ │ │ │ ├── index.ts
│ │ │ │ └── metadata/
│ │ │ │ ├── AttachmentCard.tsx
│ │ │ │ ├── AttachmentList.tsx
│ │ │ │ ├── LocationDisplay.tsx
│ │ │ │ ├── RelationCard.tsx
│ │ │ │ ├── RelationList.tsx
│ │ │ │ ├── SectionHeader.tsx
│ │ │ │ └── index.ts
│ │ │ ├── constants.ts
│ │ │ ├── hooks/
│ │ │ │ ├── index.ts
│ │ │ │ ├── useImagePreview.ts
│ │ │ │ ├── useMemoActions.ts
│ │ │ │ └── useMemoHandlers.ts
│ │ │ ├── index.ts
│ │ │ └── types.ts
│ │ ├── MemosLogo.tsx
│ │ ├── MobileHeader.tsx
│ │ ├── Navigation.tsx
│ │ ├── NavigationDrawer.tsx
│ │ ├── PagedMemoList/
│ │ │ ├── PagedMemoList.tsx
│ │ │ └── index.ts
│ │ ├── PasswordSignInForm.tsx
│ │ ├── PreviewImageDialog.tsx
│ │ ├── RequiredBadge.tsx
│ │ ├── SearchBar.tsx
│ │ ├── Settings/
│ │ │ ├── AccessTokenSection.tsx
│ │ │ ├── InstanceSection.tsx
│ │ │ ├── MemberSection.tsx
│ │ │ ├── MemoRelatedSettings.tsx
│ │ │ ├── MyAccountSection.tsx
│ │ │ ├── PreferencesSection.tsx
│ │ │ ├── SSOSection.tsx
│ │ │ ├── SectionMenuItem.tsx
│ │ │ ├── SettingGroup.tsx
│ │ │ ├── SettingRow.tsx
│ │ │ ├── SettingSection.tsx
│ │ │ ├── SettingTable.tsx
│ │ │ ├── StorageSection.tsx
│ │ │ └── WebhookSection.tsx
│ │ ├── Skeleton.tsx
│ │ ├── StatisticsView/
│ │ │ ├── MonthNavigator.tsx
│ │ │ ├── StatisticsView.tsx
│ │ │ └── index.ts
│ │ ├── TagTree.tsx
│ │ ├── ThemeSelect.tsx
│ │ ├── UpdateAccountDialog.tsx
│ │ ├── UpdateCustomizedProfileDialog.tsx
│ │ ├── UserAvatar.tsx
│ │ ├── UserMemoMap/
│ │ │ ├── UserMemoMap.tsx
│ │ │ └── index.ts
│ │ ├── UserMenu.tsx
│ │ ├── VisibilityIcon.tsx
│ │ ├── kit/
│ │ │ ├── OverflowTip.tsx
│ │ │ └── SquareDiv.tsx
│ │ ├── map/
│ │ │ ├── LocationPicker.tsx
│ │ │ ├── index.ts
│ │ │ ├── map-utils.tsx
│ │ │ └── useReverseGeocoding.ts
│ │ └── ui/
│ │ ├── badge.tsx
│ │ ├── button.tsx
│ │ ├── checkbox.tsx
│ │ ├── dialog.tsx
│ │ ├── dropdown-menu.tsx
│ │ ├── input.tsx
│ │ ├── label.tsx
│ │ ├── popover.tsx
│ │ ├── radio-group.tsx
│ │ ├── select.tsx
│ │ ├── separator.tsx
│ │ ├── sheet.tsx
│ │ ├── switch.tsx
│ │ ├── textarea.tsx
│ │ ├── tooltip.tsx
│ │ └── visually-hidden.tsx
│ ├── connect.ts
│ ├── contexts/
│ │ ├── AuthContext.tsx
│ │ ├── InstanceContext.tsx
│ │ ├── MemoFilterContext.tsx
│ │ └── ViewContext.tsx
│ ├── helpers/
│ │ ├── consts.ts
│ │ ├── resource-names.ts
│ │ └── utils.ts
│ ├── hooks/
│ │ ├── index.ts
│ │ ├── useAsyncEffect.ts
│ │ ├── useAttachmentQueries.ts
│ │ ├── useCurrentUser.ts
│ │ ├── useDateFilterNavigation.ts
│ │ ├── useDialog.ts
│ │ ├── useFilteredMemoStats.ts
│ │ ├── useInstanceQueries.ts
│ │ ├── useLiveMemoRefresh.ts
│ │ ├── useLoading.ts
│ │ ├── useMediaQuery.ts
│ │ ├── useMemoFilters.ts
│ │ ├── useMemoQueries.ts
│ │ ├── useMemoShareQueries.ts
│ │ ├── useMemoSorting.ts
│ │ ├── useNavigateTo.ts
│ │ ├── useTokenRefreshOnFocus.ts
│ │ ├── useUserLocale.ts
│ │ ├── useUserQueries.ts
│ │ └── useUserTheme.ts
│ ├── i18n.ts
│ ├── index.css
│ ├── layouts/
│ │ ├── MainLayout.tsx
│ │ └── RootLayout.tsx
│ ├── lib/
│ │ ├── calendar-utils.ts
│ │ ├── error.ts
│ │ ├── query-client.ts
│ │ └── utils.ts
│ ├── locales/
│ │ ├── ar.json
│ │ ├── ca.json
│ │ ├── cs.json
│ │ ├── de.json
│ │ ├── en-GB.json
│ │ ├── en.json
│ │ ├── es.json
│ │ ├── fa.json
│ │ ├── fr.json
│ │ ├── gl.json
│ │ ├── hi.json
│ │ ├── hr.json
│ │ ├── hu.json
│ │ ├── id.json
│ │ ├── it.json
│ │ ├── ja.json
│ │ ├── ka-GE.json
│ │ ├── ko.json
│ │ ├── mr.json
│ │ ├── nb.json
│ │ ├── nl.json
│ │ ├── pl.json
│ │ ├── pt-BR.json
│ │ ├── pt-PT.json
│ │ ├── ru.json
│ │ ├── sl.json
│ │ ├── sv.json
│ │ ├── th.json
│ │ ├── tr.json
│ │ ├── uk.json
│ │ ├── vi.json
│ │ ├── zh-Hans.json
│ │ └── zh-Hant.json
│ ├── main.tsx
│ ├── pages/
│ │ ├── AdminSignIn.tsx
│ │ ├── Archived.tsx
│ │ ├── Attachments.tsx
│ │ ├── AuthCallback.tsx
│ │ ├── Explore.tsx
│ │ ├── Home.tsx
│ │ ├── Inboxes.tsx
│ │ ├── MemoDetail.tsx
│ │ ├── NotFound.tsx
│ │ ├── PermissionDenied.tsx
│ │ ├── Setting.tsx
│ │ ├── SharedMemo.tsx
│ │ ├── SignIn.tsx
│ │ ├── SignUp.tsx
│ │ └── UserProfile.tsx
│ ├── router/
│ │ ├── index.tsx
│ │ └── routes.ts
│ ├── themes/
│ │ ├── COLOR_GUIDE.md
│ │ ├── default-dark.css
│ │ ├── default.css
│ │ └── paper.css
│ ├── types/
│ │ ├── common.d.ts
│ │ ├── common.ts
│ │ ├── i18n.d.ts
│ │ ├── markdown.ts
│ │ ├── modules/
│ │ │ └── setting.d.ts
│ │ ├── proto/
│ │ │ ├── api/
│ │ │ │ └── v1/
│ │ │ │ ├── attachment_service_pb.ts
│ │ │ │ ├── auth_service_pb.ts
│ │ │ │ ├── common_pb.ts
│ │ │ │ ├── idp_service_pb.ts
│ │ │ │ ├── instance_service_pb.ts
│ │ │ │ ├── memo_service_pb.ts
│ │ │ │ ├── shortcut_service_pb.ts
│ │ │ │ └── user_service_pb.ts
│ │ │ └── google/
│ │ │ ├── api/
│ │ │ │ ├── annotations_pb.ts
│ │ │ │ ├── client_pb.ts
│ │ │ │ ├── field_behavior_pb.ts
│ │ │ │ ├── http_pb.ts
│ │ │ │ ├── launch_stage_pb.ts
│ │ │ │ └── resource_pb.ts
│ │ │ └── type/
│ │ │ └── color_pb.ts
│ │ ├── statistics.ts
│ │ └── view.d.ts
│ └── utils/
│ ├── attachment.ts
│ ├── auth-redirect.ts
│ ├── format.ts
│ ├── i18n.ts
│ ├── markdown-list-detection.ts
│ ├── markdown-manipulation.ts
│ ├── memo.ts
│ ├── oauth.ts
│ ├── remark-plugins/
│ │ ├── remark-disable-setext.ts
│ │ ├── remark-preserve-type.ts
│ │ └── remark-tag.ts
│ ├── theme.ts
│ ├── user.ts
│ └── uuid.ts
├── tsconfig.json
└── vite.config.mts
Showing preview only (472K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (4675 symbols across 471 files)
FILE: internal/base/resource_name_test.go
function TestUIDMatcher (line 7) | func TestUIDMatcher(t *testing.T) {
FILE: internal/profile/profile.go
type Profile (line 15) | type Profile struct
method Validate (line 57) | func (p *Profile) Validate() error {
function checkDataDir (line 37) | func checkDataDir(dataDir string) (string, error) {
FILE: internal/util/util.go
function ConvertStringToInt32 (line 14) | func ConvertStringToInt32(src string) (int32, error) {
function HasPrefixes (line 23) | func HasPrefixes(src string, prefixes ...string) bool {
function ValidateEmail (line 33) | func ValidateEmail(email string) bool {
function GenUUID (line 40) | func GenUUID() string {
function RandomString (line 47) | func RandomString(n int) (string, error) {
function ReplaceString (line 66) | func ReplaceString(slice []string, old, new string) []string {
FILE: internal/util/util_test.go
function TestValidateEmail (line 7) | func TestValidateEmail(t *testing.T) {
FILE: internal/version/version.go
function GetCurrentVersion (line 14) | func GetCurrentVersion() string {
function GetMinorVersion (line 21) | func GetMinorVersion(version string) string {
function IsVersionGreaterOrEqualThan (line 31) | func IsVersionGreaterOrEqualThan(version, target string) bool {
function IsVersionGreaterThan (line 36) | func IsVersionGreaterThan(version, target string) bool {
type SortVersion (line 40) | type SortVersion
method Len (line 42) | func (s SortVersion) Len() int {
method Swap (line 46) | func (s SortVersion) Swap(i, j int) {
method Less (line 50) | func (s SortVersion) Less(i, j int) bool {
FILE: internal/version/version_test.go
function TestIsVersionGreaterOrEqualThan (line 11) | func TestIsVersionGreaterOrEqualThan(t *testing.T) {
function TestIsVersionGreaterThan (line 41) | func TestIsVersionGreaterThan(t *testing.T) {
function TestSortVersion (line 86) | func TestSortVersion(t *testing.T) {
FILE: plugin/cron/chain.go
type JobWrapper (line 12) | type JobWrapper
type Chain (line 16) | type Chain struct
method Then (line 34) | func (c Chain) Then(j Job) Job {
function NewChain (line 21) | func NewChain(c ...JobWrapper) Chain {
function Recover (line 42) | func Recover(logger Logger) JobWrapper {
function DelayIfStillRunning (line 65) | func DelayIfStillRunning(logger Logger) JobWrapper {
function SkipIfStillRunning (line 82) | func SkipIfStillRunning(logger Logger) JobWrapper {
FILE: plugin/cron/chain_test.go
function waitFor (line 13) | func waitFor(t *testing.T, timeout time.Duration, fn func() bool) {
function appendingJob (line 27) | func appendingJob(slice *[]int, value int) Job {
function appendingWrapper (line 36) | func appendingWrapper(slice *[]int, value int) JobWrapper {
function TestChain (line 45) | func TestChain(t *testing.T) {
function TestChainRecover (line 59) | func TestChainRecover(t *testing.T) {
type countJob (line 87) | type countJob struct
method Run (line 94) | func (j *countJob) Run() {
method Started (line 104) | func (j *countJob) Started() int {
method Done (line 110) | func (j *countJob) Done() int {
function TestChainDelayIfStillRunning (line 116) | func TestChainDelayIfStillRunning(t *testing.T) {
function TestChainSkipIfStillRunning (line 167) | func TestChainSkipIfStillRunning(t *testing.T) {
FILE: plugin/cron/constantdelay.go
type ConstantDelaySchedule (line 7) | type ConstantDelaySchedule struct
method Next (line 25) | func (schedule ConstantDelaySchedule) Next(t time.Time) time.Time {
function Every (line 14) | func Every(duration time.Duration) ConstantDelaySchedule {
FILE: plugin/cron/constantdelay_test.go
function TestConstantDelayNext (line 9) | func TestConstantDelayNext(t *testing.T) {
FILE: plugin/cron/cron.go
type Cron (line 13) | type Cron struct
method AddFunc (line 122) | func (c *Cron) AddFunc(spec string, cmd func()) (EntryID, error) {
method AddJob (line 129) | func (c *Cron) AddJob(spec string, cmd Job) (EntryID, error) {
method Schedule (line 139) | func (c *Cron) Schedule(schedule Schedule, cmd Job) EntryID {
method Entries (line 158) | func (c *Cron) Entries() []Entry {
method Location (line 170) | func (c *Cron) Location() *time.Location {
method Entry (line 175) | func (c *Cron) Entry(id EntryID) Entry {
method Remove (line 185) | func (c *Cron) Remove(id EntryID) {
method Start (line 196) | func (c *Cron) Start() {
method Run (line 207) | func (c *Cron) Run() {
method runScheduler (line 220) | func (c *Cron) runScheduler() {
method startJob (line 304) | func (c *Cron) startJob(j Job) {
method now (line 311) | func (c *Cron) now() time.Time {
method Stop (line 317) | func (c *Cron) Stop() context.Context {
method entrySnapshot (line 333) | func (c *Cron) entrySnapshot() []Entry {
method removeEntry (line 341) | func (c *Cron) removeEntry(id EntryID) {
type ScheduleParser (line 30) | type ScheduleParser interface
type Job (line 35) | type Job interface
type Schedule (line 40) | type Schedule interface
type EntryID (line 47) | type EntryID
type Entry (line 50) | type Entry struct
method Valid (line 75) | func (e Entry) Valid() bool { return e.ID != 0 }
function New (line 94) | func New(opts ...Option) *Cron {
type FuncJob (line 115) | type FuncJob
method Run (line 117) | func (f FuncJob) Run() { f() }
FILE: plugin/cron/cron_test.go
constant OneSecond (line 18) | OneSecond = 1*time.Second + 50*time.Millisecond
type syncWriter (line 20) | type syncWriter struct
method Write (line 25) | func (sw *syncWriter) Write(data []byte) (n int, err error) {
method String (line 32) | func (sw *syncWriter) String() string {
function newBufLogger (line 38) | func newBufLogger(sw *syncWriter) Logger {
function TestFuncPanicRecovery (line 42) | func TestFuncPanicRecovery(t *testing.T) {
type DummyJob (line 61) | type DummyJob struct
method Run (line 63) | func (DummyJob) Run() {
function TestJobPanicRecovery (line 67) | func TestJobPanicRecovery(t *testing.T) {
function TestNoEntries (line 87) | func TestNoEntries(t *testing.T) {
function TestStopCausesJobsToNotRun (line 99) | func TestStopCausesJobsToNotRun(t *testing.T) {
function TestAddBeforeRunning (line 117) | func TestAddBeforeRunning(t *testing.T) {
function TestAddWhileRunning (line 135) | func TestAddWhileRunning(t *testing.T) {
function TestAddWhileRunningWithDelay (line 152) | func TestAddWhileRunningWithDelay(t *testing.T) {
function TestRemoveBeforeRunning (line 167) | func TestRemoveBeforeRunning(t *testing.T) {
function TestRemoveWhileRunning (line 186) | func TestRemoveWhileRunning(t *testing.T) {
function TestSnapshotEntries (line 204) | func TestSnapshotEntries(t *testing.T) {
function TestMultipleEntries (line 231) | func TestMultipleEntries(t *testing.T) {
function TestRunningJobTwice (line 256) | func TestRunningJobTwice(t *testing.T) {
function TestRunningMultipleSchedules (line 275) | func TestRunningMultipleSchedules(t *testing.T) {
function TestLocalTimezone (line 298) | func TestLocalTimezone(t *testing.T) {
function TestNonLocalTimezone (line 326) | func TestNonLocalTimezone(t *testing.T) {
function TestStopWithoutStart (line 361) | func TestStopWithoutStart(t *testing.T) {
type testJob (line 366) | type testJob struct
method Run (line 371) | func (t testJob) Run() {
function TestInvalidJobSpec (line 376) | func TestInvalidJobSpec(t *testing.T) {
function TestBlockingRun (line 385) | func TestBlockingRun(t *testing.T) {
function TestStartNoop (line 410) | func TestStartNoop(t *testing.T) {
function TestJob (line 437) | func TestJob(t *testing.T) {
function TestScheduleAfterRemoval (line 491) | func TestScheduleAfterRemoval(t *testing.T) {
type ZeroSchedule (line 539) | type ZeroSchedule struct
method Next (line 541) | func (*ZeroSchedule) Next(time.Time) time.Time {
function TestJobWithZeroTimeDoesNotRun (line 546) | func TestJobWithZeroTimeDoesNotRun(t *testing.T) {
function TestStopAndWait (line 559) | func TestStopAndWait(t *testing.T) {
function TestMultiThreadedStartAndStop (line 674) | func TestMultiThreadedStartAndStop(t *testing.T) {
function wait (line 681) | func wait(wg *sync.WaitGroup) chan bool {
function stop (line 690) | func stop(cron *Cron) chan bool {
function newWithSeconds (line 700) | func newWithSeconds() *Cron {
FILE: plugin/cron/logger.go
type Logger (line 19) | type Logger interface
function PrintfLogger (line 28) | func PrintfLogger(l interface{ Printf(string, ...interface{}) }) Logger {
function VerbosePrintfLogger (line 34) | func VerbosePrintfLogger(l interface{ Printf(string, ...interface{}) }) ...
type printfLogger (line 38) | type printfLogger struct
method Info (line 43) | func (pl printfLogger) Info(msg string, keysAndValues ...interface{}) {
method Error (line 52) | func (pl printfLogger) Error(err error, msg string, keysAndValues ...i...
function formatString (line 61) | func formatString(numKeysAndValues int) string {
function formatTimes (line 77) | func formatTimes(keysAndValues []interface{}) []interface{} {
FILE: plugin/cron/option.go
type Option (line 8) | type Option
function WithLocation (line 11) | func WithLocation(loc *time.Location) Option {
function WithSeconds (line 19) | func WithSeconds() Option {
function WithParser (line 26) | func WithParser(p ScheduleParser) Option {
function WithChain (line 34) | func WithChain(wrappers ...JobWrapper) Option {
function WithLogger (line 41) | func WithLogger(logger Logger) Option {
FILE: plugin/cron/option_test.go
function TestWithLocation (line 11) | func TestWithLocation(t *testing.T) {
function TestWithParser (line 18) | func TestWithParser(t *testing.T) {
function TestWithVerboseLogger (line 26) | func TestWithVerboseLogger(t *testing.T) {
FILE: plugin/cron/parser.go
type ParseOption (line 16) | type ParseOption
constant Second (line 19) | Second ParseOption = 1 << iota
constant SecondOptional (line 20) | SecondOptional
constant Minute (line 21) | Minute
constant Hour (line 22) | Hour
constant Dom (line 23) | Dom
constant Month (line 24) | Month
constant Dow (line 25) | Dow
constant DowOptional (line 26) | DowOptional
constant Descriptor (line 27) | Descriptor
type Parser (line 49) | type Parser struct
method Parse (line 88) | func (p Parser) Parse(spec string) (Schedule, error) {
function NewParser (line 71) | func NewParser(options ParseOption) Parser {
function normalizeFields (line 160) | func normalizeFields(fields []string, options ParseOption) ([]string, er...
function ParseStandard (line 229) | func ParseStandard(standardSpec string) (Schedule, error) {
function getField (line 236) | func getField(field string, r bounds) (uint64, error) {
function getRange (line 254) | func getRange(expr string, r bounds) (uint64, error) {
function parseIntOrName (line 323) | func parseIntOrName(expr string, names map[string]uint) (uint, error) {
function mustParseInt (line 333) | func mustParseInt(expr string) (uint, error) {
function getBits (line 346) | func getBits(min, max, step uint) uint64 {
function all (line 362) | func all(r bounds) uint64 {
function parseDescriptor (line 367) | func parseDescriptor(descriptor string, loc *time.Location) (Schedule, e...
FILE: plugin/cron/parser_test.go
function TestRange (line 13) | func TestRange(t *testing.T) {
function TestField (line 61) | func TestField(t *testing.T) {
function TestAll (line 81) | func TestAll(t *testing.T) {
function TestBits (line 102) | func TestBits(t *testing.T) {
function TestParseScheduleErrors (line 122) | func TestParseScheduleErrors(t *testing.T) {
function TestParseSchedule (line 141) | func TestParseSchedule(t *testing.T) {
function TestOptionalSecondSchedule (line 185) | func TestOptionalSecondSchedule(t *testing.T) {
function TestNormalizeFields (line 207) | func TestNormalizeFields(t *testing.T) {
function TestNormalizeFields_Errors (line 271) | func TestNormalizeFields_Errors(t *testing.T) {
function TestStandardSpecSchedule (line 316) | func TestStandardSpecSchedule(t *testing.T) {
function TestNoDescriptorParser (line 354) | func TestNoDescriptorParser(t *testing.T) {
function every5min (line 362) | func every5min(loc *time.Location) *SpecSchedule {
function every5min5s (line 366) | func every5min5s(loc *time.Location) *SpecSchedule {
function midnight (line 370) | func midnight(loc *time.Location) *SpecSchedule {
function annual (line 374) | func annual(loc *time.Location) *SpecSchedule {
FILE: plugin/cron/spec.go
type SpecSchedule (line 7) | type SpecSchedule struct
method Next (line 58) | func (s *SpecSchedule) Next(t time.Time) time.Time {
type bounds (line 15) | type bounds struct
constant starBit (line 53) | starBit = 1 << 63
function dayMatches (line 179) | func dayMatches(s *SpecSchedule, t time.Time) bool {
FILE: plugin/cron/spec_test.go
function TestActivation (line 10) | func TestActivation(t *testing.T) {
function TestNext (line 75) | func TestNext(t *testing.T) {
function TestErrors (line 203) | func TestErrors(t *testing.T) {
function getTime (line 218) | func getTime(value string) time.Time {
function TestNextWithTz (line 249) | func TestNextWithTz(t *testing.T) {
function getTimeTZ (line 276) | func getTimeTZ(value string) time.Time {
function TestSlash0NoHang (line 295) | func TestSlash0NoHang(t *testing.T) {
FILE: plugin/email/client.go
type Client (line 11) | type Client struct
method validateConfig (line 23) | func (c *Client) validateConfig() error {
method createAuth (line 31) | func (c *Client) createAuth() smtp.Auth {
method createTLSConfig (line 39) | func (c *Client) createTLSConfig() *tls.Config {
method Send (line 47) | func (c *Client) Send(message *Message) error {
method sendWithTLS (line 78) | func (c *Client) sendWithTLS(auth smtp.Auth, recipients []string, body...
method sendWithSSL (line 91) | func (c *Client) sendWithSSL(auth smtp.Auth, recipients []string, body...
function NewClient (line 16) | func NewClient(config *Config) *Client {
FILE: plugin/email/client_test.go
function TestNewClient (line 9) | func TestNewClient(t *testing.T) {
function TestClientValidateConfig (line 26) | func TestClientValidateConfig(t *testing.T) {
function TestClientSendValidation (line 69) | func TestClientSendValidation(t *testing.T) {
FILE: plugin/email/config.go
type Config (line 11) | type Config struct
method Validate (line 31) | func (c *Config) Validate() error {
method GetServerAddress (line 45) | func (c *Config) GetServerAddress() string {
FILE: plugin/email/config_test.go
function TestConfigValidation (line 9) | func TestConfigValidation(t *testing.T) {
function TestConfigGetServerAddress (line 72) | func TestConfigGetServerAddress(t *testing.T) {
FILE: plugin/email/email.go
function Send (line 11) | func Send(config *Config, message *Message) error {
function SendAsync (line 26) | func SendAsync(config *Config, message *Message) {
FILE: plugin/email/email_test.go
function TestSend (line 11) | func TestSend(t *testing.T) {
function TestSendValidation (line 32) | func TestSendValidation(t *testing.T) {
function TestSendAsync (line 77) | func TestSendAsync(t *testing.T) {
function TestSendAsyncConcurrent (line 102) | func TestSendAsyncConcurrent(t *testing.T) {
FILE: plugin/email/message.go
type Message (line 11) | type Message struct
method Validate (line 22) | func (m *Message) Validate() error {
method Format (line 36) | func (m *Message) Format(fromEmail, fromName string) string {
method GetAllRecipients (line 85) | func (m *Message) GetAllRecipients() []string {
FILE: plugin/email/message_test.go
function TestMessageValidation (line 8) | func TestMessageValidation(t *testing.T) {
function TestMessageFormatPlainText (line 72) | func TestMessageFormatPlainText(t *testing.T) {
function TestMessageFormatHTML (line 100) | func TestMessageFormatHTML(t *testing.T) {
function TestMessageFormatMultipleRecipients (line 119) | func TestMessageFormatMultipleRecipients(t *testing.T) {
function TestGetAllRecipients (line 149) | func TestGetAllRecipients(t *testing.T) {
FILE: plugin/filter/engine.go
type Engine (line 14) | type Engine struct
method Compile (line 43) | func (e *Engine) Compile(_ context.Context, filter string) (*Program, ...
method CompileToStatement (line 71) | func (e *Engine) CompileToStatement(ctx context.Context, filter string...
function NewEngine (line 20) | func NewEngine(schema Schema) (*Engine, error) {
type Program (line 32) | type Program struct
method ConditionTree (line 38) | func (p *Program) ConditionTree() Condition {
method Render (line 93) | func (p *Program) Render(opts RenderOptions) (Statement, error) {
type RenderOptions (line 80) | type RenderOptions struct
type Statement (line 87) | type Statement struct
function DefaultEngine (line 108) | func DefaultEngine() (*Engine, error) {
function DefaultAttachmentEngine (line 116) | func DefaultAttachmentEngine() (*Engine, error) {
function normalizeLegacyFilter (line 123) | func normalizeLegacyFilter(expr string) string {
function rewriteNumericLogicalOperand (line 129) | func rewriteNumericLogicalOperand(expr, op string) string {
FILE: plugin/filter/helpers.go
function AppendConditions (line 9) | func AppendConditions(ctx context.Context, engine *Engine, filters []str...
FILE: plugin/filter/ir.go
type Condition (line 4) | type Condition interface
type LogicalOperator (line 9) | type LogicalOperator
constant LogicalAnd (line 12) | LogicalAnd LogicalOperator = "AND"
constant LogicalOr (line 13) | LogicalOr LogicalOperator = "OR"
type LogicalCondition (line 17) | type LogicalCondition struct
method isCondition (line 23) | func (*LogicalCondition) isCondition() {}
type NotCondition (line 26) | type NotCondition struct
method isCondition (line 30) | func (*NotCondition) isCondition() {}
type FieldPredicateCondition (line 33) | type FieldPredicateCondition struct
method isCondition (line 37) | func (*FieldPredicateCondition) isCondition() {}
type ComparisonOperator (line 40) | type ComparisonOperator
constant CompareEq (line 43) | CompareEq ComparisonOperator = "="
constant CompareNeq (line 44) | CompareNeq ComparisonOperator = "!="
constant CompareLt (line 45) | CompareLt ComparisonOperator = "<"
constant CompareLte (line 46) | CompareLte ComparisonOperator = "<="
constant CompareGt (line 47) | CompareGt ComparisonOperator = ">"
constant CompareGte (line 48) | CompareGte ComparisonOperator = ">="
type ComparisonCondition (line 52) | type ComparisonCondition struct
method isCondition (line 58) | func (*ComparisonCondition) isCondition() {}
type InCondition (line 61) | type InCondition struct
method isCondition (line 66) | func (*InCondition) isCondition() {}
type ElementInCondition (line 69) | type ElementInCondition struct
method isCondition (line 74) | func (*ElementInCondition) isCondition() {}
type ContainsCondition (line 77) | type ContainsCondition struct
method isCondition (line 82) | func (*ContainsCondition) isCondition() {}
type ConstantCondition (line 85) | type ConstantCondition struct
method isCondition (line 89) | func (*ConstantCondition) isCondition() {}
type ValueExpr (line 92) | type ValueExpr interface
type FieldRef (line 97) | type FieldRef struct
method isValueExpr (line 101) | func (*FieldRef) isValueExpr() {}
type LiteralValue (line 104) | type LiteralValue struct
method isValueExpr (line 108) | func (*LiteralValue) isValueExpr() {}
type FunctionValue (line 111) | type FunctionValue struct
method isValueExpr (line 116) | func (*FunctionValue) isValueExpr() {}
type ListComprehensionCondition (line 119) | type ListComprehensionCondition struct
method isCondition (line 126) | func (*ListComprehensionCondition) isCondition() {}
type ComprehensionKind (line 129) | type ComprehensionKind
constant ComprehensionExists (line 132) | ComprehensionExists ComprehensionKind = "exists"
type PredicateExpr (line 136) | type PredicateExpr interface
type StartsWithPredicate (line 141) | type StartsWithPredicate struct
method isPredicateExpr (line 145) | func (*StartsWithPredicate) isPredicateExpr() {}
type EndsWithPredicate (line 148) | type EndsWithPredicate struct
method isPredicateExpr (line 152) | func (*EndsWithPredicate) isPredicateExpr() {}
type ContainsPredicate (line 155) | type ContainsPredicate struct
method isPredicateExpr (line 159) | func (*ContainsPredicate) isPredicateExpr() {}
FILE: plugin/filter/parser.go
function buildCondition (line 10) | func buildCondition(expr *exprv1.Expr, schema Schema) (Condition, error) {
function buildCallCondition (line 46) | func buildCallCondition(call *exprv1.Expr_Call, schema Schema) (Conditio...
function buildComparisonCondition (line 109) | func buildComparisonCondition(call *exprv1.Expr_Call, schema Schema) (Co...
function buildInCondition (line 153) | func buildInCondition(call *exprv1.Expr_Call, schema Schema) (Condition,...
function buildContainsCondition (line 202) | func buildContainsCondition(call *exprv1.Expr_Call, schema Schema) (Cond...
function buildValueExpr (line 235) | func buildValueExpr(expr *exprv1.Expr, schema Schema) (ValueExpr, error) {
function toComparisonOperator (line 291) | func toComparisonOperator(fn string) (ComparisonOperator, error) {
function getIdentName (line 310) | func getIdentName(expr *exprv1.Expr) (string, error) {
function getConstValue (line 317) | func getConstValue(expr *exprv1.Expr) (interface{}, error) {
function evaluateBool (line 340) | func evaluateBool(call *exprv1.Expr_Call) (bool, bool, error) {
function evaluateBoolExpr (line 345) | func evaluateBoolExpr(expr *exprv1.Expr) (bool, bool, error) {
function evaluateNumeric (line 365) | func evaluateNumeric(expr *exprv1.Expr) (int64, bool, error) {
function timeNowUnix (line 417) | func timeNowUnix() int64 {
function buildComprehensionCondition (line 422) | func buildComprehensionCondition(comp *exprv1.Expr_Comprehension, schema...
function detectComprehensionKind (line 461) | func detectComprehensionKind(comp *exprv1.Expr_Comprehension) (Comprehen...
function extractPredicate (line 486) | func extractPredicate(comp *exprv1.Expr_Comprehension, _ Schema) (Predic...
function buildStartsWithPredicate (line 519) | func buildStartsWithPredicate(call *exprv1.Expr_Call, iterVar string) (P...
function buildEndsWithPredicate (line 543) | func buildEndsWithPredicate(call *exprv1.Expr_Call, iterVar string) (Pre...
function buildContainsPredicate (line 566) | func buildContainsPredicate(call *exprv1.Expr_Call, iterVar string) (Pre...
FILE: plugin/filter/render.go
type renderer (line 10) | type renderer struct
method Render (line 32) | func (r *renderer) Render(cond Condition) (Statement, error) {
method renderCondition (line 61) | func (r *renderer) renderCondition(cond Condition) (renderResult, erro...
method renderLogicalCondition (line 89) | func (r *renderer) renderLogicalCondition(cond *LogicalCondition) (ren...
method renderNotCondition (line 109) | func (r *renderer) renderNotCondition(cond *NotCondition) (renderResul...
method renderFieldPredicate (line 126) | func (r *renderer) renderFieldPredicate(cond *FieldPredicateCondition)...
method renderComparison (line 149) | func (r *renderer) renderComparison(cond *ComparisonCondition) (render...
method renderFunctionComparison (line 173) | func (r *renderer) renderFunctionComparison(fn *FunctionValue, op Comp...
method renderScalarComparison (line 205) | func (r *renderer) renderScalarComparison(field Field, op ComparisonOp...
method renderBoolColumnComparison (line 246) | func (r *renderer) renderBoolColumnComparison(field Field, op Comparis...
method renderJSONBoolComparison (line 258) | func (r *renderer) renderJSONBoolComparison(field Field, op Comparison...
method renderInCondition (line 313) | func (r *renderer) renderInCondition(cond *InCondition) (renderResult,...
method renderTagInList (line 335) | func (r *renderer) renderTagInList(values []ValueExpr) (renderResult, ...
method renderElementInCondition (line 384) | func (r *renderer) renderElementInCondition(cond *ElementInCondition) ...
method renderScalarInCondition (line 417) | func (r *renderer) renderScalarInCondition(field Field, values []Value...
method renderContainsCondition (line 449) | func (r *renderer) renderContainsCondition(cond *ContainsCondition) (r...
method renderListComprehension (line 471) | func (r *renderer) renderListComprehension(cond *ListComprehensionCond...
method renderTagStartsWith (line 495) | func (r *renderer) renderTagStartsWith(field Field, prefix string, _ C...
method renderTagEndsWith (line 519) | func (r *renderer) renderTagEndsWith(field Field, suffix string, _ Com...
method renderTagContains (line 528) | func (r *renderer) renderTagContains(field Field, substring string, _ ...
method buildJSONArrayLike (line 538) | func (r *renderer) buildJSONArrayLike(arrayExpr, pattern string) string {
method wrapWithNullCheck (line 551) | func (r *renderer) wrapWithNullCheck(arrayExpr, condition string) stri...
method jsonBoolPredicate (line 566) | func (r *renderer) jsonBoolPredicate(field Field) (string, error) {
method addArg (line 610) | func (r *renderer) addArg(value any) string {
method addBoolArg (line 619) | func (r *renderer) addBoolArg(value bool) string {
type renderResult (line 18) | type renderResult struct
function newRenderer (line 24) | func newRenderer(schema Schema, opts RenderOptions) *renderer {
function combineAnd (line 580) | func combineAnd(left, right renderResult) renderResult {
function combineOr (line 595) | func combineOr(left, right renderResult) renderResult {
function expectLiteral (line 634) | func expectLiteral(expr ValueExpr) (any, error) {
function expectBool (line 642) | func expectBool(expr ValueExpr) (bool, error) {
function expectNumericLiteral (line 654) | func expectNumericLiteral(expr ValueExpr) (int64, error) {
function toInt64 (line 662) | func toInt64(value any) (int64, error) {
function sqlOperator (line 683) | func sqlOperator(op ComparisonOperator) string {
function qualifyColumn (line 687) | func qualifyColumn(d DialectName, col Column) string {
function jsonPath (line 696) | func jsonPath(field Field) string {
function jsonExtractExpr (line 700) | func jsonExtractExpr(d DialectName, field Field) string {
function jsonArrayExpr (line 712) | func jsonArrayExpr(d DialectName, field Field) string {
function jsonArrayLengthExpr (line 724) | func jsonArrayLengthExpr(d DialectName, field Field) string {
function buildPostgresJSONAccessor (line 738) | func buildPostgresJSONAccessor(base string, path []string, terminalText ...
FILE: plugin/filter/schema.go
type DialectName (line 13) | type DialectName
constant DialectSQLite (line 16) | DialectSQLite DialectName = "sqlite"
constant DialectMySQL (line 17) | DialectMySQL DialectName = "mysql"
constant DialectPostgres (line 18) | DialectPostgres DialectName = "postgres"
type FieldType (line 22) | type FieldType
constant FieldTypeString (line 25) | FieldTypeString FieldType = "string"
constant FieldTypeInt (line 26) | FieldTypeInt FieldType = "int"
constant FieldTypeBool (line 27) | FieldTypeBool FieldType = "bool"
constant FieldTypeTimestamp (line 28) | FieldTypeTimestamp FieldType = "timestamp"
type FieldKind (line 32) | type FieldKind
constant FieldKindScalar (line 35) | FieldKindScalar FieldKind = "scalar"
constant FieldKindBoolColumn (line 36) | FieldKindBoolColumn FieldKind = "bool_column"
constant FieldKindJSONBool (line 37) | FieldKindJSONBool FieldKind = "json_bool"
constant FieldKindJSONList (line 38) | FieldKindJSONList FieldKind = "json_list"
constant FieldKindVirtualAlias (line 39) | FieldKindVirtualAlias FieldKind = "virtual_alias"
type Column (line 43) | type Column struct
type Field (line 49) | type Field struct
method columnExpr (line 313) | func (f Field) columnExpr(d DialectName) string {
type Schema (line 62) | type Schema struct
method Field (line 69) | func (s Schema) Field(name string) (Field, bool) {
method ResolveAlias (line 75) | func (s Schema) ResolveAlias(name string) (Field, bool) {
function NewSchema (line 101) | func NewSchema() Schema {
function NewAttachmentSchema (line 253) | func NewAttachmentSchema() Schema {
FILE: plugin/httpgetter/html_meta.go
type HTMLMeta (line 29) | type HTMLMeta struct
function GetHTMLMeta (line 35) | func GetHTMLMeta(urlStr string) (*HTMLMeta, error) {
function extractHTMLMeta (line 61) | func extractHTMLMeta(resp io.Reader) *HTMLMeta {
function extractMetaProperty (line 106) | func extractMetaProperty(token html.Token, prop string) (content string,...
function validateURL (line 119) | func validateURL(urlStr string) error {
function enrichSiteMeta (line 157) | func enrichSiteMeta(url *url.URL, meta *HTMLMeta) {
FILE: plugin/httpgetter/html_meta_test.go
function TestGetHTMLMeta (line 10) | func TestGetHTMLMeta(t *testing.T) {
function TestGetHTMLMetaForInternal (line 22) | func TestGetHTMLMetaForInternal(t *testing.T) {
FILE: plugin/httpgetter/image.go
type Image (line 11) | type Image struct
function GetImage (line 16) | func GetImage(urlStr string) (*Image, error) {
FILE: plugin/httpgetter/util.go
function getMediatype (line 8) | func getMediatype(response *http.Response) (string, error) {
FILE: plugin/idp/idp.go
type IdentityProviderUserInfo (line 3) | type IdentityProviderUserInfo struct
FILE: plugin/idp/oauth2/oauth2.go
type IdentityProvider (line 20) | type IdentityProvider struct
method ExchangeToken (line 45) | func (p *IdentityProvider) ExchangeToken(ctx context.Context, redirect...
method UserInfo (line 81) | func (p *IdentityProvider) UserInfo(token string) (*idp.IdentityProvid...
function NewIdentityProvider (line 25) | func NewIdentityProvider(config *storepb.OAuth2Config) (*IdentityProvide...
FILE: plugin/idp/oauth2/oauth2_test.go
function TestNewIdentityProvider (line 20) | func TestNewIdentityProvider(t *testing.T) {
function newMockServer (line 77) | func newMockServer(t *testing.T, code, accessToken string, userinfo []by...
function TestIdentityProvider (line 112) | func TestIdentityProvider(t *testing.T) {
FILE: plugin/markdown/ast/tag.go
type TagNode (line 8) | type TagNode struct
method Kind (line 19) | func (*TagNode) Kind() gast.NodeKind {
method Dump (line 24) | func (n *TagNode) Dump(source []byte, level int) {
FILE: plugin/markdown/extensions/tag.go
type tagExtension (line 11) | type tagExtension struct
method Extend (line 17) | func (*tagExtension) Extend(m goldmark.Markdown) {
FILE: plugin/markdown/markdown.go
type ExtractedData (line 21) | type ExtractedData struct
type Service (line 30) | type Service interface
type service (line 57) | type service struct
method parse (line 104) | func (s *service) parse(content []byte) (gast.Node, error) {
method ExtractTags (line 111) | func (s *service) ExtractTags(content []byte) ([]string, error) {
method ExtractProperties (line 162) | func (s *service) ExtractProperties(content []byte) (*storepb.MemoPayl...
method RenderMarkdown (line 213) | func (s *service) RenderMarkdown(content []byte) (string, error) {
method RenderHTML (line 224) | func (s *service) RenderHTML(content []byte) (string, error) {
method GenerateSnippet (line 233) | func (s *service) GenerateSnippet(content []byte, maxLength int) (stri...
method ValidateContent (line 318) | func (s *service) ValidateContent(content []byte) error {
method ExtractAll (line 325) | func (s *service) ExtractAll(content []byte) (*ExtractedData, error) {
method RenameTag (line 390) | func (s *service) RenameTag(content []byte, oldTag, newTag string) (st...
type Option (line 62) | type Option
type config (line 64) | type config struct
function WithTagExtension (line 69) | func WithTagExtension() Option {
function NewService (line 76) | func NewService(opts ...Option) Service {
function extractHeadingText (line 142) | func extractHeadingText(n gast.Node, source []byte) string {
function extractTextFromNode (line 151) | func extractTextFromNode(n gast.Node, source []byte, buf *strings.Builde...
function uniquePreserveCase (line 422) | func uniquePreserveCase(strs []string) []string {
function truncateAtWord (line 438) | func truncateAtWord(s string, maxLength int) string {
FILE: plugin/markdown/markdown_test.go
function TestNewService (line 10) | func TestNewService(t *testing.T) {
function TestValidateContent (line 15) | func TestValidateContent(t *testing.T) {
function TestGenerateSnippet (line 52) | func TestGenerateSnippet(t *testing.T) {
function TestExtractProperties (line 185) | func TestExtractProperties(t *testing.T) {
function TestExtractAllTitle (line 309) | func TestExtractAllTitle(t *testing.T) {
function TestExtractTags (line 343) | func TestExtractTags(t *testing.T) {
function TestUniquePreserveCase (line 464) | func TestUniquePreserveCase(t *testing.T) {
function TestTruncateAtWord (line 500) | func TestTruncateAtWord(t *testing.T) {
function BenchmarkGenerateSnippet (line 554) | func BenchmarkGenerateSnippet(b *testing.B) {
function BenchmarkExtractProperties (line 583) | func BenchmarkExtractProperties(b *testing.B) {
FILE: plugin/markdown/parser/tag.go
constant MaxTagLength (line 16) | MaxTagLength = 100
type tagParser (line 19) | type tagParser struct
method Trigger (line 27) | func (*tagParser) Trigger() []byte {
method Parse (line 79) | func (*tagParser) Parse(_ gast.Node, block text.Reader, _ parser.Conte...
function NewTagParser (line 22) | func NewTagParser() parser.InlineParser {
function isValidTagRune (line 33) | func isValidTagRune(r rune) bool {
FILE: plugin/markdown/parser/tag_test.go
function TestTagParser (line 14) | func TestTagParser(t *testing.T) {
function TestTagParser_Trigger (line 213) | func TestTagParser_Trigger(t *testing.T) {
function TestTagParser_MultipleTags (line 220) | func TestTagParser_MultipleTags(t *testing.T) {
function TestTagNode_Kind (line 246) | func TestTagNode_Kind(t *testing.T) {
function TestTagNode_Dump (line 254) | func TestTagNode_Dump(t *testing.T) {
FILE: plugin/markdown/renderer/markdown_renderer.go
type MarkdownRenderer (line 15) | type MarkdownRenderer struct
method Render (line 27) | func (r *MarkdownRenderer) Render(node gast.Node, source []byte) string {
method renderNode (line 34) | func (r *MarkdownRenderer) renderNode(node gast.Node, source []byte, d...
method renderChildren (line 166) | func (r *MarkdownRenderer) renderChildren(node gast.Node, source []byt...
method renderCodeBlock (line 175) | func (r *MarkdownRenderer) renderCodeBlock(node gast.Node, source []by...
method renderBlockquote (line 210) | func (r *MarkdownRenderer) renderBlockquote(node *gast.Blockquote, sou...
method renderListItem (line 229) | func (r *MarkdownRenderer) renderListItem(node *gast.ListItem, source ...
method renderTable (line 262) | func (r *MarkdownRenderer) renderTable(table *east.Table, source []byt...
function NewMarkdownRenderer (line 20) | func NewMarkdownRenderer() *MarkdownRenderer {
FILE: plugin/markdown/renderer/markdown_renderer_test.go
function TestMarkdownRenderer (line 16) | func TestMarkdownRenderer(t *testing.T) {
function TestMarkdownRendererPreservesStructure (line 144) | func TestMarkdownRendererPreservesStructure(t *testing.T) {
FILE: plugin/scheduler/example_test.go
function Example_basic (line 14) | func Example_basic() {
function Example_timezone (line 35) | func Example_timezone() {
function Example_middleware (line 54) | func Example_middleware() {
type slogAdapter (line 81) | type slogAdapter struct
method Info (line 85) | func (a *slogAdapter) Info(msg string, args ...interface{}) {
method Error (line 89) | func (a *slogAdapter) Error(msg string, args ...interface{}) {
function Example_multipleJobs (line 94) | func Example_multipleJobs() {
function Example_gracefulShutdown (line 135) | func Example_gracefulShutdown() {
FILE: plugin/scheduler/integration_test.go
function TestRealWorldScenario (line 17) | func TestRealWorldScenario(t *testing.T) {
function TestCancellationDuringExecution (line 126) | func TestCancellationDuringExecution(t *testing.T) {
function TestTimezoneHandling (line 181) | func TestTimezoneHandling(t *testing.T) {
function TestErrorPropagation (line 216) | func TestErrorPropagation(t *testing.T) {
function TestPanicRecovery (line 261) | func TestPanicRecovery(t *testing.T) {
function TestMultipleJobsWithDifferentSchedules (line 301) | func TestMultipleJobsWithDifferentSchedules(t *testing.T) {
type testLogger (line 374) | type testLogger struct
method Info (line 379) | func (l *testLogger) Info(msg string, args ...interface{}) {
method Error (line 385) | func (l *testLogger) Error(msg string, args ...interface{}) {
function contains (line 391) | func contains(s, substr string) bool {
FILE: plugin/scheduler/job.go
type JobHandler (line 11) | type JobHandler
type Job (line 14) | type Job struct
method Validate (line 39) | func (j *Job) Validate() error {
FILE: plugin/scheduler/job_test.go
function TestJobDefinition (line 8) | func TestJobDefinition(t *testing.T) {
function TestJobValidation (line 32) | func TestJobValidation(t *testing.T) {
FILE: plugin/scheduler/middleware.go
type Middleware (line 11) | type Middleware
function Chain (line 15) | func Chain(middlewares ...Middleware) Middleware {
function Recovery (line 26) | func Recovery(onPanic func(jobName string, recovered interface{})) Middl...
type Logger (line 44) | type Logger interface
function Logging (line 50) | func Logging(logger Logger) Middleware {
function Timeout (line 73) | func Timeout(duration time.Duration) Middleware {
type contextKey (line 95) | type contextKey
constant jobNameKey (line 98) | jobNameKey contextKey = iota
function withJobName (line 102) | func withJobName(ctx context.Context, name string) context.Context {
function getJobName (line 107) | func getJobName(ctx context.Context) string {
function GetJobName (line 118) | func GetJobName(ctx context.Context) string {
FILE: plugin/scheduler/middleware_test.go
function TestMiddlewareChaining (line 10) | func TestMiddlewareChaining(t *testing.T) {
function TestRecoveryMiddleware (line 55) | func TestRecoveryMiddleware(t *testing.T) {
function TestLoggingMiddleware (line 80) | func TestLoggingMiddleware(t *testing.T) {
type testLogger (line 131) | type testLogger struct
method Info (line 136) | func (l *testLogger) Info(msg string, args ...interface{}) {
method Error (line 142) | func (l *testLogger) Error(msg string, args ...interface{}) {
FILE: plugin/scheduler/parser.go
type Schedule (line 12) | type Schedule struct
method Next (line 91) | func (s *Schedule) Next(from time.Time) time.Time {
method matches (line 120) | func (s *Schedule) matches(t time.Time) bool {
type fieldMatcher (line 23) | type fieldMatcher interface
function ParseCronExpression (line 29) | func ParseCronExpression(expr string) (*Schedule, error) {
function parseField (line 129) | func parseField(field string, min, max int) (fieldMatcher, error) {
type wildcardMatcher (line 181) | type wildcardMatcher struct
method matches (line 183) | func (*wildcardMatcher) matches(_ int) bool {
type exactMatcher (line 188) | type exactMatcher struct
method matches (line 192) | func (m *exactMatcher) matches(value int) bool {
type rangeMatcher (line 197) | type rangeMatcher struct
method matches (line 201) | func (m *rangeMatcher) matches(value int) bool {
type listMatcher (line 206) | type listMatcher struct
method matches (line 210) | func (m *listMatcher) matches(value int) bool {
type stepMatcher (line 220) | type stepMatcher struct
method matches (line 224) | func (m *stepMatcher) matches(value int) bool {
FILE: plugin/scheduler/parser_test.go
function TestParseCronExpression (line 8) | func TestParseCronExpression(t *testing.T) {
function TestScheduleNext (line 55) | func TestScheduleNext(t *testing.T) {
function TestScheduleNextWithTimezone (line 109) | func TestScheduleNextWithTimezone(t *testing.T) {
FILE: plugin/scheduler/scheduler.go
type Scheduler (line 12) | type Scheduler struct
method Register (line 70) | func (s *Scheduler) Register(job *Job) error {
method Start (line 91) | func (s *Scheduler) Start() error {
method runJobWithSchedule (line 121) | func (s *Scheduler) runJobWithSchedule(ctx context.Context, rj *regist...
method Stop (line 169) | func (s *Scheduler) Stop(ctx context.Context) error {
type registeredJob (line 24) | type registeredJob struct
type Option (line 30) | type Option
function WithTimezone (line 33) | func WithTimezone(tz string) Option {
function WithMiddleware (line 45) | func WithMiddleware(mw ...Middleware) Option {
function New (line 54) | func New(opts ...Option) *Scheduler {
FILE: plugin/scheduler/scheduler_test.go
function TestSchedulerCreation (line 13) | func TestSchedulerCreation(t *testing.T) {
function TestSchedulerWithTimezone (line 20) | func TestSchedulerWithTimezone(t *testing.T) {
function TestJobRegistration (line 27) | func TestJobRegistration(t *testing.T) {
function TestSchedulerStartStop (line 46) | func TestSchedulerStartStop(t *testing.T) {
function TestSchedulerWithMiddleware (line 93) | func TestSchedulerWithMiddleware(t *testing.T) {
FILE: plugin/storage/s3/s3.go
type Client (line 17) | type Client struct
method UploadObject (line 44) | func (c *Client) UploadObject(ctx context.Context, key string, fileTyp...
method PresignGetObject (line 58) | func (c *Client) PresignGetObject(ctx context.Context, key string) (st...
method GetObject (line 75) | func (c *Client) GetObject(ctx context.Context, key string) ([]byte, e...
method GetObjectStream (line 92) | func (c *Client) GetObjectStream(ctx context.Context, key string) (io....
method DeleteObject (line 104) | func (c *Client) DeleteObject(ctx context.Context, key string) error {
function NewClient (line 22) | func NewClient(ctx context.Context, s3Config *storepb.StorageS3Config) (...
FILE: plugin/webhook/validate.go
function init (line 28) | func init() {
function isReservedIP (line 44) | func isReservedIP(ip net.IP) bool {
function ValidateURL (line 62) | func ValidateURL(rawURL string) error {
FILE: plugin/webhook/webhook.go
function safeDialContext (line 35) | func safeDialContext(ctx context.Context, network, addr string) (net.Con...
type WebhookRequestPayload (line 55) | type WebhookRequestPayload struct
function Post (line 67) | func Post(requestPayload *WebhookRequestPayload) error {
function PostAsync (line 111) | func PostAsync(requestPayload *WebhookRequestPayload) {
FILE: proto/gen/api/v1/apiv1connect/attachment_service.connect.go
constant _ (line 22) | _ = connect.IsAtLeastVersion1_13_0
constant AttachmentServiceName (line 26) | AttachmentServiceName = "memos.api.v1.AttachmentService"
constant AttachmentServiceCreateAttachmentProcedure (line 39) | AttachmentServiceCreateAttachmentProcedure = "/memos.api.v1.AttachmentSe...
constant AttachmentServiceListAttachmentsProcedure (line 42) | AttachmentServiceListAttachmentsProcedure = "/memos.api.v1.AttachmentSer...
constant AttachmentServiceGetAttachmentProcedure (line 45) | AttachmentServiceGetAttachmentProcedure = "/memos.api.v1.AttachmentServi...
constant AttachmentServiceUpdateAttachmentProcedure (line 48) | AttachmentServiceUpdateAttachmentProcedure = "/memos.api.v1.AttachmentSe...
constant AttachmentServiceDeleteAttachmentProcedure (line 51) | AttachmentServiceDeleteAttachmentProcedure = "/memos.api.v1.AttachmentSe...
type AttachmentServiceClient (line 55) | type AttachmentServiceClient interface
function NewAttachmentServiceClient (line 75) | func NewAttachmentServiceClient(httpClient connect.HTTPClient, baseURL s...
type attachmentServiceClient (line 113) | type attachmentServiceClient struct
method CreateAttachment (line 122) | func (c *attachmentServiceClient) CreateAttachment(ctx context.Context...
method ListAttachments (line 127) | func (c *attachmentServiceClient) ListAttachments(ctx context.Context,...
method GetAttachment (line 132) | func (c *attachmentServiceClient) GetAttachment(ctx context.Context, r...
method UpdateAttachment (line 137) | func (c *attachmentServiceClient) UpdateAttachment(ctx context.Context...
method DeleteAttachment (line 142) | func (c *attachmentServiceClient) DeleteAttachment(ctx context.Context...
type AttachmentServiceHandler (line 147) | type AttachmentServiceHandler interface
function NewAttachmentServiceHandler (line 165) | func NewAttachmentServiceHandler(svc AttachmentServiceHandler, opts ...c...
type UnimplementedAttachmentServiceHandler (line 216) | type UnimplementedAttachmentServiceHandler struct
method CreateAttachment (line 218) | func (UnimplementedAttachmentServiceHandler) CreateAttachment(context....
method ListAttachments (line 222) | func (UnimplementedAttachmentServiceHandler) ListAttachments(context.C...
method GetAttachment (line 226) | func (UnimplementedAttachmentServiceHandler) GetAttachment(context.Con...
method UpdateAttachment (line 230) | func (UnimplementedAttachmentServiceHandler) UpdateAttachment(context....
method DeleteAttachment (line 234) | func (UnimplementedAttachmentServiceHandler) DeleteAttachment(context....
FILE: proto/gen/api/v1/apiv1connect/auth_service.connect.go
constant _ (line 22) | _ = connect.IsAtLeastVersion1_13_0
constant AuthServiceName (line 26) | AuthServiceName = "memos.api.v1.AuthService"
constant AuthServiceGetCurrentUserProcedure (line 39) | AuthServiceGetCurrentUserProcedure = "/memos.api.v1.AuthService/GetCurre...
constant AuthServiceSignInProcedure (line 41) | AuthServiceSignInProcedure = "/memos.api.v1.AuthService/SignIn"
constant AuthServiceSignOutProcedure (line 43) | AuthServiceSignOutProcedure = "/memos.api.v1.AuthService/SignOut"
constant AuthServiceRefreshTokenProcedure (line 46) | AuthServiceRefreshTokenProcedure = "/memos.api.v1.AuthService/RefreshToken"
type AuthServiceClient (line 50) | type AuthServiceClient interface
function NewAuthServiceClient (line 75) | func NewAuthServiceClient(httpClient connect.HTTPClient, baseURL string,...
type authServiceClient (line 107) | type authServiceClient struct
method GetCurrentUser (line 115) | func (c *authServiceClient) GetCurrentUser(ctx context.Context, req *c...
method SignIn (line 120) | func (c *authServiceClient) SignIn(ctx context.Context, req *connect.R...
method SignOut (line 125) | func (c *authServiceClient) SignOut(ctx context.Context, req *connect....
method RefreshToken (line 130) | func (c *authServiceClient) RefreshToken(ctx context.Context, req *con...
type AuthServiceHandler (line 135) | type AuthServiceHandler interface
function NewAuthServiceHandler (line 158) | func NewAuthServiceHandler(svc AuthServiceHandler, opts ...connect.Handl...
type UnimplementedAuthServiceHandler (line 201) | type UnimplementedAuthServiceHandler struct
method GetCurrentUser (line 203) | func (UnimplementedAuthServiceHandler) GetCurrentUser(context.Context,...
method SignIn (line 207) | func (UnimplementedAuthServiceHandler) SignIn(context.Context, *connec...
method SignOut (line 211) | func (UnimplementedAuthServiceHandler) SignOut(context.Context, *conne...
method RefreshToken (line 215) | func (UnimplementedAuthServiceHandler) RefreshToken(context.Context, *...
FILE: proto/gen/api/v1/apiv1connect/idp_service.connect.go
constant _ (line 22) | _ = connect.IsAtLeastVersion1_13_0
constant IdentityProviderServiceName (line 26) | IdentityProviderServiceName = "memos.api.v1.IdentityProviderService"
constant IdentityProviderServiceListIdentityProvidersProcedure (line 39) | IdentityProviderServiceListIdentityProvidersProcedure = "/memos.api.v1.I...
constant IdentityProviderServiceGetIdentityProviderProcedure (line 42) | IdentityProviderServiceGetIdentityProviderProcedure = "/memos.api.v1.Ide...
constant IdentityProviderServiceCreateIdentityProviderProcedure (line 45) | IdentityProviderServiceCreateIdentityProviderProcedure = "/memos.api.v1....
constant IdentityProviderServiceUpdateIdentityProviderProcedure (line 48) | IdentityProviderServiceUpdateIdentityProviderProcedure = "/memos.api.v1....
constant IdentityProviderServiceDeleteIdentityProviderProcedure (line 51) | IdentityProviderServiceDeleteIdentityProviderProcedure = "/memos.api.v1....
type IdentityProviderServiceClient (line 55) | type IdentityProviderServiceClient interface
function NewIdentityProviderServiceClient (line 75) | func NewIdentityProviderServiceClient(httpClient connect.HTTPClient, bas...
type identityProviderServiceClient (line 113) | type identityProviderServiceClient struct
method ListIdentityProviders (line 122) | func (c *identityProviderServiceClient) ListIdentityProviders(ctx cont...
method GetIdentityProvider (line 127) | func (c *identityProviderServiceClient) GetIdentityProvider(ctx contex...
method CreateIdentityProvider (line 132) | func (c *identityProviderServiceClient) CreateIdentityProvider(ctx con...
method UpdateIdentityProvider (line 137) | func (c *identityProviderServiceClient) UpdateIdentityProvider(ctx con...
method DeleteIdentityProvider (line 142) | func (c *identityProviderServiceClient) DeleteIdentityProvider(ctx con...
type IdentityProviderServiceHandler (line 148) | type IdentityProviderServiceHandler interface
function NewIdentityProviderServiceHandler (line 166) | func NewIdentityProviderServiceHandler(svc IdentityProviderServiceHandle...
type UnimplementedIdentityProviderServiceHandler (line 217) | type UnimplementedIdentityProviderServiceHandler struct
method ListIdentityProviders (line 219) | func (UnimplementedIdentityProviderServiceHandler) ListIdentityProvide...
method GetIdentityProvider (line 223) | func (UnimplementedIdentityProviderServiceHandler) GetIdentityProvider...
method CreateIdentityProvider (line 227) | func (UnimplementedIdentityProviderServiceHandler) CreateIdentityProvi...
method UpdateIdentityProvider (line 231) | func (UnimplementedIdentityProviderServiceHandler) UpdateIdentityProvi...
method DeleteIdentityProvider (line 235) | func (UnimplementedIdentityProviderServiceHandler) DeleteIdentityProvi...
FILE: proto/gen/api/v1/apiv1connect/instance_service.connect.go
constant _ (line 21) | _ = connect.IsAtLeastVersion1_13_0
constant InstanceServiceName (line 25) | InstanceServiceName = "memos.api.v1.InstanceService"
constant InstanceServiceGetInstanceProfileProcedure (line 38) | InstanceServiceGetInstanceProfileProcedure = "/memos.api.v1.InstanceServ...
constant InstanceServiceGetInstanceSettingProcedure (line 41) | InstanceServiceGetInstanceSettingProcedure = "/memos.api.v1.InstanceServ...
constant InstanceServiceUpdateInstanceSettingProcedure (line 44) | InstanceServiceUpdateInstanceSettingProcedure = "/memos.api.v1.InstanceS...
type InstanceServiceClient (line 48) | type InstanceServiceClient interface
function NewInstanceServiceClient (line 64) | func NewInstanceServiceClient(httpClient connect.HTTPClient, baseURL str...
type instanceServiceClient (line 90) | type instanceServiceClient struct
method GetInstanceProfile (line 97) | func (c *instanceServiceClient) GetInstanceProfile(ctx context.Context...
method GetInstanceSetting (line 102) | func (c *instanceServiceClient) GetInstanceSetting(ctx context.Context...
method UpdateInstanceSetting (line 107) | func (c *instanceServiceClient) UpdateInstanceSetting(ctx context.Cont...
type InstanceServiceHandler (line 112) | type InstanceServiceHandler interface
function NewInstanceServiceHandler (line 126) | func NewInstanceServiceHandler(svc InstanceServiceHandler, opts ...conne...
type UnimplementedInstanceServiceHandler (line 161) | type UnimplementedInstanceServiceHandler struct
method GetInstanceProfile (line 163) | func (UnimplementedInstanceServiceHandler) GetInstanceProfile(context....
method GetInstanceSetting (line 167) | func (UnimplementedInstanceServiceHandler) GetInstanceSetting(context....
method UpdateInstanceSetting (line 171) | func (UnimplementedInstanceServiceHandler) UpdateInstanceSetting(conte...
FILE: proto/gen/api/v1/apiv1connect/memo_service.connect.go
constant _ (line 22) | _ = connect.IsAtLeastVersion1_13_0
constant MemoServiceName (line 26) | MemoServiceName = "memos.api.v1.MemoService"
constant MemoServiceCreateMemoProcedure (line 38) | MemoServiceCreateMemoProcedure = "/memos.api.v1.MemoService/CreateMemo"
constant MemoServiceListMemosProcedure (line 40) | MemoServiceListMemosProcedure = "/memos.api.v1.MemoService/ListMemos"
constant MemoServiceGetMemoProcedure (line 42) | MemoServiceGetMemoProcedure = "/memos.api.v1.MemoService/GetMemo"
constant MemoServiceUpdateMemoProcedure (line 44) | MemoServiceUpdateMemoProcedure = "/memos.api.v1.MemoService/UpdateMemo"
constant MemoServiceDeleteMemoProcedure (line 46) | MemoServiceDeleteMemoProcedure = "/memos.api.v1.MemoService/DeleteMemo"
constant MemoServiceSetMemoAttachmentsProcedure (line 49) | MemoServiceSetMemoAttachmentsProcedure = "/memos.api.v1.MemoService/SetM...
constant MemoServiceListMemoAttachmentsProcedure (line 52) | MemoServiceListMemoAttachmentsProcedure = "/memos.api.v1.MemoService/Lis...
constant MemoServiceSetMemoRelationsProcedure (line 55) | MemoServiceSetMemoRelationsProcedure = "/memos.api.v1.MemoService/SetMem...
constant MemoServiceListMemoRelationsProcedure (line 58) | MemoServiceListMemoRelationsProcedure = "/memos.api.v1.MemoService/ListM...
constant MemoServiceCreateMemoCommentProcedure (line 61) | MemoServiceCreateMemoCommentProcedure = "/memos.api.v1.MemoService/Creat...
constant MemoServiceListMemoCommentsProcedure (line 64) | MemoServiceListMemoCommentsProcedure = "/memos.api.v1.MemoService/ListMe...
constant MemoServiceListMemoReactionsProcedure (line 67) | MemoServiceListMemoReactionsProcedure = "/memos.api.v1.MemoService/ListM...
constant MemoServiceUpsertMemoReactionProcedure (line 70) | MemoServiceUpsertMemoReactionProcedure = "/memos.api.v1.MemoService/Upse...
constant MemoServiceDeleteMemoReactionProcedure (line 73) | MemoServiceDeleteMemoReactionProcedure = "/memos.api.v1.MemoService/Dele...
constant MemoServiceCreateMemoShareProcedure (line 76) | MemoServiceCreateMemoShareProcedure = "/memos.api.v1.MemoService/CreateM...
constant MemoServiceListMemoSharesProcedure (line 79) | MemoServiceListMemoSharesProcedure = "/memos.api.v1.MemoService/ListMemo...
constant MemoServiceDeleteMemoShareProcedure (line 82) | MemoServiceDeleteMemoShareProcedure = "/memos.api.v1.MemoService/DeleteM...
constant MemoServiceGetMemoByShareProcedure (line 85) | MemoServiceGetMemoByShareProcedure = "/memos.api.v1.MemoService/GetMemoB...
type MemoServiceClient (line 89) | type MemoServiceClient interface
function NewMemoServiceClient (line 136) | func NewMemoServiceClient(httpClient connect.HTTPClient, baseURL string,...
type memoServiceClient (line 252) | type memoServiceClient struct
method CreateMemo (line 274) | func (c *memoServiceClient) CreateMemo(ctx context.Context, req *conne...
method ListMemos (line 279) | func (c *memoServiceClient) ListMemos(ctx context.Context, req *connec...
method GetMemo (line 284) | func (c *memoServiceClient) GetMemo(ctx context.Context, req *connect....
method UpdateMemo (line 289) | func (c *memoServiceClient) UpdateMemo(ctx context.Context, req *conne...
method DeleteMemo (line 294) | func (c *memoServiceClient) DeleteMemo(ctx context.Context, req *conne...
method SetMemoAttachments (line 299) | func (c *memoServiceClient) SetMemoAttachments(ctx context.Context, re...
method ListMemoAttachments (line 304) | func (c *memoServiceClient) ListMemoAttachments(ctx context.Context, r...
method SetMemoRelations (line 309) | func (c *memoServiceClient) SetMemoRelations(ctx context.Context, req ...
method ListMemoRelations (line 314) | func (c *memoServiceClient) ListMemoRelations(ctx context.Context, req...
method CreateMemoComment (line 319) | func (c *memoServiceClient) CreateMemoComment(ctx context.Context, req...
method ListMemoComments (line 324) | func (c *memoServiceClient) ListMemoComments(ctx context.Context, req ...
method ListMemoReactions (line 329) | func (c *memoServiceClient) ListMemoReactions(ctx context.Context, req...
method UpsertMemoReaction (line 334) | func (c *memoServiceClient) UpsertMemoReaction(ctx context.Context, re...
method DeleteMemoReaction (line 339) | func (c *memoServiceClient) DeleteMemoReaction(ctx context.Context, re...
method CreateMemoShare (line 344) | func (c *memoServiceClient) CreateMemoShare(ctx context.Context, req *...
method ListMemoShares (line 349) | func (c *memoServiceClient) ListMemoShares(ctx context.Context, req *c...
method DeleteMemoShare (line 354) | func (c *memoServiceClient) DeleteMemoShare(ctx context.Context, req *...
method GetMemoByShare (line 359) | func (c *memoServiceClient) GetMemoByShare(ctx context.Context, req *c...
type MemoServiceHandler (line 364) | type MemoServiceHandler interface
function NewMemoServiceHandler (line 409) | func NewMemoServiceHandler(svc MemoServiceHandler, opts ...connect.Handl...
type UnimplementedMemoServiceHandler (line 564) | type UnimplementedMemoServiceHandler struct
method CreateMemo (line 566) | func (UnimplementedMemoServiceHandler) CreateMemo(context.Context, *co...
method ListMemos (line 570) | func (UnimplementedMemoServiceHandler) ListMemos(context.Context, *con...
method GetMemo (line 574) | func (UnimplementedMemoServiceHandler) GetMemo(context.Context, *conne...
method UpdateMemo (line 578) | func (UnimplementedMemoServiceHandler) UpdateMemo(context.Context, *co...
method DeleteMemo (line 582) | func (UnimplementedMemoServiceHandler) DeleteMemo(context.Context, *co...
method SetMemoAttachments (line 586) | func (UnimplementedMemoServiceHandler) SetMemoAttachments(context.Cont...
method ListMemoAttachments (line 590) | func (UnimplementedMemoServiceHandler) ListMemoAttachments(context.Con...
method SetMemoRelations (line 594) | func (UnimplementedMemoServiceHandler) SetMemoRelations(context.Contex...
method ListMemoRelations (line 598) | func (UnimplementedMemoServiceHandler) ListMemoRelations(context.Conte...
method CreateMemoComment (line 602) | func (UnimplementedMemoServiceHandler) CreateMemoComment(context.Conte...
method ListMemoComments (line 606) | func (UnimplementedMemoServiceHandler) ListMemoComments(context.Contex...
method ListMemoReactions (line 610) | func (UnimplementedMemoServiceHandler) ListMemoReactions(context.Conte...
method UpsertMemoReaction (line 614) | func (UnimplementedMemoServiceHandler) UpsertMemoReaction(context.Cont...
method DeleteMemoReaction (line 618) | func (UnimplementedMemoServiceHandler) DeleteMemoReaction(context.Cont...
method CreateMemoShare (line 622) | func (UnimplementedMemoServiceHandler) CreateMemoShare(context.Context...
method ListMemoShares (line 626) | func (UnimplementedMemoServiceHandler) ListMemoShares(context.Context,...
method DeleteMemoShare (line 630) | func (UnimplementedMemoServiceHandler) DeleteMemoShare(context.Context...
method GetMemoByShare (line 634) | func (UnimplementedMemoServiceHandler) GetMemoByShare(context.Context,...
FILE: proto/gen/api/v1/apiv1connect/shortcut_service.connect.go
constant _ (line 22) | _ = connect.IsAtLeastVersion1_13_0
constant ShortcutServiceName (line 26) | ShortcutServiceName = "memos.api.v1.ShortcutService"
constant ShortcutServiceListShortcutsProcedure (line 39) | ShortcutServiceListShortcutsProcedure = "/memos.api.v1.ShortcutService/L...
constant ShortcutServiceGetShortcutProcedure (line 42) | ShortcutServiceGetShortcutProcedure = "/memos.api.v1.ShortcutService/Get...
constant ShortcutServiceCreateShortcutProcedure (line 45) | ShortcutServiceCreateShortcutProcedure = "/memos.api.v1.ShortcutService/...
constant ShortcutServiceUpdateShortcutProcedure (line 48) | ShortcutServiceUpdateShortcutProcedure = "/memos.api.v1.ShortcutService/...
constant ShortcutServiceDeleteShortcutProcedure (line 51) | ShortcutServiceDeleteShortcutProcedure = "/memos.api.v1.ShortcutService/...
type ShortcutServiceClient (line 55) | type ShortcutServiceClient interface
function NewShortcutServiceClient (line 75) | func NewShortcutServiceClient(httpClient connect.HTTPClient, baseURL str...
type shortcutServiceClient (line 113) | type shortcutServiceClient struct
method ListShortcuts (line 122) | func (c *shortcutServiceClient) ListShortcuts(ctx context.Context, req...
method GetShortcut (line 127) | func (c *shortcutServiceClient) GetShortcut(ctx context.Context, req *...
method CreateShortcut (line 132) | func (c *shortcutServiceClient) CreateShortcut(ctx context.Context, re...
method UpdateShortcut (line 137) | func (c *shortcutServiceClient) UpdateShortcut(ctx context.Context, re...
method DeleteShortcut (line 142) | func (c *shortcutServiceClient) DeleteShortcut(ctx context.Context, re...
type ShortcutServiceHandler (line 147) | type ShortcutServiceHandler interface
function NewShortcutServiceHandler (line 165) | func NewShortcutServiceHandler(svc ShortcutServiceHandler, opts ...conne...
type UnimplementedShortcutServiceHandler (line 216) | type UnimplementedShortcutServiceHandler struct
method ListShortcuts (line 218) | func (UnimplementedShortcutServiceHandler) ListShortcuts(context.Conte...
method GetShortcut (line 222) | func (UnimplementedShortcutServiceHandler) GetShortcut(context.Context...
method CreateShortcut (line 226) | func (UnimplementedShortcutServiceHandler) CreateShortcut(context.Cont...
method UpdateShortcut (line 230) | func (UnimplementedShortcutServiceHandler) UpdateShortcut(context.Cont...
method DeleteShortcut (line 234) | func (UnimplementedShortcutServiceHandler) DeleteShortcut(context.Cont...
FILE: proto/gen/api/v1/apiv1connect/user_service.connect.go
constant _ (line 22) | _ = connect.IsAtLeastVersion1_13_0
constant UserServiceName (line 26) | UserServiceName = "memos.api.v1.UserService"
constant UserServiceListUsersProcedure (line 38) | UserServiceListUsersProcedure = "/memos.api.v1.UserService/ListUsers"
constant UserServiceGetUserProcedure (line 40) | UserServiceGetUserProcedure = "/memos.api.v1.UserService/GetUser"
constant UserServiceCreateUserProcedure (line 42) | UserServiceCreateUserProcedure = "/memos.api.v1.UserService/CreateUser"
constant UserServiceUpdateUserProcedure (line 44) | UserServiceUpdateUserProcedure = "/memos.api.v1.UserService/UpdateUser"
constant UserServiceDeleteUserProcedure (line 46) | UserServiceDeleteUserProcedure = "/memos.api.v1.UserService/DeleteUser"
constant UserServiceListAllUserStatsProcedure (line 49) | UserServiceListAllUserStatsProcedure = "/memos.api.v1.UserService/ListAl...
constant UserServiceGetUserStatsProcedure (line 52) | UserServiceGetUserStatsProcedure = "/memos.api.v1.UserService/GetUserStats"
constant UserServiceGetUserSettingProcedure (line 55) | UserServiceGetUserSettingProcedure = "/memos.api.v1.UserService/GetUserS...
constant UserServiceUpdateUserSettingProcedure (line 58) | UserServiceUpdateUserSettingProcedure = "/memos.api.v1.UserService/Updat...
constant UserServiceListUserSettingsProcedure (line 61) | UserServiceListUserSettingsProcedure = "/memos.api.v1.UserService/ListUs...
constant UserServiceListPersonalAccessTokensProcedure (line 64) | UserServiceListPersonalAccessTokensProcedure = "/memos.api.v1.UserServic...
constant UserServiceCreatePersonalAccessTokenProcedure (line 67) | UserServiceCreatePersonalAccessTokenProcedure = "/memos.api.v1.UserServi...
constant UserServiceDeletePersonalAccessTokenProcedure (line 70) | UserServiceDeletePersonalAccessTokenProcedure = "/memos.api.v1.UserServi...
constant UserServiceListUserWebhooksProcedure (line 73) | UserServiceListUserWebhooksProcedure = "/memos.api.v1.UserService/ListUs...
constant UserServiceCreateUserWebhookProcedure (line 76) | UserServiceCreateUserWebhookProcedure = "/memos.api.v1.UserService/Creat...
constant UserServiceUpdateUserWebhookProcedure (line 79) | UserServiceUpdateUserWebhookProcedure = "/memos.api.v1.UserService/Updat...
constant UserServiceDeleteUserWebhookProcedure (line 82) | UserServiceDeleteUserWebhookProcedure = "/memos.api.v1.UserService/Delet...
constant UserServiceListUserNotificationsProcedure (line 85) | UserServiceListUserNotificationsProcedure = "/memos.api.v1.UserService/L...
constant UserServiceUpdateUserNotificationProcedure (line 88) | UserServiceUpdateUserNotificationProcedure = "/memos.api.v1.UserService/...
constant UserServiceDeleteUserNotificationProcedure (line 91) | UserServiceDeleteUserNotificationProcedure = "/memos.api.v1.UserService/...
type UserServiceClient (line 95) | type UserServiceClient interface
function NewUserServiceClient (line 150) | func NewUserServiceClient(httpClient connect.HTTPClient, baseURL string,...
type userServiceClient (line 278) | type userServiceClient struct
method ListUsers (line 302) | func (c *userServiceClient) ListUsers(ctx context.Context, req *connec...
method GetUser (line 307) | func (c *userServiceClient) GetUser(ctx context.Context, req *connect....
method CreateUser (line 312) | func (c *userServiceClient) CreateUser(ctx context.Context, req *conne...
method UpdateUser (line 317) | func (c *userServiceClient) UpdateUser(ctx context.Context, req *conne...
method DeleteUser (line 322) | func (c *userServiceClient) DeleteUser(ctx context.Context, req *conne...
method ListAllUserStats (line 327) | func (c *userServiceClient) ListAllUserStats(ctx context.Context, req ...
method GetUserStats (line 332) | func (c *userServiceClient) GetUserStats(ctx context.Context, req *con...
method GetUserSetting (line 337) | func (c *userServiceClient) GetUserSetting(ctx context.Context, req *c...
method UpdateUserSetting (line 342) | func (c *userServiceClient) UpdateUserSetting(ctx context.Context, req...
method ListUserSettings (line 347) | func (c *userServiceClient) ListUserSettings(ctx context.Context, req ...
method ListPersonalAccessTokens (line 352) | func (c *userServiceClient) ListPersonalAccessTokens(ctx context.Conte...
method CreatePersonalAccessToken (line 357) | func (c *userServiceClient) CreatePersonalAccessToken(ctx context.Cont...
method DeletePersonalAccessToken (line 362) | func (c *userServiceClient) DeletePersonalAccessToken(ctx context.Cont...
method ListUserWebhooks (line 367) | func (c *userServiceClient) ListUserWebhooks(ctx context.Context, req ...
method CreateUserWebhook (line 372) | func (c *userServiceClient) CreateUserWebhook(ctx context.Context, req...
method UpdateUserWebhook (line 377) | func (c *userServiceClient) UpdateUserWebhook(ctx context.Context, req...
method DeleteUserWebhook (line 382) | func (c *userServiceClient) DeleteUserWebhook(ctx context.Context, req...
method ListUserNotifications (line 387) | func (c *userServiceClient) ListUserNotifications(ctx context.Context,...
method UpdateUserNotification (line 392) | func (c *userServiceClient) UpdateUserNotification(ctx context.Context...
method DeleteUserNotification (line 397) | func (c *userServiceClient) DeleteUserNotification(ctx context.Context...
type UserServiceHandler (line 402) | type UserServiceHandler interface
function NewUserServiceHandler (line 455) | func NewUserServiceHandler(svc UserServiceHandler, opts ...connect.Handl...
type UnimplementedUserServiceHandler (line 626) | type UnimplementedUserServiceHandler struct
method ListUsers (line 628) | func (UnimplementedUserServiceHandler) ListUsers(context.Context, *con...
method GetUser (line 632) | func (UnimplementedUserServiceHandler) GetUser(context.Context, *conne...
method CreateUser (line 636) | func (UnimplementedUserServiceHandler) CreateUser(context.Context, *co...
method UpdateUser (line 640) | func (UnimplementedUserServiceHandler) UpdateUser(context.Context, *co...
method DeleteUser (line 644) | func (UnimplementedUserServiceHandler) DeleteUser(context.Context, *co...
method ListAllUserStats (line 648) | func (UnimplementedUserServiceHandler) ListAllUserStats(context.Contex...
method GetUserStats (line 652) | func (UnimplementedUserServiceHandler) GetUserStats(context.Context, *...
method GetUserSetting (line 656) | func (UnimplementedUserServiceHandler) GetUserSetting(context.Context,...
method UpdateUserSetting (line 660) | func (UnimplementedUserServiceHandler) UpdateUserSetting(context.Conte...
method ListUserSettings (line 664) | func (UnimplementedUserServiceHandler) ListUserSettings(context.Contex...
method ListPersonalAccessTokens (line 668) | func (UnimplementedUserServiceHandler) ListPersonalAccessTokens(contex...
method CreatePersonalAccessToken (line 672) | func (UnimplementedUserServiceHandler) CreatePersonalAccessToken(conte...
method DeletePersonalAccessToken (line 676) | func (UnimplementedUserServiceHandler) DeletePersonalAccessToken(conte...
method ListUserWebhooks (line 680) | func (UnimplementedUserServiceHandler) ListUserWebhooks(context.Contex...
method CreateUserWebhook (line 684) | func (UnimplementedUserServiceHandler) CreateUserWebhook(context.Conte...
method UpdateUserWebhook (line 688) | func (UnimplementedUserServiceHandler) UpdateUserWebhook(context.Conte...
method DeleteUserWebhook (line 692) | func (UnimplementedUserServiceHandler) DeleteUserWebhook(context.Conte...
method ListUserNotifications (line 696) | func (UnimplementedUserServiceHandler) ListUserNotifications(context.C...
method UpdateUserNotification (line 700) | func (UnimplementedUserServiceHandler) UpdateUserNotification(context....
method DeleteUserNotification (line 704) | func (UnimplementedUserServiceHandler) DeleteUserNotification(context....
FILE: proto/gen/api/v1/attachment_service.pb.go
constant _ (line 23) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
constant _ (line 25) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
type Attachment (line 28) | type Attachment struct
method Reset (line 52) | func (x *Attachment) Reset() {
method String (line 59) | func (x *Attachment) String() string {
method ProtoMessage (line 63) | func (*Attachment) ProtoMessage() {}
method ProtoReflect (line 65) | func (x *Attachment) ProtoReflect() protoreflect.Message {
method Descriptor (line 78) | func (*Attachment) Descriptor() ([]byte, []int) {
method GetName (line 82) | func (x *Attachment) GetName() string {
method GetCreateTime (line 89) | func (x *Attachment) GetCreateTime() *timestamppb.Timestamp {
method GetFilename (line 96) | func (x *Attachment) GetFilename() string {
method GetContent (line 103) | func (x *Attachment) GetContent() []byte {
method GetExternalLink (line 110) | func (x *Attachment) GetExternalLink() string {
method GetType (line 117) | func (x *Attachment) GetType() string {
method GetSize (line 124) | func (x *Attachment) GetSize() int64 {
method GetMemo (line 131) | func (x *Attachment) GetMemo() string {
type CreateAttachmentRequest (line 138) | type CreateAttachmentRequest struct
method Reset (line 149) | func (x *CreateAttachmentRequest) Reset() {
method String (line 156) | func (x *CreateAttachmentRequest) String() string {
method ProtoMessage (line 160) | func (*CreateAttachmentRequest) ProtoMessage() {}
method ProtoReflect (line 162) | func (x *CreateAttachmentRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 175) | func (*CreateAttachmentRequest) Descriptor() ([]byte, []int) {
method GetAttachment (line 179) | func (x *CreateAttachmentRequest) GetAttachment() *Attachment {
method GetAttachmentId (line 186) | func (x *CreateAttachmentRequest) GetAttachmentId() string {
type ListAttachmentsRequest (line 193) | type ListAttachmentsRequest struct
method Reset (line 215) | func (x *ListAttachmentsRequest) Reset() {
method String (line 222) | func (x *ListAttachmentsRequest) String() string {
method ProtoMessage (line 226) | func (*ListAttachmentsRequest) ProtoMessage() {}
method ProtoReflect (line 228) | func (x *ListAttachmentsRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 241) | func (*ListAttachmentsRequest) Descriptor() ([]byte, []int) {
method GetPageSize (line 245) | func (x *ListAttachmentsRequest) GetPageSize() int32 {
method GetPageToken (line 252) | func (x *ListAttachmentsRequest) GetPageToken() string {
method GetFilter (line 259) | func (x *ListAttachmentsRequest) GetFilter() string {
method GetOrderBy (line 266) | func (x *ListAttachmentsRequest) GetOrderBy() string {
type ListAttachmentsResponse (line 273) | type ListAttachmentsResponse struct
method Reset (line 286) | func (x *ListAttachmentsResponse) Reset() {
method String (line 293) | func (x *ListAttachmentsResponse) String() string {
method ProtoMessage (line 297) | func (*ListAttachmentsResponse) ProtoMessage() {}
method ProtoReflect (line 299) | func (x *ListAttachmentsResponse) ProtoReflect() protoreflect.Message {
method Descriptor (line 312) | func (*ListAttachmentsResponse) Descriptor() ([]byte, []int) {
method GetAttachments (line 316) | func (x *ListAttachmentsResponse) GetAttachments() []*Attachment {
method GetNextPageToken (line 323) | func (x *ListAttachmentsResponse) GetNextPageToken() string {
method GetTotalSize (line 330) | func (x *ListAttachmentsResponse) GetTotalSize() int32 {
type GetAttachmentRequest (line 337) | type GetAttachmentRequest struct
method Reset (line 346) | func (x *GetAttachmentRequest) Reset() {
method String (line 353) | func (x *GetAttachmentRequest) String() string {
method ProtoMessage (line 357) | func (*GetAttachmentRequest) ProtoMessage() {}
method ProtoReflect (line 359) | func (x *GetAttachmentRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 372) | func (*GetAttachmentRequest) Descriptor() ([]byte, []int) {
method GetName (line 376) | func (x *GetAttachmentRequest) GetName() string {
type UpdateAttachmentRequest (line 383) | type UpdateAttachmentRequest struct
method Reset (line 393) | func (x *UpdateAttachmentRequest) Reset() {
method String (line 400) | func (x *UpdateAttachmentRequest) String() string {
method ProtoMessage (line 404) | func (*UpdateAttachmentRequest) ProtoMessage() {}
method ProtoReflect (line 406) | func (x *UpdateAttachmentRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 419) | func (*UpdateAttachmentRequest) Descriptor() ([]byte, []int) {
method GetAttachment (line 423) | func (x *UpdateAttachmentRequest) GetAttachment() *Attachment {
method GetUpdateMask (line 430) | func (x *UpdateAttachmentRequest) GetUpdateMask() *fieldmaskpb.FieldMa...
type DeleteAttachmentRequest (line 437) | type DeleteAttachmentRequest struct
method Reset (line 446) | func (x *DeleteAttachmentRequest) Reset() {
method String (line 453) | func (x *DeleteAttachmentRequest) String() string {
method ProtoMessage (line 457) | func (*DeleteAttachmentRequest) ProtoMessage() {}
method ProtoReflect (line 459) | func (x *DeleteAttachmentRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 472) | func (*DeleteAttachmentRequest) Descriptor() ([]byte, []int) {
method GetName (line 476) | func (x *DeleteAttachmentRequest) GetName() string {
constant file_api_v1_attachment_service_proto_rawDesc (line 485) | file_api_v1_attachment_service_proto_rawDesc = "" +
function file_api_v1_attachment_service_proto_rawDescGZIP (line 546) | func file_api_v1_attachment_service_proto_rawDescGZIP() []byte {
function init (line 589) | func init() { file_api_v1_attachment_service_proto_init() }
function file_api_v1_attachment_service_proto_init (line 590) | func file_api_v1_attachment_service_proto_init() {
FILE: proto/gen/api/v1/attachment_service.pb.gw.go
function request_AttachmentService_CreateAttachment_0 (line 40) | func request_AttachmentService_CreateAttachment_0(ctx context.Context, m...
function local_request_AttachmentService_CreateAttachment_0 (line 61) | func local_request_AttachmentService_CreateAttachment_0(ctx context.Cont...
function request_AttachmentService_ListAttachments_0 (line 81) | func request_AttachmentService_ListAttachments_0(ctx context.Context, ma...
function local_request_AttachmentService_ListAttachments_0 (line 99) | func local_request_AttachmentService_ListAttachments_0(ctx context.Conte...
function request_AttachmentService_GetAttachment_0 (line 114) | func request_AttachmentService_GetAttachment_0(ctx context.Context, mars...
function local_request_AttachmentService_GetAttachment_0 (line 135) | func local_request_AttachmentService_GetAttachment_0(ctx context.Context...
function request_AttachmentService_UpdateAttachment_0 (line 155) | func request_AttachmentService_UpdateAttachment_0(ctx context.Context, m...
function local_request_AttachmentService_UpdateAttachment_0 (line 196) | func local_request_AttachmentService_UpdateAttachment_0(ctx context.Cont...
function request_AttachmentService_DeleteAttachment_0 (line 234) | func request_AttachmentService_DeleteAttachment_0(ctx context.Context, m...
function local_request_AttachmentService_DeleteAttachment_0 (line 255) | func local_request_AttachmentService_DeleteAttachment_0(ctx context.Cont...
function RegisterAttachmentServiceHandlerServer (line 278) | func RegisterAttachmentServiceHandlerServer(ctx context.Context, mux *ru...
function RegisterAttachmentServiceHandlerFromEndpoint (line 385) | func RegisterAttachmentServiceHandlerFromEndpoint(ctx context.Context, m...
function RegisterAttachmentServiceHandler (line 409) | func RegisterAttachmentServiceHandler(ctx context.Context, mux *runtime....
function RegisterAttachmentServiceHandlerClient (line 418) | func RegisterAttachmentServiceHandlerClient(ctx context.Context, mux *ru...
FILE: proto/gen/api/v1/attachment_service_grpc.pb.go
constant _ (line 20) | _ = grpc.SupportPackageIsVersion9
constant AttachmentService_CreateAttachment_FullMethodName (line 23) | AttachmentService_CreateAttachment_FullMethodName = "/memos.api.v1.Attac...
constant AttachmentService_ListAttachments_FullMethodName (line 24) | AttachmentService_ListAttachments_FullMethodName = "/memos.api.v1.Attac...
constant AttachmentService_GetAttachment_FullMethodName (line 25) | AttachmentService_GetAttachment_FullMethodName = "/memos.api.v1.Attac...
constant AttachmentService_UpdateAttachment_FullMethodName (line 26) | AttachmentService_UpdateAttachment_FullMethodName = "/memos.api.v1.Attac...
constant AttachmentService_DeleteAttachment_FullMethodName (line 27) | AttachmentService_DeleteAttachment_FullMethodName = "/memos.api.v1.Attac...
type AttachmentServiceClient (line 33) | type AttachmentServiceClient interface
type attachmentServiceClient (line 46) | type attachmentServiceClient struct
method CreateAttachment (line 54) | func (c *attachmentServiceClient) CreateAttachment(ctx context.Context...
method ListAttachments (line 64) | func (c *attachmentServiceClient) ListAttachments(ctx context.Context,...
method GetAttachment (line 74) | func (c *attachmentServiceClient) GetAttachment(ctx context.Context, i...
method UpdateAttachment (line 84) | func (c *attachmentServiceClient) UpdateAttachment(ctx context.Context...
method DeleteAttachment (line 94) | func (c *attachmentServiceClient) DeleteAttachment(ctx context.Context...
function NewAttachmentServiceClient (line 50) | func NewAttachmentServiceClient(cc grpc.ClientConnInterface) AttachmentS...
type AttachmentServiceServer (line 107) | type AttachmentServiceServer interface
type UnimplementedAttachmentServiceServer (line 126) | type UnimplementedAttachmentServiceServer struct
method CreateAttachment (line 128) | func (UnimplementedAttachmentServiceServer) CreateAttachment(context.C...
method ListAttachments (line 131) | func (UnimplementedAttachmentServiceServer) ListAttachments(context.Co...
method GetAttachment (line 134) | func (UnimplementedAttachmentServiceServer) GetAttachment(context.Cont...
method UpdateAttachment (line 137) | func (UnimplementedAttachmentServiceServer) UpdateAttachment(context.C...
method DeleteAttachment (line 140) | func (UnimplementedAttachmentServiceServer) DeleteAttachment(context.C...
method mustEmbedUnimplementedAttachmentServiceServer (line 143) | func (UnimplementedAttachmentServiceServer) mustEmbedUnimplementedAtta...
method testEmbeddedByValue (line 144) | func (UnimplementedAttachmentServiceServer) testEmbeddedByValue() ...
type UnsafeAttachmentServiceServer (line 149) | type UnsafeAttachmentServiceServer interface
function RegisterAttachmentServiceServer (line 153) | func RegisterAttachmentServiceServer(s grpc.ServiceRegistrar, srv Attach...
function _AttachmentService_CreateAttachment_Handler (line 164) | func _AttachmentService_CreateAttachment_Handler(srv interface{}, ctx co...
function _AttachmentService_ListAttachments_Handler (line 182) | func _AttachmentService_ListAttachments_Handler(srv interface{}, ctx con...
function _AttachmentService_GetAttachment_Handler (line 200) | func _AttachmentService_GetAttachment_Handler(srv interface{}, ctx conte...
function _AttachmentService_UpdateAttachment_Handler (line 218) | func _AttachmentService_UpdateAttachment_Handler(srv interface{}, ctx co...
function _AttachmentService_DeleteAttachment_Handler (line 236) | func _AttachmentService_DeleteAttachment_Handler(srv interface{}, ctx co...
FILE: proto/gen/api/v1/auth_service.pb.go
constant _ (line 22) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
constant _ (line 24) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
type GetCurrentUserRequest (line 27) | type GetCurrentUserRequest struct
method Reset (line 33) | func (x *GetCurrentUserRequest) Reset() {
method String (line 40) | func (x *GetCurrentUserRequest) String() string {
method ProtoMessage (line 44) | func (*GetCurrentUserRequest) ProtoMessage() {}
method ProtoReflect (line 46) | func (x *GetCurrentUserRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 59) | func (*GetCurrentUserRequest) Descriptor() ([]byte, []int) {
type GetCurrentUserResponse (line 63) | type GetCurrentUserResponse struct
method Reset (line 71) | func (x *GetCurrentUserResponse) Reset() {
method String (line 78) | func (x *GetCurrentUserResponse) String() string {
method ProtoMessage (line 82) | func (*GetCurrentUserResponse) ProtoMessage() {}
method ProtoReflect (line 84) | func (x *GetCurrentUserResponse) ProtoReflect() protoreflect.Message {
method Descriptor (line 97) | func (*GetCurrentUserResponse) Descriptor() ([]byte, []int) {
method GetUser (line 101) | func (x *GetCurrentUserResponse) GetUser() *User {
type SignInRequest (line 108) | type SignInRequest struct
method Reset (line 121) | func (x *SignInRequest) Reset() {
method String (line 128) | func (x *SignInRequest) String() string {
method ProtoMessage (line 132) | func (*SignInRequest) ProtoMessage() {}
method ProtoReflect (line 134) | func (x *SignInRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 147) | func (*SignInRequest) Descriptor() ([]byte, []int) {
method GetCredentials (line 151) | func (x *SignInRequest) GetCredentials() isSignInRequest_Credentials {
method GetPasswordCredentials (line 158) | func (x *SignInRequest) GetPasswordCredentials() *SignInRequest_Passwo...
method GetSsoCredentials (line 167) | func (x *SignInRequest) GetSsoCredentials() *SignInRequest_SSOCredenti...
type isSignInRequest_Credentials (line 176) | type isSignInRequest_Credentials interface
type SignInRequest_PasswordCredentials_ (line 180) | type SignInRequest_PasswordCredentials_ struct
method isSignInRequest_Credentials (line 190) | func (*SignInRequest_PasswordCredentials_) isSignInRequest_Credentials...
type SignInRequest_SsoCredentials (line 185) | type SignInRequest_SsoCredentials struct
method isSignInRequest_Credentials (line 192) | func (*SignInRequest_SsoCredentials) isSignInRequest_Credentials() {}
type SignInResponse (line 194) | type SignInResponse struct
method Reset (line 208) | func (x *SignInResponse) Reset() {
method String (line 215) | func (x *SignInResponse) String() string {
method ProtoMessage (line 219) | func (*SignInResponse) ProtoMessage() {}
method ProtoReflect (line 221) | func (x *SignInResponse) ProtoReflect() protoreflect.Message {
method Descriptor (line 234) | func (*SignInResponse) Descriptor() ([]byte, []int) {
method GetUser (line 238) | func (x *SignInResponse) GetUser() *User {
method GetAccessToken (line 245) | func (x *SignInResponse) GetAccessToken() string {
method GetAccessTokenExpiresAt (line 252) | func (x *SignInResponse) GetAccessTokenExpiresAt() *timestamppb.Timest...
type SignOutRequest (line 259) | type SignOutRequest struct
method Reset (line 265) | func (x *SignOutRequest) Reset() {
method String (line 272) | func (x *SignOutRequest) String() string {
method ProtoMessage (line 276) | func (*SignOutRequest) ProtoMessage() {}
method ProtoReflect (line 278) | func (x *SignOutRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 291) | func (*SignOutRequest) Descriptor() ([]byte, []int) {
type RefreshTokenRequest (line 295) | type RefreshTokenRequest struct
method Reset (line 301) | func (x *RefreshTokenRequest) Reset() {
method String (line 308) | func (x *RefreshTokenRequest) String() string {
method ProtoMessage (line 312) | func (*RefreshTokenRequest) ProtoMessage() {}
method ProtoReflect (line 314) | func (x *RefreshTokenRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 327) | func (*RefreshTokenRequest) Descriptor() ([]byte, []int) {
type RefreshTokenResponse (line 331) | type RefreshTokenResponse struct
method Reset (line 341) | func (x *RefreshTokenResponse) Reset() {
method String (line 348) | func (x *RefreshTokenResponse) String() string {
method ProtoMessage (line 352) | func (*RefreshTokenResponse) ProtoMessage() {}
method ProtoReflect (line 354) | func (x *RefreshTokenResponse) ProtoReflect() protoreflect.Message {
method Descriptor (line 367) | func (*RefreshTokenResponse) Descriptor() ([]byte, []int) {
method GetAccessToken (line 371) | func (x *RefreshTokenResponse) GetAccessToken() string {
method GetExpiresAt (line 378) | func (x *RefreshTokenResponse) GetExpiresAt() *timestamppb.Timestamp {
type SignInRequest_PasswordCredentials (line 386) | type SignInRequest_PasswordCredentials struct
method Reset (line 396) | func (x *SignInRequest_PasswordCredentials) Reset() {
method String (line 403) | func (x *SignInRequest_PasswordCredentials) String() string {
method ProtoMessage (line 407) | func (*SignInRequest_PasswordCredentials) ProtoMessage() {}
method ProtoReflect (line 409) | func (x *SignInRequest_PasswordCredentials) ProtoReflect() protoreflec...
method Descriptor (line 422) | func (*SignInRequest_PasswordCredentials) Descriptor() ([]byte, []int) {
method GetUsername (line 426) | func (x *SignInRequest_PasswordCredentials) GetUsername() string {
method GetPassword (line 433) | func (x *SignInRequest_PasswordCredentials) GetPassword() string {
type SignInRequest_SSOCredentials (line 441) | type SignInRequest_SSOCredentials struct
method Reset (line 457) | func (x *SignInRequest_SSOCredentials) Reset() {
method String (line 464) | func (x *SignInRequest_SSOCredentials) String() string {
method ProtoMessage (line 468) | func (*SignInRequest_SSOCredentials) ProtoMessage() {}
method ProtoReflect (line 470) | func (x *SignInRequest_SSOCredentials) ProtoReflect() protoreflect.Mes...
method Descriptor (line 483) | func (*SignInRequest_SSOCredentials) Descriptor() ([]byte, []int) {
method GetIdpName (line 487) | func (x *SignInRequest_SSOCredentials) GetIdpName() string {
method GetCode (line 494) | func (x *SignInRequest_SSOCredentials) GetCode() string {
method GetRedirectUri (line 501) | func (x *SignInRequest_SSOCredentials) GetRedirectUri() string {
method GetCodeVerifier (line 508) | func (x *SignInRequest_SSOCredentials) GetCodeVerifier() string {
constant file_api_v1_auth_service_proto_rawDesc (line 517) | file_api_v1_auth_service_proto_rawDesc = "" +
function file_api_v1_auth_service_proto_rawDescGZIP (line 557) | func file_api_v1_auth_service_proto_rawDescGZIP() []byte {
function init (line 601) | func init() { file_api_v1_auth_service_proto_init() }
function file_api_v1_auth_service_proto_init (line 602) | func file_api_v1_auth_service_proto_init() {
FILE: proto/gen/api/v1/auth_service.pb.gw.go
function request_AuthService_GetCurrentUser_0 (line 38) | func request_AuthService_GetCurrentUser_0(ctx context.Context, marshaler...
function local_request_AuthService_GetCurrentUser_0 (line 50) | func local_request_AuthService_GetCurrentUser_0(ctx context.Context, mar...
function request_AuthService_SignIn_0 (line 59) | func request_AuthService_SignIn_0(ctx context.Context, marshaler runtime...
function local_request_AuthService_SignIn_0 (line 74) | func local_request_AuthService_SignIn_0(ctx context.Context, marshaler r...
function request_AuthService_SignOut_0 (line 86) | func request_AuthService_SignOut_0(ctx context.Context, marshaler runtim...
function local_request_AuthService_SignOut_0 (line 98) | func local_request_AuthService_SignOut_0(ctx context.Context, marshaler ...
function request_AuthService_RefreshToken_0 (line 107) | func request_AuthService_RefreshToken_0(ctx context.Context, marshaler r...
function local_request_AuthService_RefreshToken_0 (line 122) | func local_request_AuthService_RefreshToken_0(ctx context.Context, marsh...
function RegisterAuthServiceHandlerServer (line 139) | func RegisterAuthServiceHandlerServer(ctx context.Context, mux *runtime....
function RegisterAuthServiceHandlerFromEndpoint (line 226) | func RegisterAuthServiceHandlerFromEndpoint(ctx context.Context, mux *ru...
function RegisterAuthServiceHandler (line 250) | func RegisterAuthServiceHandler(ctx context.Context, mux *runtime.ServeM...
function RegisterAuthServiceHandlerClient (line 259) | func RegisterAuthServiceHandlerClient(ctx context.Context, mux *runtime....
FILE: proto/gen/api/v1/auth_service_grpc.pb.go
constant _ (line 20) | _ = grpc.SupportPackageIsVersion9
constant AuthService_GetCurrentUser_FullMethodName (line 23) | AuthService_GetCurrentUser_FullMethodName = "/memos.api.v1.AuthService/G...
constant AuthService_SignIn_FullMethodName (line 24) | AuthService_SignIn_FullMethodName = "/memos.api.v1.AuthService/S...
constant AuthService_SignOut_FullMethodName (line 25) | AuthService_SignOut_FullMethodName = "/memos.api.v1.AuthService/S...
constant AuthService_RefreshToken_FullMethodName (line 26) | AuthService_RefreshToken_FullMethodName = "/memos.api.v1.AuthService/R...
type AuthServiceClient (line 32) | type AuthServiceClient interface
type authServiceClient (line 50) | type authServiceClient struct
method GetCurrentUser (line 58) | func (c *authServiceClient) GetCurrentUser(ctx context.Context, in *Ge...
method SignIn (line 68) | func (c *authServiceClient) SignIn(ctx context.Context, in *SignInRequ...
method SignOut (line 78) | func (c *authServiceClient) SignOut(ctx context.Context, in *SignOutRe...
method RefreshToken (line 88) | func (c *authServiceClient) RefreshToken(ctx context.Context, in *Refr...
function NewAuthServiceClient (line 54) | func NewAuthServiceClient(cc grpc.ClientConnInterface) AuthServiceClient {
type AuthServiceServer (line 101) | type AuthServiceServer interface
type UnimplementedAuthServiceServer (line 125) | type UnimplementedAuthServiceServer struct
method GetCurrentUser (line 127) | func (UnimplementedAuthServiceServer) GetCurrentUser(context.Context, ...
method SignIn (line 130) | func (UnimplementedAuthServiceServer) SignIn(context.Context, *SignInR...
method SignOut (line 133) | func (UnimplementedAuthServiceServer) SignOut(context.Context, *SignOu...
method RefreshToken (line 136) | func (UnimplementedAuthServiceServer) RefreshToken(context.Context, *R...
method mustEmbedUnimplementedAuthServiceServer (line 139) | func (UnimplementedAuthServiceServer) mustEmbedUnimplementedAuthServic...
method testEmbeddedByValue (line 140) | func (UnimplementedAuthServiceServer) testEmbeddedByValue() ...
type UnsafeAuthServiceServer (line 145) | type UnsafeAuthServiceServer interface
function RegisterAuthServiceServer (line 149) | func RegisterAuthServiceServer(s grpc.ServiceRegistrar, srv AuthServiceS...
function _AuthService_GetCurrentUser_Handler (line 160) | func _AuthService_GetCurrentUser_Handler(srv interface{}, ctx context.Co...
function _AuthService_SignIn_Handler (line 178) | func _AuthService_SignIn_Handler(srv interface{}, ctx context.Context, d...
function _AuthService_SignOut_Handler (line 196) | func _AuthService_SignOut_Handler(srv interface{}, ctx context.Context, ...
function _AuthService_RefreshToken_Handler (line 214) | func _AuthService_RefreshToken_Handler(srv interface{}, ctx context.Cont...
FILE: proto/gen/api/v1/common.pb.go
constant _ (line 19) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
constant _ (line 21) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
type State (line 24) | type State
method Enum (line 46) | func (x State) Enum() *State {
method String (line 52) | func (x State) String() string {
method Descriptor (line 56) | func (State) Descriptor() protoreflect.EnumDescriptor {
method Type (line 60) | func (State) Type() protoreflect.EnumType {
method Number (line 64) | func (x State) Number() protoreflect.EnumNumber {
method EnumDescriptor (line 69) | func (State) EnumDescriptor() ([]byte, []int) {
constant State_STATE_UNSPECIFIED (line 27) | State_STATE_UNSPECIFIED State = 0
constant State_NORMAL (line 28) | State_NORMAL State = 1
constant State_ARCHIVED (line 29) | State_ARCHIVED State = 2
type Direction (line 73) | type Direction
method Enum (line 95) | func (x Direction) Enum() *Direction {
method String (line 101) | func (x Direction) String() string {
method Descriptor (line 105) | func (Direction) Descriptor() protoreflect.EnumDescriptor {
method Type (line 109) | func (Direction) Type() protoreflect.EnumType {
method Number (line 113) | func (x Direction) Number() protoreflect.EnumNumber {
method EnumDescriptor (line 118) | func (Direction) EnumDescriptor() ([]byte, []int) {
constant Direction_DIRECTION_UNSPECIFIED (line 76) | Direction_DIRECTION_UNSPECIFIED Direction = 0
constant Direction_ASC (line 77) | Direction_ASC Direction = 1
constant Direction_DESC (line 78) | Direction_DESC Direction = 2
type PageToken (line 123) | type PageToken struct
method Reset (line 131) | func (x *PageToken) Reset() {
method String (line 138) | func (x *PageToken) String() string {
method ProtoMessage (line 142) | func (*PageToken) ProtoMessage() {}
method ProtoReflect (line 144) | func (x *PageToken) ProtoReflect() protoreflect.Message {
method Descriptor (line 157) | func (*PageToken) Descriptor() ([]byte, []int) {
method GetLimit (line 161) | func (x *PageToken) GetLimit() int32 {
method GetOffset (line 168) | func (x *PageToken) GetOffset() int32 {
constant file_api_v1_common_proto_rawDesc (line 177) | file_api_v1_common_proto_rawDesc = "" +
function file_api_v1_common_proto_rawDescGZIP (line 199) | func file_api_v1_common_proto_rawDescGZIP() []byte {
function init (line 221) | func init() { file_api_v1_common_proto_init() }
function file_api_v1_common_proto_init (line 222) | func file_api_v1_common_proto_init() {
FILE: proto/gen/api/v1/idp_service.pb.go
constant _ (line 22) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
constant _ (line 24) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
type IdentityProvider_Type (line 27) | type IdentityProvider_Type
method Enum (line 47) | func (x IdentityProvider_Type) Enum() *IdentityProvider_Type {
method String (line 53) | func (x IdentityProvider_Type) String() string {
method Descriptor (line 57) | func (IdentityProvider_Type) Descriptor() protoreflect.EnumDescriptor {
method Type (line 61) | func (IdentityProvider_Type) Type() protoreflect.EnumType {
method Number (line 65) | func (x IdentityProvider_Type) Number() protoreflect.EnumNumber {
method EnumDescriptor (line 70) | func (IdentityProvider_Type) EnumDescriptor() ([]byte, []int) {
constant IdentityProvider_TYPE_UNSPECIFIED (line 30) | IdentityProvider_TYPE_UNSPECIFIED IdentityProvider_Type = 0
constant IdentityProvider_OAUTH2 (line 32) | IdentityProvider_OAUTH2 IdentityProvider_Type = 1
type IdentityProvider (line 74) | type IdentityProvider struct
method Reset (line 91) | func (x *IdentityProvider) Reset() {
method String (line 98) | func (x *IdentityProvider) String() string {
method ProtoMessage (line 102) | func (*IdentityProvider) ProtoMessage() {}
method ProtoReflect (line 104) | func (x *IdentityProvider) ProtoReflect() protoreflect.Message {
method Descriptor (line 117) | func (*IdentityProvider) Descriptor() ([]byte, []int) {
method GetName (line 121) | func (x *IdentityProvider) GetName() string {
method GetType (line 128) | func (x *IdentityProvider) GetType() IdentityProvider_Type {
method GetTitle (line 135) | func (x *IdentityProvider) GetTitle() string {
method GetIdentifierFilter (line 142) | func (x *IdentityProvider) GetIdentifierFilter() string {
method GetConfig (line 149) | func (x *IdentityProvider) GetConfig() *IdentityProviderConfig {
type IdentityProviderConfig (line 156) | type IdentityProviderConfig struct
method Reset (line 166) | func (x *IdentityProviderConfig) Reset() {
method String (line 173) | func (x *IdentityProviderConfig) String() string {
method ProtoMessage (line 177) | func (*IdentityProviderConfig) ProtoMessage() {}
method ProtoReflect (line 179) | func (x *IdentityProviderConfig) ProtoReflect() protoreflect.Message {
method Descriptor (line 192) | func (*IdentityProviderConfig) Descriptor() ([]byte, []int) {
method GetConfig (line 196) | func (x *IdentityProviderConfig) GetConfig() isIdentityProviderConfig_...
method GetOauth2Config (line 203) | func (x *IdentityProviderConfig) GetOauth2Config() *OAuth2Config {
type isIdentityProviderConfig_Config (line 212) | type isIdentityProviderConfig_Config interface
type IdentityProviderConfig_Oauth2Config (line 216) | type IdentityProviderConfig_Oauth2Config struct
method isIdentityProviderConfig_Config (line 220) | func (*IdentityProviderConfig_Oauth2Config) isIdentityProviderConfig_C...
type FieldMapping (line 222) | type FieldMapping struct
method Reset (line 232) | func (x *FieldMapping) Reset() {
method String (line 239) | func (x *FieldMapping) String() string {
method ProtoMessage (line 243) | func (*FieldMapping) ProtoMessage() {}
method ProtoReflect (line 245) | func (x *FieldMapping) ProtoReflect() protoreflect.Message {
method Descriptor (line 258) | func (*FieldMapping) Descriptor() ([]byte, []int) {
method GetIdentifier (line 262) | func (x *FieldMapping) GetIdentifier() string {
method GetDisplayName (line 269) | func (x *FieldMapping) GetDisplayName() string {
method GetEmail (line 276) | func (x *FieldMapping) GetEmail() string {
method GetAvatarUrl (line 283) | func (x *FieldMapping) GetAvatarUrl() string {
type OAuth2Config (line 290) | type OAuth2Config struct
method Reset (line 303) | func (x *OAuth2Config) Reset() {
method String (line 310) | func (x *OAuth2Config) String() string {
method ProtoMessage (line 314) | func (*OAuth2Config) ProtoMessage() {}
method ProtoReflect (line 316) | func (x *OAuth2Config) ProtoReflect() protoreflect.Message {
method Descriptor (line 329) | func (*OAuth2Config) Descriptor() ([]byte, []int) {
method GetClientId (line 333) | func (x *OAuth2Config) GetClientId() string {
method GetClientSecret (line 340) | func (x *OAuth2Config) GetClientSecret() string {
method GetAuthUrl (line 347) | func (x *OAuth2Config) GetAuthUrl() string {
method GetTokenUrl (line 354) | func (x *OAuth2Config) GetTokenUrl() string {
method GetUserInfoUrl (line 361) | func (x *OAuth2Config) GetUserInfoUrl() string {
method GetScopes (line 368) | func (x *OAuth2Config) GetScopes() []string {
method GetFieldMapping (line 375) | func (x *OAuth2Config) GetFieldMapping() *FieldMapping {
type ListIdentityProvidersRequest (line 382) | type ListIdentityProvidersRequest struct
method Reset (line 388) | func (x *ListIdentityProvidersRequest) Reset() {
method String (line 395) | func (x *ListIdentityProvidersRequest) String() string {
method ProtoMessage (line 399) | func (*ListIdentityProvidersRequest) ProtoMessage() {}
method ProtoReflect (line 401) | func (x *ListIdentityProvidersRequest) ProtoReflect() protoreflect.Mes...
method Descriptor (line 414) | func (*ListIdentityProvidersRequest) Descriptor() ([]byte, []int) {
type ListIdentityProvidersResponse (line 418) | type ListIdentityProvidersResponse struct
method Reset (line 426) | func (x *ListIdentityProvidersResponse) Reset() {
method String (line 433) | func (x *ListIdentityProvidersResponse) String() string {
method ProtoMessage (line 437) | func (*ListIdentityProvidersResponse) ProtoMessage() {}
method ProtoReflect (line 439) | func (x *ListIdentityProvidersResponse) ProtoReflect() protoreflect.Me...
method Descriptor (line 452) | func (*ListIdentityProvidersResponse) Descriptor() ([]byte, []int) {
method GetIdentityProviders (line 456) | func (x *ListIdentityProvidersResponse) GetIdentityProviders() []*Iden...
type GetIdentityProviderRequest (line 463) | type GetIdentityProviderRequest struct
method Reset (line 472) | func (x *GetIdentityProviderRequest) Reset() {
method String (line 479) | func (x *GetIdentityProviderRequest) String() string {
method ProtoMessage (line 483) | func (*GetIdentityProviderRequest) ProtoMessage() {}
method ProtoReflect (line 485) | func (x *GetIdentityProviderRequest) ProtoReflect() protoreflect.Messa...
method Descriptor (line 498) | func (*GetIdentityProviderRequest) Descriptor() ([]byte, []int) {
method GetName (line 502) | func (x *GetIdentityProviderRequest) GetName() string {
type CreateIdentityProviderRequest (line 509) | type CreateIdentityProviderRequest struct
method Reset (line 520) | func (x *CreateIdentityProviderRequest) Reset() {
method String (line 527) | func (x *CreateIdentityProviderRequest) String() string {
method ProtoMessage (line 531) | func (*CreateIdentityProviderRequest) ProtoMessage() {}
method ProtoReflect (line 533) | func (x *CreateIdentityProviderRequest) ProtoReflect() protoreflect.Me...
method Descriptor (line 546) | func (*CreateIdentityProviderRequest) Descriptor() ([]byte, []int) {
method GetIdentityProvider (line 550) | func (x *CreateIdentityProviderRequest) GetIdentityProvider() *Identit...
method GetIdentityProviderId (line 557) | func (x *CreateIdentityProviderRequest) GetIdentityProviderId() string {
type UpdateIdentityProviderRequest (line 564) | type UpdateIdentityProviderRequest struct
method Reset (line 575) | func (x *UpdateIdentityProviderRequest) Reset() {
method String (line 582) | func (x *UpdateIdentityProviderRequest) String() string {
method ProtoMessage (line 586) | func (*UpdateIdentityProviderRequest) ProtoMessage() {}
method ProtoReflect (line 588) | func (x *UpdateIdentityProviderRequest) ProtoReflect() protoreflect.Me...
method Descriptor (line 601) | func (*UpdateIdentityProviderRequest) Descriptor() ([]byte, []int) {
method GetIdentityProvider (line 605) | func (x *UpdateIdentityProviderRequest) GetIdentityProvider() *Identit...
method GetUpdateMask (line 612) | func (x *UpdateIdentityProviderRequest) GetUpdateMask() *fieldmaskpb.F...
type DeleteIdentityProviderRequest (line 619) | type DeleteIdentityProviderRequest struct
method Reset (line 628) | func (x *DeleteIdentityProviderRequest) Reset() {
method String (line 635) | func (x *DeleteIdentityProviderRequest) String() string {
method ProtoMessage (line 639) | func (*DeleteIdentityProviderRequest) ProtoMessage() {}
method ProtoReflect (line 641) | func (x *DeleteIdentityProviderRequest) ProtoReflect() protoreflect.Me...
method Descriptor (line 654) | func (*DeleteIdentityProviderRequest) Descriptor() ([]byte, []int) {
method GetName (line 658) | func (x *DeleteIdentityProviderRequest) GetName() string {
constant file_api_v1_idp_service_proto_rawDesc (line 667) | file_api_v1_idp_service_proto_rawDesc = "" +
function file_api_v1_idp_service_proto_rawDescGZIP (line 729) | func file_api_v1_idp_service_proto_rawDescGZIP() []byte {
function init (line 779) | func init() { file_api_v1_idp_service_proto_init() }
function file_api_v1_idp_service_proto_init (line 780) | func file_api_v1_idp_service_proto_init() {
FILE: proto/gen/api/v1/idp_service.pb.gw.go
function request_IdentityProviderService_ListIdentityProviders_0 (line 38) | func request_IdentityProviderService_ListIdentityProviders_0(ctx context...
function local_request_IdentityProviderService_ListIdentityProviders_0 (line 50) | func local_request_IdentityProviderService_ListIdentityProviders_0(ctx c...
function request_IdentityProviderService_GetIdentityProvider_0 (line 59) | func request_IdentityProviderService_GetIdentityProvider_0(ctx context.C...
function local_request_IdentityProviderService_GetIdentityProvider_0 (line 80) | func local_request_IdentityProviderService_GetIdentityProvider_0(ctx con...
function request_IdentityProviderService_CreateIdentityProvider_0 (line 100) | func request_IdentityProviderService_CreateIdentityProvider_0(ctx contex...
function local_request_IdentityProviderService_CreateIdentityProvider_0 (line 121) | func local_request_IdentityProviderService_CreateIdentityProvider_0(ctx ...
function request_IdentityProviderService_UpdateIdentityProvider_0 (line 141) | func request_IdentityProviderService_UpdateIdentityProvider_0(ctx contex...
function local_request_IdentityProviderService_UpdateIdentityProvider_0 (line 182) | func local_request_IdentityProviderService_UpdateIdentityProvider_0(ctx ...
function request_IdentityProviderService_DeleteIdentityProvider_0 (line 220) | func request_IdentityProviderService_DeleteIdentityProvider_0(ctx contex...
function local_request_IdentityProviderService_DeleteIdentityProvider_0 (line 241) | func local_request_IdentityProviderService_DeleteIdentityProvider_0(ctx ...
function RegisterIdentityProviderServiceHandlerServer (line 264) | func RegisterIdentityProviderServiceHandlerServer(ctx context.Context, m...
function RegisterIdentityProviderServiceHandlerFromEndpoint (line 371) | func RegisterIdentityProviderServiceHandlerFromEndpoint(ctx context.Cont...
function RegisterIdentityProviderServiceHandler (line 395) | func RegisterIdentityProviderServiceHandler(ctx context.Context, mux *ru...
function RegisterIdentityProviderServiceHandlerClient (line 404) | func RegisterIdentityProviderServiceHandlerClient(ctx context.Context, m...
FILE: proto/gen/api/v1/idp_service_grpc.pb.go
constant _ (line 20) | _ = grpc.SupportPackageIsVersion9
constant IdentityProviderService_ListIdentityProviders_FullMethodName (line 23) | IdentityProviderService_ListIdentityProviders_FullMethodName = "/memos....
constant IdentityProviderService_GetIdentityProvider_FullMethodName (line 24) | IdentityProviderService_GetIdentityProvider_FullMethodName = "/memos....
constant IdentityProviderService_CreateIdentityProvider_FullMethodName (line 25) | IdentityProviderService_CreateIdentityProvider_FullMethodName = "/memos....
constant IdentityProviderService_UpdateIdentityProvider_FullMethodName (line 26) | IdentityProviderService_UpdateIdentityProvider_FullMethodName = "/memos....
constant IdentityProviderService_DeleteIdentityProvider_FullMethodName (line 27) | IdentityProviderService_DeleteIdentityProvider_FullMethodName = "/memos....
type IdentityProviderServiceClient (line 33) | type IdentityProviderServiceClient interface
type identityProviderServiceClient (line 46) | type identityProviderServiceClient struct
method ListIdentityProviders (line 54) | func (c *identityProviderServiceClient) ListIdentityProviders(ctx cont...
method GetIdentityProvider (line 64) | func (c *identityProviderServiceClient) GetIdentityProvider(ctx contex...
method CreateIdentityProvider (line 74) | func (c *identityProviderServiceClient) CreateIdentityProvider(ctx con...
method UpdateIdentityProvider (line 84) | func (c *identityProviderServiceClient) UpdateIdentityProvider(ctx con...
method DeleteIdentityProvider (line 94) | func (c *identityProviderServiceClient) DeleteIdentityProvider(ctx con...
function NewIdentityProviderServiceClient (line 50) | func NewIdentityProviderServiceClient(cc grpc.ClientConnInterface) Ident...
type IdentityProviderServiceServer (line 107) | type IdentityProviderServiceServer interface
type UnimplementedIdentityProviderServiceServer (line 126) | type UnimplementedIdentityProviderServiceServer struct
method ListIdentityProviders (line 128) | func (UnimplementedIdentityProviderServiceServer) ListIdentityProvider...
method GetIdentityProvider (line 131) | func (UnimplementedIdentityProviderServiceServer) GetIdentityProvider(...
method CreateIdentityProvider (line 134) | func (UnimplementedIdentityProviderServiceServer) CreateIdentityProvid...
method UpdateIdentityProvider (line 137) | func (UnimplementedIdentityProviderServiceServer) UpdateIdentityProvid...
method DeleteIdentityProvider (line 140) | func (UnimplementedIdentityProviderServiceServer) DeleteIdentityProvid...
method mustEmbedUnimplementedIdentityProviderServiceServer (line 143) | func (UnimplementedIdentityProviderServiceServer) mustEmbedUnimplement...
method testEmbeddedByValue (line 145) | func (UnimplementedIdentityProviderServiceServer) testEmbeddedByValue(...
type UnsafeIdentityProviderServiceServer (line 150) | type UnsafeIdentityProviderServiceServer interface
function RegisterIdentityProviderServiceServer (line 154) | func RegisterIdentityProviderServiceServer(s grpc.ServiceRegistrar, srv ...
function _IdentityProviderService_ListIdentityProviders_Handler (line 165) | func _IdentityProviderService_ListIdentityProviders_Handler(srv interfac...
function _IdentityProviderService_GetIdentityProvider_Handler (line 183) | func _IdentityProviderService_GetIdentityProvider_Handler(srv interface{...
function _IdentityProviderService_CreateIdentityProvider_Handler (line 201) | func _IdentityProviderService_CreateIdentityProvider_Handler(srv interfa...
function _IdentityProviderService_UpdateIdentityProvider_Handler (line 219) | func _IdentityProviderService_UpdateIdentityProvider_Handler(srv interfa...
function _IdentityProviderService_DeleteIdentityProvider_Handler (line 237) | func _IdentityProviderService_DeleteIdentityProvider_Handler(srv interfa...
FILE: proto/gen/api/v1/instance_service.pb.go
constant _ (line 22) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
constant _ (line 24) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
type InstanceSetting_Key (line 28) | type InstanceSetting_Key
method Enum (line 64) | func (x InstanceSetting_Key) Enum() *InstanceSetting_Key {
method String (line 70) | func (x InstanceSetting_Key) String() string {
method Descriptor (line 74) | func (InstanceSetting_Key) Descriptor() protoreflect.EnumDescriptor {
method Type (line 78) | func (InstanceSetting_Key) Type() protoreflect.EnumType {
method Number (line 82) | func (x InstanceSetting_Key) Number() protoreflect.EnumNumber {
method EnumDescriptor (line 87) | func (InstanceSetting_Key) EnumDescriptor() ([]byte, []int) {
constant InstanceSetting_KEY_UNSPECIFIED (line 31) | InstanceSetting_KEY_UNSPECIFIED InstanceSetting_Key = 0
constant InstanceSetting_GENERAL (line 33) | InstanceSetting_GENERAL InstanceSetting_Key = 1
constant InstanceSetting_STORAGE (line 35) | InstanceSetting_STORAGE InstanceSetting_Key = 2
constant InstanceSetting_MEMO_RELATED (line 37) | InstanceSetting_MEMO_RELATED InstanceSetting_Key = 3
constant InstanceSetting_TAGS (line 39) | InstanceSetting_TAGS InstanceSetting_Key = 4
constant InstanceSetting_NOTIFICATION (line 41) | InstanceSetting_NOTIFICATION InstanceSetting_Key = 5
type InstanceSetting_StorageSetting_StorageType (line 92) | type InstanceSetting_StorageSetting_StorageType
method Enum (line 120) | func (x InstanceSetting_StorageSetting_StorageType) Enum() *InstanceSe...
method String (line 126) | func (x InstanceSetting_StorageSetting_StorageType) String() string {
method Descriptor (line 130) | func (InstanceSetting_StorageSetting_StorageType) Descriptor() protore...
method Type (line 134) | func (InstanceSetting_StorageSetting_StorageType) Type() protoreflect....
method Number (line 138) | func (x InstanceSetting_StorageSetting_StorageType) Number() protorefl...
method EnumDescriptor (line 143) | func (InstanceSetting_StorageSetting_StorageType) EnumDescriptor() ([]...
constant InstanceSetting_StorageSetting_STORAGE_TYPE_UNSPECIFIED (line 95) | InstanceSetting_StorageSetting_STORAGE_TYPE_UNSPECIFIED InstanceSetting_...
constant InstanceSetting_StorageSetting_DATABASE (line 97) | InstanceSetting_StorageSetting_DATABASE InstanceSetting_StorageSetting_S...
constant InstanceSetting_StorageSetting_LOCAL (line 99) | InstanceSetting_StorageSetting_LOCAL InstanceSetting_StorageSetting_Stor...
constant InstanceSetting_StorageSetting_S3 (line 101) | InstanceSetting_StorageSetting_S3 InstanceSetting_StorageSetting_Storage...
type InstanceProfile (line 148) | type InstanceProfile struct
method Reset (line 163) | func (x *InstanceProfile) Reset() {
method String (line 170) | func (x *InstanceProfile) String() string {
method ProtoMessage (line 174) | func (*InstanceProfile) ProtoMessage() {}
method ProtoReflect (line 176) | func (x *InstanceProfile) ProtoReflect() protoreflect.Message {
method Descriptor (line 189) | func (*InstanceProfile) Descriptor() ([]byte, []int) {
method GetVersion (line 193) | func (x *InstanceProfile) GetVersion() string {
method GetDemo (line 200) | func (x *InstanceProfile) GetDemo() bool {
method GetInstanceUrl (line 207) | func (x *InstanceProfile) GetInstanceUrl() string {
method GetAdmin (line 214) | func (x *InstanceProfile) GetAdmin() *User {
type GetInstanceProfileRequest (line 222) | type GetInstanceProfileRequest struct
method Reset (line 228) | func (x *GetInstanceProfileRequest) Reset() {
method String (line 235) | func (x *GetInstanceProfileRequest) String() string {
method ProtoMessage (line 239) | func (*GetInstanceProfileRequest) ProtoMessage() {}
method ProtoReflect (line 241) | func (x *GetInstanceProfileRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 254) | func (*GetInstanceProfileRequest) Descriptor() ([]byte, []int) {
type InstanceSetting (line 259) | type InstanceSetting struct
method Reset (line 276) | func (x *InstanceSetting) Reset() {
method String (line 283) | func (x *InstanceSetting) String() string {
method ProtoMessage (line 287) | func (*InstanceSetting) ProtoMessage() {}
method ProtoReflect (line 289) | func (x *InstanceSetting) ProtoReflect() protoreflect.Message {
method Descriptor (line 302) | func (*InstanceSetting) Descriptor() ([]byte, []int) {
method GetName (line 306) | func (x *InstanceSetting) GetName() string {
method GetValue (line 313) | func (x *InstanceSetting) GetValue() isInstanceSetting_Value {
method GetGeneralSetting (line 320) | func (x *InstanceSetting) GetGeneralSetting() *InstanceSetting_General...
method GetStorageSetting (line 329) | func (x *InstanceSetting) GetStorageSetting() *InstanceSetting_Storage...
method GetMemoRelatedSetting (line 338) | func (x *InstanceSetting) GetMemoRelatedSetting() *InstanceSetting_Mem...
method GetTagsSetting (line 347) | func (x *InstanceSetting) GetTagsSetting() *InstanceSetting_TagsSetting {
method GetNotificationSetting (line 356) | func (x *InstanceSetting) GetNotificationSetting() *InstanceSetting_No...
type isInstanceSetting_Value (line 365) | type isInstanceSetting_Value interface
type InstanceSetting_GeneralSetting_ (line 369) | type InstanceSetting_GeneralSetting_ struct
method isInstanceSetting_Value (line 389) | func (*InstanceSetting_GeneralSetting_) isInstanceSetting_Value() {}
type InstanceSetting_StorageSetting_ (line 373) | type InstanceSetting_StorageSetting_ struct
method isInstanceSetting_Value (line 391) | func (*InstanceSetting_StorageSetting_) isInstanceSetting_Value() {}
type InstanceSetting_MemoRelatedSetting_ (line 377) | type InstanceSetting_MemoRelatedSetting_ struct
method isInstanceSetting_Value (line 393) | func (*InstanceSetting_MemoRelatedSetting_) isInstanceSetting_Value() {}
type InstanceSetting_TagsSetting_ (line 381) | type InstanceSetting_TagsSetting_ struct
method isInstanceSetting_Value (line 395) | func (*InstanceSetting_TagsSetting_) isInstanceSetting_Value() {}
type InstanceSetting_NotificationSetting_ (line 385) | type InstanceSetting_NotificationSetting_ struct
method isInstanceSetting_Value (line 397) | func (*InstanceSetting_NotificationSetting_) isInstanceSetting_Value() {}
type GetInstanceSettingRequest (line 400) | type GetInstanceSettingRequest struct
method Reset (line 409) | func (x *GetInstanceSettingRequest) Reset() {
method String (line 416) | func (x *GetInstanceSettingRequest) String() string {
method ProtoMessage (line 420) | func (*GetInstanceSettingRequest) ProtoMessage() {}
method ProtoReflect (line 422) | func (x *GetInstanceSettingRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 435) | func (*GetInstanceSettingRequest) Descriptor() ([]byte, []int) {
method GetName (line 439) | func (x *GetInstanceSettingRequest) GetName() string {
type UpdateInstanceSettingRequest (line 447) | type UpdateInstanceSettingRequest struct
method Reset (line 457) | func (x *UpdateInstanceSettingRequest) Reset() {
method String (line 464) | func (x *UpdateInstanceSettingRequest) String() string {
method ProtoMessage (line 468) | func (*UpdateInstanceSettingRequest) ProtoMessage() {}
method ProtoReflect (line 470) | func (x *UpdateInstanceSettingRequest) ProtoReflect() protoreflect.Mes...
method Descriptor (line 483) | func (*UpdateInstanceSettingRequest) Descriptor() ([]byte, []int) {
method GetSetting (line 487) | func (x *UpdateInstanceSettingRequest) GetSetting() *InstanceSetting {
method GetUpdateMask (line 494) | func (x *UpdateInstanceSettingRequest) GetUpdateMask() *fieldmaskpb.Fi...
type InstanceSetting_GeneralSetting (line 502) | type InstanceSetting_GeneralSetting struct
method Reset (line 526) | func (x *InstanceSetting_GeneralSetting) Reset() {
method String (line 533) | func (x *InstanceSetting_GeneralSetting) String() string {
method ProtoMessage (line 537) | func (*InstanceSetting_GeneralSetting) ProtoMessage() {}
method ProtoReflect (line 539) | func (x *InstanceSetting_GeneralSetting) ProtoReflect() protoreflect.M...
method Descriptor (line 552) | func (*InstanceSetting_GeneralSetting) Descriptor() ([]byte, []int) {
method GetDisallowUserRegistration (line 556) | func (x *InstanceSetting_GeneralSetting) GetDisallowUserRegistration()...
method GetDisallowPasswordAuth (line 563) | func (x *InstanceSetting_GeneralSetting) GetDisallowPasswordAuth() bool {
method GetAdditionalScript (line 570) | func (x *InstanceSetting_GeneralSetting) GetAdditionalScript() string {
method GetAdditionalStyle (line 577) | func (x *InstanceSetting_GeneralSetting) GetAdditionalStyle() string {
method GetCustomProfile (line 584) | func (x *InstanceSetting_GeneralSetting) GetCustomProfile() *InstanceS...
method GetWeekStartDayOffset (line 591) | func (x *InstanceSetting_GeneralSetting) GetWeekStartDayOffset() int32 {
method GetDisallowChangeUsername (line 598) | func (x *InstanceSetting_GeneralSetting) GetDisallowChangeUsername() b...
method GetDisallowChangeNickname (line 605) | func (x *InstanceSetting_GeneralSetting) GetDisallowChangeNickname() b...
type InstanceSetting_StorageSetting (line 613) | type InstanceSetting_StorageSetting struct
method Reset (line 628) | func (x *InstanceSetting_StorageSetting) Reset() {
method String (line 635) | func (x *InstanceSetting_StorageSetting) String() string {
method ProtoMessage (line 639) | func (*InstanceSetting_StorageSetting) ProtoMessage() {}
method ProtoReflect (line 641) | func (x *InstanceSetting_StorageSetting) ProtoReflect() protoreflect.M...
method Descriptor (line 654) | func (*InstanceSetting_StorageSetting) Descriptor() ([]byte, []int) {
method GetStorageType (line 658) | func (x *InstanceSetting_StorageSetting) GetStorageType() InstanceSett...
method GetFilepathTemplate (line 665) | func (x *InstanceSetting_StorageSetting) GetFilepathTemplate() string {
method GetUploadSizeLimitMb (line 672) | func (x *InstanceSetting_StorageSetting) GetUploadSizeLimitMb() int64 {
method GetS3Config (line 679) | func (x *InstanceSetting_StorageSetting) GetS3Config() *InstanceSettin...
type InstanceSetting_MemoRelatedSetting (line 687) | type InstanceSetting_MemoRelatedSetting struct
method Reset (line 701) | func (x *InstanceSetting_MemoRelatedSetting) Reset() {
method String (line 708) | func (x *InstanceSetting_MemoRelatedSetting) String() string {
method ProtoMessage (line 712) | func (*InstanceSetting_MemoRelatedSetting) ProtoMessage() {}
method ProtoReflect (line 714) | func (x *InstanceSetting_MemoRelatedSetting) ProtoReflect() protorefle...
method Descriptor (line 727) | func (*InstanceSetting_MemoRelatedSetting) Descriptor() ([]byte, []int) {
method GetDisplayWithUpdateTime (line 731) | func (x *InstanceSetting_MemoRelatedSetting) GetDisplayWithUpdateTime(...
method GetContentLengthLimit (line 738) | func (x *InstanceSetting_MemoRelatedSetting) GetContentLengthLimit() i...
method GetEnableDoubleClickEdit (line 745) | func (x *InstanceSetting_MemoRelatedSetting) GetEnableDoubleClickEdit(...
method GetReactions (line 752) | func (x *InstanceSetting_MemoRelatedSetting) GetReactions() []string {
type InstanceSetting_TagMetadata (line 760) | type InstanceSetting_TagMetadata struct
method Reset (line 768) | func (x *InstanceSetting_TagMetadata) Reset() {
method String (line 775) | func (x *InstanceSetting_TagMetadata) String() string {
method ProtoMessage (line 779) | func (*InstanceSetting_TagMetadata) ProtoMessage() {}
method ProtoReflect (line 781) | func (x *InstanceSetting_TagMetadata) ProtoReflect() protoreflect.Mess...
method Descriptor (line 794) | func (*InstanceSetting_TagMetadata) Descriptor() ([]byte, []int) {
method GetBackgroundColor (line 798) | func (x *InstanceSetting_TagMetadata) GetBackgroundColor() *color.Color {
type InstanceSetting_TagsSetting (line 806) | type InstanceSetting_TagsSetting struct
method Reset (line 813) | func (x *InstanceSetting_TagsSetting) Reset() {
method String (line 820) | func (x *InstanceSetting_TagsSetting) String() string {
method ProtoMessage (line 824) | func (*InstanceSetting_TagsSetting) ProtoMessage() {}
method ProtoReflect (line 826) | func (x *InstanceSetting_TagsSetting) ProtoReflect() protoreflect.Mess...
method Descriptor (line 839) | func (*InstanceSetting_TagsSetting) Descriptor() ([]byte, []int) {
method GetTags (line 843) | func (x *InstanceSetting_TagsSetting) GetTags() map[string]*InstanceSe...
type InstanceSetting_NotificationSetting (line 851) | type InstanceSetting_NotificationSetting struct
method Reset (line 858) | func (x *InstanceSetting_NotificationSetting) Reset() {
method String (line 865) | func (x *InstanceSetting_NotificationSetting) String() string {
method ProtoMessage (line 869) | func (*InstanceSetting_NotificationSetting) ProtoMessage() {}
method ProtoReflect (line 871) | func (x *InstanceSetting_NotificationSetting) ProtoReflect() protorefl...
method Descriptor (line 884) | func (*InstanceSetting_NotificationSetting) Descriptor() ([]byte, []in...
method GetEmail (line 888) | func (x *InstanceSetting_NotificationSetting) GetEmail() *InstanceSett...
type InstanceSetting_GeneralSetting_CustomProfile (line 896) | type InstanceSetting_GeneralSetting_CustomProfile struct
method Reset (line 905) | func (x *InstanceSetting_GeneralSetting_CustomProfile) Reset() {
method String (line 912) | func (x *InstanceSetting_GeneralSetting_CustomProfile) String() string {
method ProtoMessage (line 916) | func (*InstanceSetting_GeneralSetting_CustomProfile) ProtoMessage() {}
method ProtoReflect (line 918) | func (x *InstanceSetting_GeneralSetting_CustomProfile) ProtoReflect() ...
method Descriptor (line 931) | func (*InstanceSetting_GeneralSetting_CustomProfile) Descriptor() ([]b...
method GetTitle (line 935) | func (x *InstanceSetting_GeneralSetting_CustomProfile) GetTitle() stri...
method GetDescription (line 942) | func (x *InstanceSetting_GeneralSetting_CustomProfile) GetDescription(...
method GetLogoUrl (line 949) | func (x *InstanceSetting_GeneralSetting_CustomProfile) GetLogoUrl() st...
type InstanceSetting_StorageSetting_S3Config (line 958) | type InstanceSetting_StorageSetting_S3Config struct
method Reset (line 970) | func (x *InstanceSetting_StorageSetting_S3Config) Reset() {
method String (line 977) | func (x *InstanceSetting_StorageSetting_S3Config) String() string {
method ProtoMessage (line 981) | func (*InstanceSetting_StorageSetting_S3Config) ProtoMessage() {}
method ProtoReflect (line 983) | func (x *InstanceSetting_StorageSetting_S3Config) ProtoReflect() proto...
method Descriptor (line 996) | func (*InstanceSetting_StorageSetting_S3Config) Descriptor() ([]byte, ...
method GetAccessKeyId (line 1000) | func (x *InstanceSetting_StorageSetting_S3Config) GetAccessKeyId() str...
method GetAccessKeySecret (line 1007) | func (x *InstanceSetting_StorageSetting_S3Config) GetAccessKeySecret()...
method GetEndpoint (line 1014) | func (x *InstanceSetting_StorageSetting_S3Config) GetEndpoint() string {
method GetRegion (line 1021) | func (x *InstanceSetting_StorageSetting_S3Config) GetRegion() string {
method GetBucket (line 1028) | func (x *InstanceSetting_StorageSetting_S3Config) GetBucket() string {
method GetUsePathStyle (line 1035) | func (x *InstanceSetting_StorageSetting_S3Config) GetUsePathStyle() bo...
type InstanceSetting_NotificationSetting_EmailSetting (line 1043) | type InstanceSetting_NotificationSetting_EmailSetting struct
method Reset (line 1059) | func (x *InstanceSetting_NotificationSetting_EmailSetting) Reset() {
method String (line 1066) | func (x *InstanceSetting_NotificationSetting_EmailSetting) String() st...
method ProtoMessage (line 1070) | func (*InstanceSetting_NotificationSetting_EmailSetting) ProtoMessage(...
method ProtoReflect (line 1072) | func (x *InstanceSetting_NotificationSetting_EmailSetting) ProtoReflec...
method Descriptor (line 1085) | func (*InstanceSetting_NotificationSetting_EmailSetting) Descriptor() ...
method GetEnabled (line 1089) | func (x *InstanceSetting_NotificationSetting_EmailSetting) GetEnabled(...
method GetSmtpHost (line 1096) | func (x *InstanceSetting_NotificationSetting_EmailSetting) GetSmtpHost...
method GetSmtpPort (line 1103) | func (x *InstanceSetting_NotificationSetting_EmailSetting) GetSmtpPort...
method GetSmtpUsername (line 1110) | func (x *InstanceSetting_NotificationSetting_EmailSetting) GetSmtpUser...
method GetSmtpPassword (line 1117) | func (x *InstanceSetting_NotificationSetting_EmailSetting) GetSmtpPass...
method GetFromEmail (line 1124) | func (x *InstanceSetting_NotificationSetting_EmailSetting) GetFromEmai...
method GetFromName (line 1131) | func (x *InstanceSetting_NotificationSetting_EmailSetting) GetFromName...
method GetReplyTo (line 1138) | func (x *InstanceSetting_NotificationSetting_EmailSetting) GetReplyTo(...
method GetUseTls (line 1145) | func (x *InstanceSetting_NotificationSetting_EmailSetting) GetUseTls()...
method GetUseSsl (line 1152) | func (x *InstanceSetting_NotificationSetting_EmailSetting) GetUseSsl()...
constant file_api_v1_instance_service_proto_rawDesc (line 1161) | file_api_v1_instance_service_proto_rawDesc = "" +
function file_api_v1_instance_service_proto_rawDescGZIP (line 1261) | func file_api_v1_instance_service_proto_rawDescGZIP() []byte {
function init (line 1321) | func init() { file_api_v1_instance_service_proto_init() }
function file_api_v1_instance_service_proto_init (line 1322) | func file_api_v1_instance_service_proto_init() {
FILE: proto/gen/api/v1/instance_service.pb.gw.go
function request_InstanceService_GetInstanceProfile_0 (line 38) | func request_InstanceService_GetInstanceProfile_0(ctx context.Context, m...
function local_request_InstanceService_GetInstanceProfile_0 (line 50) | func local_request_InstanceService_GetInstanceProfile_0(ctx context.Cont...
function request_InstanceService_GetInstanceSetting_0 (line 59) | func request_InstanceService_GetInstanceSetting_0(ctx context.Context, m...
function local_request_InstanceService_GetInstanceSetting_0 (line 80) | func local_request_InstanceService_GetInstanceSetting_0(ctx context.Cont...
function request_InstanceService_UpdateInstanceSetting_0 (line 100) | func request_InstanceService_UpdateInstanceSetting_0(ctx context.Context...
function local_request_InstanceService_UpdateInstanceSetting_0 (line 141) | func local_request_InstanceService_UpdateInstanceSetting_0(ctx context.C...
function RegisterInstanceServiceHandlerServer (line 184) | func RegisterInstanceServiceHandlerServer(ctx context.Context, mux *runt...
function RegisterInstanceServiceHandlerFromEndpoint (line 251) | func RegisterInstanceServiceHandlerFromEndpoint(ctx context.Context, mux...
function RegisterInstanceServiceHandler (line 275) | func RegisterInstanceServiceHandler(ctx context.Context, mux *runtime.Se...
function RegisterInstanceServiceHandlerClient (line 284) | func RegisterInstanceServiceHandlerClient(ctx context.Context, mux *runt...
FILE: proto/gen/api/v1/instance_service_grpc.pb.go
constant _ (line 19) | _ = grpc.SupportPackageIsVersion9
constant InstanceService_GetInstanceProfile_FullMethodName (line 22) | InstanceService_GetInstanceProfile_FullMethodName = "/memos.api.v1.In...
constant InstanceService_GetInstanceSetting_FullMethodName (line 23) | InstanceService_GetInstanceSetting_FullMethodName = "/memos.api.v1.In...
constant InstanceService_UpdateInstanceSetting_FullMethodName (line 24) | InstanceService_UpdateInstanceSetting_FullMethodName = "/memos.api.v1.In...
type InstanceServiceClient (line 30) | type InstanceServiceClient interface
type instanceServiceClient (line 39) | type instanceServiceClient struct
method GetInstanceProfile (line 47) | func (c *instanceServiceClient) GetInstanceProfile(ctx context.Context...
method GetInstanceSetting (line 57) | func (c *instanceServiceClient) GetInstanceSetting(ctx context.Context...
method UpdateInstanceSetting (line 67) | func (c *instanceServiceClient) UpdateInstanceSetting(ctx context.Cont...
function NewInstanceServiceClient (line 43) | func NewInstanceServiceClient(cc grpc.ClientConnInterface) InstanceServi...
type InstanceServiceServer (line 80) | type InstanceServiceServer interface
type UnimplementedInstanceServiceServer (line 95) | type UnimplementedInstanceServiceServer struct
method GetInstanceProfile (line 97) | func (UnimplementedInstanceServiceServer) GetInstanceProfile(context.C...
method GetInstanceSetting (line 100) | func (UnimplementedInstanceServiceServer) GetInstanceSetting(context.C...
method UpdateInstanceSetting (line 103) | func (UnimplementedInstanceServiceServer) UpdateInstanceSetting(contex...
method mustEmbedUnimplementedInstanceServiceServer (line 106) | func (UnimplementedInstanceServiceServer) mustEmbedUnimplementedInstan...
method testEmbeddedByValue (line 107) | func (UnimplementedInstanceServiceServer) testEmbeddedByValue() ...
type UnsafeInstanceServiceServer (line 112) | type UnsafeInstanceServiceServer interface
function RegisterInstanceServiceServer (line 116) | func RegisterInstanceServiceServer(s grpc.ServiceRegistrar, srv Instance...
function _InstanceService_GetInstanceProfile_Handler (line 127) | func _InstanceService_GetInstanceProfile_Handler(srv interface{}, ctx co...
function _InstanceService_GetInstanceSetting_Handler (line 145) | func _InstanceService_GetInstanceSetting_Handler(srv interface{}, ctx co...
function _InstanceService_UpdateInstanceSetting_Handler (line 163) | func _InstanceService_UpdateInstanceSetting_Handler(srv interface{}, ctx...
FILE: proto/gen/api/v1/memo_service.pb.go
constant _ (line 23) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
constant _ (line 25) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
type Visibility (line 28) | type Visibility
method Enum (line 53) | func (x Visibility) Enum() *Visibility {
method String (line 59) | func (x Visibility) String() string {
method Descriptor (line 63) | func (Visibility) Descriptor() protoreflect.EnumDescriptor {
method Type (line 67) | func (Visibility) Type() protoreflect.EnumType {
method Number (line 71) | func (x Visibility) Number() protoreflect.EnumNumber {
method EnumDescriptor (line 76) | func (Visibility) EnumDescriptor() ([]byte, []int) {
constant Visibility_VISIBILITY_UNSPECIFIED (line 31) | Visibility_VISIBILITY_UNSPECIFIED Visibility = 0
constant Visibility_PRIVATE (line 32) | Visibility_PRIVATE Visibility = 1
constant Visibility_PROTECTED (line 33) | Visibility_PROTECTED Visibility = 2
constant Visibility_PUBLIC (line 34) | Visibility_PUBLIC Visibility = 3
type MemoRelation_Type (line 81) | type MemoRelation_Type
method Enum (line 103) | func (x MemoRelation_Type) Enum() *MemoRelation_Type {
method String (line 109) | func (x MemoRelation_Type) String() string {
method Descriptor (line 113) | func (MemoRelation_Type) Descriptor() protoreflect.EnumDescriptor {
method Type (line 117) | func (MemoRelation_Type) Type() protoreflect.EnumType {
method Number (line 121) | func (x MemoRelation_Type) Number() protoreflect.EnumNumber {
method EnumDescriptor (line 126) | func (MemoRelation_Type) EnumDescriptor() ([]byte, []int) {
constant MemoRelation_TYPE_UNSPECIFIED (line 84) | MemoRelation_TYPE_UNSPECIFIED MemoRelation_Type = 0
constant MemoRelation_REFERENCE (line 85) | MemoRelation_REFERENCE MemoRelation_Type = 1
constant MemoRelation_COMMENT (line 86) | MemoRelation_COMMENT MemoRelation_Type = 2
type Reaction (line 130) | type Reaction struct
method Reset (line 150) | func (x *Reaction) Reset() {
method String (line 157) | func (x *Reaction) String() string {
method ProtoMessage (line 161) | func (*Reaction) ProtoMessage() {}
method ProtoReflect (line 163) | func (x *Reaction) ProtoReflect() protoreflect.Message {
method Descriptor (line 176) | func (*Reaction) Descriptor() ([]byte, []int) {
method GetName (line 180) | func (x *Reaction) GetName() string {
method GetCreator (line 187) | func (x *Reaction) GetCreator() string {
method GetContentId (line 194) | func (x *Reaction) GetContentId() string {
method GetReactionType (line 201) | func (x *Reaction) GetReactionType() string {
method GetCreateTime (line 208) | func (x *Reaction) GetCreateTime() *timestamppb.Timestamp {
type Memo (line 215) | type Memo struct
method Reset (line 260) | func (x *Memo) Reset() {
method String (line 267) | func (x *Memo) String() string {
method ProtoMessage (line 271) | func (*Memo) ProtoMessage() {}
method ProtoReflect (line 273) | func (x *Memo) ProtoReflect() protoreflect.Message {
method Descriptor (line 286) | func (*Memo) Descriptor() ([]byte, []int) {
method GetName (line 290) | func (x *Memo) GetName() string {
method GetState (line 297) | func (x *Memo) GetState() State {
method GetCreator (line 304) | func (x *Memo) GetCreator() string {
method GetCreateTime (line 311) | func (x *Memo) GetCreateTime() *timestamppb.Timestamp {
method GetUpdateTime (line 318) | func (x *Memo) GetUpdateTime() *timestamppb.Timestamp {
method GetDisplayTime (line 325) | func (x *Memo) GetDisplayTime() *timestamppb.Timestamp {
method GetContent (line 332) | func (x *Memo) GetContent() string {
method GetVisibility (line 339) | func (x *Memo) GetVisibility() Visibility {
method GetTags (line 346) | func (x *Memo) GetTags() []string {
method GetPinned (line 353) | func (x *Memo) GetPinned() bool {
method GetAttachments (line 360) | func (x *Memo) GetAttachments() []*Attachment {
method GetRelations (line 367) | func (x *Memo) GetRelations() []*MemoRelation {
method GetReactions (line 374) | func (x *Memo) GetReactions() []*Reaction {
method GetProperty (line 381) | func (x *Memo) GetProperty() *Memo_Property {
method GetParent (line 388) | func (x *Memo) GetParent() string {
method GetSnippet (line 395) | func (x *Memo) GetSnippet() string {
method GetLocation (line 402) | func (x *Memo) GetLocation() *Location {
type Location (line 409) | type Location struct
method Reset (line 421) | func (x *Location) Reset() {
method String (line 428) | func (x *Location) String() string {
method ProtoMessage (line 432) | func (*Location) ProtoMessage() {}
method ProtoReflect (line 434) | func (x *Location) ProtoReflect() protoreflect.Message {
method Descriptor (line 447) | func (*Location) Descriptor() ([]byte, []int) {
method GetPlaceholder (line 451) | func (x *Location) GetPlaceholder() string {
method GetLatitude (line 458) | func (x *Location) GetLatitude() float64 {
method GetLongitude (line 465) | func (x *Location) GetLongitude() float64 {
type CreateMemoRequest (line 472) | type CreateMemoRequest struct
method Reset (line 483) | func (x *CreateMemoRequest) Reset() {
method String (line 490) | func (x *CreateMemoRequest) String() string {
method ProtoMessage (line 494) | func (*CreateMemoRequest) ProtoMessage() {}
method ProtoReflect (line 496) | func (x *CreateMemoRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 509) | func (*CreateMemoRequest) Descriptor() ([]byte, []int) {
method GetMemo (line 513) | func (x *CreateMemoRequest) GetMemo() *Memo {
method GetMemoId (line 520) | func (x *CreateMemoRequest) GetMemoId() string {
type ListMemosRequest (line 527) | type ListMemosRequest struct
method Reset (line 556) | func (x *ListMemosRequest) Reset() {
method String (line 563) | func (x *ListMemosRequest) String() string {
method ProtoMessage (line 567) | func (*ListMemosRequest) ProtoMessage() {}
method ProtoReflect (line 569) | func (x *ListMemosRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 582) | func (*ListMemosRequest) Descriptor() ([]byte, []int) {
method GetPageSize (line 586) | func (x *ListMemosRequest) GetPageSize() int32 {
method GetPageToken (line 593) | func (x *ListMemosRequest) GetPageToken() string {
method GetState (line 600) | func (x *ListMemosRequest) GetState() State {
method GetOrderBy (line 607) | func (x *ListMemosRequest) GetOrderBy() string {
method GetFilter (line 614) | func (x *ListMemosRequest) GetFilter() string {
method GetShowDeleted (line 621) | func (x *ListMemosRequest) GetShowDeleted() bool {
type ListMemosResponse (line 628) | type ListMemosResponse struct
method Reset (line 639) | func (x *ListMemosResponse) Reset() {
method String (line 646) | func (x *ListMemosResponse) String() string {
method ProtoMessage (line 650) | func (*ListMemosResponse) ProtoMessage() {}
method ProtoReflect (line 652) | func (x *ListMemosResponse) ProtoReflect() protoreflect.Message {
method Descriptor (line 665) | func (*ListMemosResponse) Descriptor() ([]byte, []int) {
method GetMemos (line 669) | func (x *ListMemosResponse) GetMemos() []*Memo {
method GetNextPageToken (line 676) | func (x *ListMemosResponse) GetNextPageToken() string {
type GetMemoRequest (line 683) | type GetMemoRequest struct
method Reset (line 692) | func (x *GetMemoRequest) Reset() {
method String (line 699) | func (x *GetMemoRequest) String() string {
method ProtoMessage (line 703) | func (*GetMemoRequest) ProtoMessage() {}
method ProtoReflect (line 705) | func (x *GetMemoRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 718) | func (*GetMemoRequest) Descriptor() ([]byte, []int) {
method GetName (line 722) | func (x *GetMemoRequest) GetName() string {
type UpdateMemoRequest (line 729) | type UpdateMemoRequest struct
method Reset (line 740) | func (x *UpdateMemoRequest) Reset() {
method String (line 747) | func (x *UpdateMemoRequest) String() string {
method ProtoMessage (line 751) | func (*UpdateMemoRequest) ProtoMessage() {}
method ProtoReflect (line 753) | func (x *UpdateMemoRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 766) | func (*UpdateMemoRequest) Descriptor() ([]byte, []int) {
method GetMemo (line 770) | func (x *UpdateMemoRequest) GetMemo() *Memo {
method GetUpdateMask (line 777) | func (x *UpdateMemoRequest) GetUpdateMask() *fieldmaskpb.FieldMask {
type DeleteMemoRequest (line 784) | type DeleteMemoRequest struct
method Reset (line 795) | func (x *DeleteMemoRequest) Reset() {
method String (line 802) | func (x *DeleteMemoRequest) String() string {
method ProtoMessage (line 806) | func (*DeleteMemoRequest) ProtoMessage() {}
method ProtoReflect (line 808) | func (x *DeleteMemoRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 821) | func (*DeleteMemoRequest) Descriptor() ([]byte, []int) {
method GetName (line 825) | func (x *DeleteMemoRequest) GetName() string {
method GetForce (line 832) | func (x *DeleteMemoRequest) GetForce() bool {
type SetMemoAttachmentsRequest (line 839) | type SetMemoAttachmentsRequest struct
method Reset (line 850) | func (x *SetMemoAttachmentsRequest) Reset() {
method String (line 857) | func (x *SetMemoAttachmentsRequest) String() string {
method ProtoMessage (line 861) | func (*SetMemoAttachmentsRequest) ProtoMessage() {}
method ProtoReflect (line 863) | func (x *SetMemoAttachmentsRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 876) | func (*SetMemoAttachmentsRequest) Descriptor() ([]byte, []int) {
method GetName (line 880) | func (x *SetMemoAttachmentsRequest) GetName() string {
method GetAttachments (line 887) | func (x *SetMemoAttachmentsRequest) GetAttachments() []*Attachment {
type ListMemoAttachmentsRequest (line 894) | type ListMemoAttachmentsRequest struct
method Reset (line 907) | func (x *ListMemoAttachmentsRequest) Reset() {
method String (line 914) | func (x *ListMemoAttachmentsRequest) String() string {
method ProtoMessage (line 918) | func (*ListMemoAttachmentsRequest) ProtoMessage() {}
method ProtoReflect (line 920) | func (x *ListMemoAttachmentsRequest) ProtoReflect() protoreflect.Messa...
method Descriptor (line 933) | func (*ListMemoAttachmentsRequest) Descriptor() ([]byte, []int) {
method GetName (line 937) | func (x *ListMemoAttachmentsRequest) GetName() string {
method GetPageSize (line 944) | func (x *ListMemoAttachmentsRequest) GetPageSize() int32 {
method GetPageToken (line 951) | func (x *ListMemoAttachmentsRequest) GetPageToken() string {
type ListMemoAttachmentsResponse (line 958) | type ListMemoAttachmentsResponse struct
method Reset (line 968) | func (x *ListMemoAttachmentsResponse) Reset() {
method String (line 975) | func (x *ListMemoAttachmentsResponse) String() string {
method ProtoMessage (line 979) | func (*ListMemoAttachmentsResponse) ProtoMessage() {}
method ProtoReflect (line 981) | func (x *ListMemoAttachmentsResponse) ProtoReflect() protoreflect.Mess...
method Descriptor (line 994) | func (*ListMemoAttachmentsResponse) Descriptor() ([]byte, []int) {
method GetAttachments (line 998) | func (x *ListMemoAttachmentsResponse) GetAttachments() []*Attachment {
method GetNextPageToken (line 1005) | func (x *ListMemoAttachmentsResponse) GetNextPageToken() string {
type MemoRelation (line 1012) | type MemoRelation struct
method Reset (line 1023) | func (x *MemoRelation) Reset() {
method String (line 1030) | func (x *MemoRelation) String() string {
method ProtoMessage (line 1034) | func (*MemoRelation) ProtoMessage() {}
method ProtoReflect (line 1036) | func (x *MemoRelation) ProtoReflect() protoreflect.Message {
method Descriptor (line 1049) | func (*MemoRelation) Descriptor() ([]byte, []int) {
method GetMemo (line 1053) | func (x *MemoRelation) GetMemo() *MemoRelation_Memo {
method GetRelatedMemo (line 1060) | func (x *MemoRelation) GetRelatedMemo() *MemoRelation_Memo {
method GetType (line 1067) | func (x *MemoRelation) GetType() MemoRelation_Type {
type SetMemoRelationsRequest (line 1074) | type SetMemoRelationsRequest struct
method Reset (line 1085) | func (x *SetMemoRelationsRequest) Reset() {
method String (line 1092) | func (x *SetMemoRelationsRequest) String() string {
method ProtoMessage (line 1096) | func (*SetMemoRelationsRequest) ProtoMessage() {}
method ProtoReflect (line 1098) | func (x *SetMemoRelationsRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 1111) | func (*SetMemoRelationsRequest) Descriptor() ([]byte, []int) {
method GetName (line 1115) | func (x *SetMemoRelationsRequest) GetName() string {
method GetRelations (line 1122) | func (x *SetMemoRelationsRequest) GetRelations() []*MemoRelation {
type ListMemoRelationsRequest (line 1129) | type ListMemoRelationsRequest struct
method Reset (line 1142) | func (x *ListMemoRelationsRequest) Reset() {
method String (line 1149) | func (x *ListMemoRelationsRequest) String() string {
method ProtoMessage (line 1153) | func (*ListMemoRelationsRequest) ProtoMessage() {}
method ProtoReflect (line 1155) | func (x *ListMemoRelationsRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 1168) | func (*ListMemoRelationsRequest) Descriptor() ([]byte, []int) {
method GetName (line 1172) | func (x *ListMemoRelationsRequest) GetName() string {
method GetPageSize (line 1179) | func (x *ListMemoRelationsRequest) GetPageSize() int32 {
method GetPageToken (line 1186) | func (x *ListMemoRelationsRequest) GetPageToken() string {
type ListMemoRelationsResponse (line 1193) | type ListMemoRelationsResponse struct
method Reset (line 1203) | func (x *ListMemoRelationsResponse) Reset() {
method String (line 1210) | func (x *ListMemoRelationsResponse) String() string {
method ProtoMessage (line 1214) | func (*ListMemoRelationsResponse) ProtoMessage() {}
method ProtoReflect (line 1216) | func (x *ListMemoRelationsResponse) ProtoReflect() protoreflect.Message {
method Descriptor (line 1229) | func (*ListMemoRelationsResponse) Descriptor() ([]byte, []int) {
method GetRelations (line 1233) | func (x *ListMemoRelationsResponse) GetRelations() []*MemoRelation {
method GetNextPageToken (line 1240) | func (x *ListMemoRelationsResponse) GetNextPageToken() string {
type CreateMemoCommentRequest (line 1247) | type CreateMemoCommentRequest struct
method Reset (line 1260) | func (x *CreateMemoCommentRequest) Reset() {
method String (line 1267) | func (x *CreateMemoCommentRequest) String() string {
method ProtoMessage (line 1271) | func (*CreateMemoCommentRequest) ProtoMessage() {}
method ProtoReflect (line 1273) | func (x *CreateMemoCommentRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 1286) | func (*CreateMemoCommentRequest) Descriptor() ([]byte, []int) {
method GetName (line 1290) | func (x *CreateMemoCommentRequest) GetName() string {
method GetComment (line 1297) | func (x *CreateMemoCommentRequest) GetComment() *Memo {
method GetCommentId (line 1304) | func (x *CreateMemoCommentRequest) GetCommentId() string {
type ListMemoCommentsRequest (line 1311) | type ListMemoCommentsRequest struct
method Reset (line 1326) | func (x *ListMemoCommentsRequest) Reset() {
method String (line 1333) | func (x *ListMemoCommentsRequest) String() string {
method ProtoMessage (line 1337) | func (*ListMemoCommentsRequest) ProtoMessage() {}
method ProtoReflect (line 1339) | func (x *ListMemoCommentsRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 1352) | func (*ListMemoCommentsRequest) Descriptor() ([]byte, []int) {
method GetName (line 1356) | func (x *ListMemoCommentsRequest) GetName() string {
method GetPageSize (line 1363) | func (x *ListMemoCommentsRequest) GetPageSize() int32 {
method GetPageToken (line 1370) | func (x *ListMemoCommentsRequest) GetPageToken() string {
method GetOrderBy (line 1377) | func (x *ListMemoCommentsRequest) GetOrderBy() string {
type ListMemoCommentsResponse (line 1384) | type ListMemoCommentsResponse struct
method Reset (line 1396) | func (x *ListMemoCommentsResponse) Reset() {
method String (line 1403) | func (x *ListMemoCommentsResponse) String() string {
method ProtoMessage (line 1407) | func (*ListMemoCommentsResponse) ProtoMessage() {}
method ProtoReflect (line 1409) | func (x *ListMemoCommentsResponse) ProtoReflect() protoreflect.Message {
method Descriptor (line 1422) | func (*ListMemoCommentsResponse) Descriptor() ([]byte, []int) {
method GetMemos (line 1426) | func (x *ListMemoCommentsResponse) GetMemos() []*Memo {
method GetNextPageToken (line 1433) | func (x *ListMemoCommentsResponse) GetNextPageToken() string {
method GetTotalSize (line 1440) | func (x *ListMemoCommentsResponse) GetTotalSize() int32 {
type ListMemoReactionsRequest (line 1447) | type ListMemoReactionsRequest struct
method Reset (line 1460) | func (x *ListMemoReactionsRequest) Reset() {
method String (line 1467) | func (x *ListMemoReactionsRequest) String() string {
method ProtoMessage (line 1471) | func (*ListMemoReactionsRequest) ProtoMessage() {}
method ProtoReflect (line 1473) | func (x *ListMemoReactionsRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 1486) | func (*ListMemoReactionsRequest) Descriptor() ([]byte, []int) {
method GetName (line 1490) | func (x *ListMemoReactionsRequest) GetName() string {
method GetPageSize (line 1497) | func (x *ListMemoReactionsRequest) GetPageSize() int32 {
method GetPageToken (line 1504) | func (x *ListMemoReactionsRequest) GetPageToken() string {
type ListMemoReactionsResponse (line 1511) | type ListMemoReactionsResponse struct
method Reset (line 1523) | func (x *ListMemoReactionsResponse) Reset() {
method String (line 1530) | func (x *ListMemoReactionsResponse) String() string {
method ProtoMessage (line 1534) | func (*ListMemoReactionsResponse) ProtoMessage() {}
method ProtoReflect (line 1536) | func (x *ListMemoReactionsResponse) ProtoReflect() protoreflect.Message {
method Descriptor (line 1549) | func (*ListMemoReactionsResponse) Descriptor() ([]byte, []int) {
method GetReactions (line 1553) | func (x *ListMemoReactionsResponse) GetReactions() []*Reaction {
method GetNextPageToken (line 1560) | func (x *ListMemoReactionsResponse) GetNextPageToken() string {
method GetTotalSize (line 1567) | func (x *ListMemoReactionsResponse) GetTotalSize() int32 {
type UpsertMemoReactionRequest (line 1574) | type UpsertMemoReactionRequest struct
method Reset (line 1585) | func (x *UpsertMemoReactionRequest) Reset() {
method String (line 1592) | func (x *UpsertMemoReactionRequest) String() string {
method ProtoMessage (line 1596) | func (*UpsertMemoReactionRequest) ProtoMessage() {}
method ProtoReflect (line 1598) | func (x *UpsertMemoReactionRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 1611) | func (*UpsertMemoReactionRequest) Descriptor() ([]byte, []int) {
method GetName (line 1615) | func (x *UpsertMemoReactionRequest) GetName() string {
method GetReaction (line 1622) | func (x *UpsertMemoReactionRequest) GetReaction() *Reaction {
type DeleteMemoReactionRequest (line 1629) | type DeleteMemoReactionRequest struct
method Reset (line 1638) | func (x *DeleteMemoReactionRequest) Reset() {
method String (line 1645) | func (x *DeleteMemoReactionRequest) String() string {
method ProtoMessage (line 1649) | func (*DeleteMemoReactionRequest) ProtoMessage() {}
method ProtoReflect (line 1651) | func (x *DeleteMemoReactionRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 1664) | func (*DeleteMemoReactionRequest) Descriptor() ([]byte, []int) {
method GetName (line 1668) | func (x *DeleteMemoReactionRequest) GetName() string {
type MemoShare (line 1676) | type MemoShare struct
method Reset (line 1690) | func (x *MemoShare) Reset() {
method String (line 1697) | func (x *MemoShare) String() string {
method ProtoMessage (line 1701) | func (*MemoShare) ProtoMessage() {}
method ProtoReflect (line 1703) | func (x *MemoShare) ProtoReflect() protoreflect.Message {
method Descriptor (line 1716) | func (*MemoShare) Descriptor() ([]byte, []int) {
method GetName (line 1720) | func (x *MemoShare) GetName() string {
method GetCreateTime (line 1727) | func (x *MemoShare) GetCreateTime() *timestamppb.Timestamp {
method GetExpireTime (line 1734) | func (x *MemoShare) GetExpireTime() *timestamppb.Timestamp {
type CreateMemoShareRequest (line 1741) | type CreateMemoShareRequest struct
method Reset (line 1752) | func (x *CreateMemoShareRequest) Reset() {
method String (line 1759) | func (x *CreateMemoShareRequest) String() string {
method ProtoMessage (line 1763) | func (*CreateMemoShareRequest) ProtoMessage() {}
method ProtoReflect (line 1765) | func (x *CreateMemoShareRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 1778) | func (*CreateMemoShareRequest) Descriptor() ([]byte, []int) {
method GetParent (line 1782) | func (x *CreateMemoShareRequest) GetParent() string {
method GetMemoShare (line 1789) | func (x *CreateMemoShareRequest) GetMemoShare() *MemoShare {
type ListMemoSharesRequest (line 1796) | type ListMemoSharesRequest struct
method Reset (line 1805) | func (x *ListMemoSharesRequest) Reset() {
method String (line 1812) | func (x *ListMemoSharesRequest) String() string {
method ProtoMessage (line 1816) | func (*ListMemoSharesRequest) ProtoMessage() {}
method ProtoReflect (line 1818) | func (x *ListMemoSharesRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 1831) | func (*ListMemoSharesRequest) Descriptor() ([]byte, []int) {
method GetParent (line 1835) | func (x *ListMemoSharesRequest) GetParent() string {
type ListMemoSharesResponse (line 1842) | type ListMemoSharesResponse struct
method Reset (line 1850) | func (x *ListMemoSharesResponse) Reset() {
method String (line 1857) | func (x *ListMemoSharesResponse) String() string {
method ProtoMessage (line 1861) | func (*ListMemoSharesResponse) ProtoMessage() {}
method ProtoReflect (line 1863) | func (x *ListMemoSharesResponse) ProtoReflect() protoreflect.Message {
method Descriptor (line 1876) | func (*ListMemoSharesResponse) Descriptor() ([]byte, []int) {
method GetMemoShares (line 1880) | func (x *ListMemoSharesResponse) GetMemoShares() []*MemoShare {
type DeleteMemoShareRequest (line 1887) | type DeleteMemoShareRequest struct
method Reset (line 1896) | func (x *DeleteMemoShareRequest) Reset() {
method String (line 1903) | func (x *DeleteMemoShareRequest) String() string {
method ProtoMessage (line 1907) | func (*DeleteMemoShareRequest) ProtoMessage() {}
method ProtoReflect (line 1909) | func (x *DeleteMemoShareRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 1922) | func (*DeleteMemoShareRequest) Descriptor() ([]byte, []int) {
method GetName (line 1926) | func (x *DeleteMemoShareRequest) GetName() string {
type GetMemoByShareRequest (line 1933) | type GetMemoByShareRequest struct
method Reset (line 1941) | func (x *GetMemoByShareRequest) Reset() {
method String (line 1948) | func (x *GetMemoByShareRequest) String() string {
method ProtoMessage (line 1952) | func (*GetMemoByShareRequest) ProtoMessage() {}
method ProtoReflect (line 1954) | func (x *GetMemoByShareRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 1967) | func (*GetMemoByShareRequest) Descriptor() ([]byte, []int) {
method GetShareId (line 1971) | func (x *GetMemoByShareRequest) GetShareId() string {
type Memo_Property (line 1979) | type Memo_Property struct
method Reset (line 1991) | func (x *Memo_Property) Reset() {
method String (line 1998) | func (x *Memo_Property) String() string {
method ProtoMessage (line 2002) | func (*Memo_Property) ProtoMessage() {}
method ProtoReflect (line 2004) | func (x *Memo_Property) ProtoReflect() protoreflect.Message {
method Descriptor (line 2017) | func (*Memo_Property) Descriptor() ([]byte, []int) {
method GetHasLink (line 2021) | func (x *Memo_Property) GetHasLink() bool {
method GetHasTaskList (line 2028) | func (x *Memo_Property) GetHasTaskList() bool {
method GetHasCode (line 2035) | func (x *Memo_Property) GetHasCode() bool {
method GetHasIncompleteTasks (line 2042) | func (x *Memo_Property) GetHasIncompleteTasks() bool {
method GetTitle (line 2049) | func (x *Memo_Property) GetTitle() string {
type MemoRelation_Memo (line 2057) | type MemoRelation_Memo struct
method Reset (line 2068) | func (x *MemoRelation_Memo) Reset() {
method String (line 2075) | func (x *MemoRelation_Memo) String() string {
method ProtoMessage (line 2079) | func (*MemoRelation_Memo) ProtoMessage() {}
method ProtoReflect (line 2081) | func (x *MemoRelation_Memo) ProtoReflect() protoreflect.Message {
method Descriptor (line 2094) | func (*MemoRelation_Memo) Descriptor() ([]byte, []int) {
method GetName (line 2098) | func (x *MemoRelation_Memo) GetName() string {
method GetSnippet (line 2105) | func (x *MemoRelation_Memo) GetSnippet() string {
constant file_api_v1_memo_service_proto_rawDesc (line 2114) | file_api_v1_memo_service_proto_rawDesc = "" +
function file_api_v1_memo_service_proto_rawDescGZIP (line 2326) | func file_api_v1_memo_service_proto_rawDescGZIP() []byte {
function init (line 2450) | func init() { file_api_v1_memo_service_proto_init() }
function file_api_v1_memo_service_proto_init (line 2451) | func file_api_v1_memo_service_proto_init() {
FILE: proto/gen/api/v1/memo_service.pb.gw.go
function request_MemoService_CreateMemo_0 (line 40) | func request_MemoService_CreateMemo_0(ctx context.Context, marshaler run...
function local_request_MemoService_CreateMemo_0 (line 61) | func local_request_MemoService_CreateMemo_0(ctx context.Context, marshal...
function request_MemoService_ListMemos_0 (line 81) | func request_MemoService_ListMemos_0(ctx context.Context, marshaler runt...
function local_request_MemoService_ListMemos_0 (line 99) | func local_request_MemoService_ListMemos_0(ctx context.Context, marshale...
function request_MemoService_GetMemo_0 (line 114) | func request_MemoService_GetMemo_0(ctx context.Context, marshaler runtim...
function local_request_MemoService_GetMemo_0 (line 135) | func local_request_MemoService_GetMemo_0(ctx context.Context, marshaler ...
function request_MemoService_UpdateMemo_0 (line 155) | func request_MemoService_UpdateMemo_0(ctx context.Context, marshaler run...
function local_request_MemoService_UpdateMemo_0 (line 196) | func local_request_MemoService_UpdateMemo_0(ctx context.Context, marshal...
function request_MemoService_DeleteMemo_0 (line 236) | func request_MemoService_DeleteMemo_0(ctx context.Context, marshaler run...
function local_request_MemoService_DeleteMemo_0 (line 263) | func local_request_MemoService_DeleteMemo_0(ctx context.Context, marshal...
function request_MemoService_SetMemoAttachments_0 (line 287) | func request_MemoService_SetMemoAttachments_0(ctx context.Context, marsh...
function local_request_MemoService_SetMemoAttachments_0 (line 311) | func local_request_MemoService_SetMemoAttachments_0(ctx context.Context,...
function request_MemoService_ListMemoAttachments_0 (line 334) | func request_MemoService_ListMemoAttachments_0(ctx context.Context, mars...
function local_request_MemoService_ListMemoAttachments_0 (line 361) | func local_request_MemoService_ListMemoAttachments_0(ctx context.Context...
function request_MemoService_SetMemoRelations_0 (line 385) | func request_MemoService_SetMemoRelations_0(ctx context.Context, marshal...
function local_request_MemoService_SetMemoRelations_0 (line 409) | func local_request_MemoService_SetMemoRelations_0(ctx context.Context, m...
function request_MemoService_ListMemoRelations_0 (line 432) | func request_MemoService_ListMemoRelations_0(ctx context.Context, marsha...
function local_request_MemoService_ListMemoRelations_0 (line 459) | func local_request_MemoService_ListMemoRelations_0(ctx context.Context, ...
function request_MemoService_CreateMemoComment_0 (line 485) | func request_MemoService_CreateMemoComment_0(ctx context.Context, marsha...
function local_request_MemoService_CreateMemoComment_0 (line 515) | func local_request_MemoService_CreateMemoComment_0(ctx context.Context, ...
function request_MemoService_ListMemoComments_0 (line 544) | func request_MemoService_ListMemoComments_0(ctx context.Context, marshal...
function local_request_MemoService_ListMemoComments_0 (line 571) | func local_request_MemoService_ListMemoComments_0(ctx context.Context, m...
function request_MemoService_ListMemoReactions_0 (line 597) | func request_MemoService_ListMemoReactions_0(ctx context.Context, marsha...
function local_request_MemoService_ListMemoReactions_0 (line 624) | func local_request_MemoService_ListMemoReactions_0(ctx context.Context, ...
function request_MemoService_UpsertMemoReaction_0 (line 648) | func request_MemoService_UpsertMemoReaction_0(ctx context.Context, marsh...
function local_request_MemoService_UpsertMemoReaction_0 (line 672) | func local_request_MemoService_UpsertMemoReaction_0(ctx context.Context,...
function request_MemoService_DeleteMemoReaction_0 (line 693) | func request_MemoService_DeleteMemoReaction_0(ctx context.Context, marsh...
function local_request_MemoService_DeleteMemoReaction_0 (line 714) | func local_request_MemoService_DeleteMemoReaction_0(ctx context.Context,...
function request_MemoService_CreateMemoShare_0 (line 732) | func request_MemoService_CreateMemoShare_0(ctx context.Context, marshale...
function local_request_MemoService_CreateMemoShare_0 (line 756) | func local_request_MemoService_CreateMemoShare_0(ctx context.Context, ma...
function request_MemoService_ListMemoShares_0 (line 777) | func request_MemoService_ListMemoShares_0(ctx context.Context, marshaler...
function local_request_MemoService_ListMemoShares_0 (line 798) | func local_request_MemoService_ListMemoShares_0(ctx context.Context, mar...
function request_MemoService_DeleteMemoShare_0 (line 816) | func request_MemoService_DeleteMemoShare_0(ctx context.Context, marshale...
function local_request_MemoService_DeleteMemoShare_0 (line 837) | func local_request_MemoService_DeleteMemoShare_0(ctx context.Context, ma...
function request_MemoService_GetMemoByShare_0 (line 855) | func request_MemoService_GetMemoByShare_0(ctx context.Context, marshaler...
function local_request_MemoService_GetMemoByShare_0 (line 876) | func local_request_MemoService_GetMemoByShare_0(ctx context.Context, mar...
function RegisterMemoServiceHandlerServer (line 899) | func RegisterMemoServiceHandlerServer(ctx context.Context, mux *runtime....
function RegisterMemoServiceHandlerFromEndpoint (line 1266) | func RegisterMemoServiceHandlerFromEndpoint(ctx context.Context, mux *ru...
function RegisterMemoServiceHandler (line 1290) | func RegisterMemoServiceHandler(ctx context.Context, mux *runtime.ServeM...
function RegisterMemoServiceHandlerClient (line 1299) | func RegisterMemoServiceHandlerClient(ctx context.Context, mux *runtime....
FILE: proto/gen/api/v1/memo_service_grpc.pb.go
constant _ (line 20) | _ = grpc.SupportPackageIsVersion9
constant MemoService_CreateMemo_FullMethodName (line 23) | MemoService_CreateMemo_FullMethodName = "/memos.api.v1.MemoServ...
constant MemoService_ListMemos_FullMethodName (line 24) | MemoService_ListMemos_FullMethodName = "/memos.api.v1.MemoServ...
constant MemoService_GetMemo_FullMethodName (line 25) | MemoService_GetMemo_FullMethodName = "/memos.api.v1.MemoServ...
constant MemoService_UpdateMemo_FullMethodName (line 26) | MemoService_UpdateMemo_FullMethodName = "/memos.api.v1.MemoServ...
constant MemoService_DeleteMemo_FullMethodName (line 27) | MemoService_DeleteMemo_FullMethodName = "/memos.api.v1.MemoServ...
constant MemoService_SetMemoAttachments_FullMethodName (line 28) | MemoService_SetMemoAttachments_FullMethodName = "/memos.api.v1.MemoServ...
constant MemoService_ListMemoAttachments_FullMethodName (line 29) | MemoService_ListMemoAttachments_FullMethodName = "/memos.api.v1.MemoServ...
constant MemoService_SetMemoRelations_FullMethodName (line 30) | MemoService_SetMemoRelations_FullMethodName = "/memos.api.v1.MemoServ...
constant MemoService_ListMemoRelations_FullMethodName (line 31) | MemoService_ListMemoRelations_FullMethodName = "/memos.api.v1.MemoServ...
constant MemoService_CreateMemoComment_FullMethodName (line 32) | MemoService_CreateMemoComment_FullMethodName = "/memos.api.v1.MemoServ...
constant MemoService_ListMemoComments_FullMethodName (line 33) | MemoService_ListMemoComments_FullMethodName = "/memos.api.v1.MemoServ...
constant MemoService_ListMemoReactions_FullMethodName (line 34) | MemoService_ListMemoReactions_FullMethodName = "/memos.api.v1.MemoServ...
constant MemoService_UpsertMemoReaction_FullMethodName (line 35) | MemoService_UpsertMemoReaction_FullMethodName = "/memos.api.v1.MemoServ...
constant MemoService_DeleteMemoReaction_FullMethodName (line 36) | MemoService_DeleteMemoReaction_FullMethodName = "/memos.api.v1.MemoServ...
constant MemoService_CreateMemoShare_FullMethodName (line 37) | MemoService_CreateMemoShare_FullMethodName = "/memos.api.v1.MemoServ...
constant MemoService_ListMemoShares_FullMethodName (line 38) | MemoService_ListMemoShares_FullMethodName = "/memos.api.v1.MemoServ...
constant MemoService_DeleteMemoShare_FullMethodName (line 39) | MemoService_DeleteMemoShare_FullMethodName = "/memos.api.v1.MemoServ...
constant MemoService_GetMemoByShare_FullMethodName (line 40) | MemoService_GetMemoByShare_FullMethodName = "/memos.api.v1.MemoServ...
type MemoServiceClient (line 46) | type MemoServiceClient interface
type memoServiceClient (line 86) | type memoServiceClient struct
method CreateMemo (line 94) | func (c *memoServiceClient) CreateMemo(ctx context.Context, in *Create...
method ListMemos (line 104) | func (c *memoServiceClient) ListMemos(ctx context.Context, in *ListMem...
method GetMemo (line 114) | func (c *memoServiceClient) GetMemo(ctx context.Context, in *GetMemoRe...
method UpdateMemo (line 124) | func (c *memoServiceClient) UpdateMemo(ctx context.Context, in *Update...
method DeleteMemo (line 134) | func (c *memoServiceClient) DeleteMemo(ctx context.Context, in *Delete...
method SetMemoAttachments (line 144) | func (c *memoServiceClient) SetMemoAttachments(ctx context.Context, in...
method ListMemoAttachments (line 154) | func (c *memoServiceClient) ListMemoAttachments(ctx context.Context, i...
method SetMemoRelations (line 164) | func (c *memoServiceClient) SetMemoRelations(ctx context.Context, in *...
method ListMemoRelations (line 174) | func (c *memoServiceClient) ListMemoRelations(ctx context.Context, in ...
method CreateMemoComment (line 184) | func (c *memoServiceClient) CreateMemoComment(ctx context.Context, in ...
method ListMemoComments (line 194) | func (c *memoServiceClient) ListMemoComments(ctx context.Context, in *...
method ListMemoReactions (line 204) | func (c *memoServiceClient) ListMemoReactions(ctx context.Context, in ...
method UpsertMemoReaction (line 214) | func (c *memoServiceClient) UpsertMemoReaction(ctx context.Context, in...
method DeleteMemoReaction (line 224) | func (c *memoServiceClient) DeleteMemoReaction(ctx context.Context, in...
method CreateMemoShare (line 234) | func (c *memoServiceClient) CreateMemoShare(ctx context.Context, in *C...
method ListMemoShares (line 244) | func (c *memoServiceClient) ListMemoShares(ctx context.Context, in *Li...
method DeleteMemoShare (line 254) | func (c *memoServiceClient) DeleteMemoShare(ctx context.Context, in *D...
method GetMemoByShare (line 264) | func (c *memoServiceClient) GetMemoByShare(ctx context.Context, in *Ge...
function NewMemoServiceClient (line 90) | func NewMemoServiceClient(cc grpc.ClientConnInterface) MemoServiceClient {
type MemoServiceServer (line 277) | type MemoServiceServer interface
type UnimplementedMemoServiceServer (line 323) | type UnimplementedMemoServiceServer struct
method CreateMemo (line 325) | func (UnimplementedMemoServiceServer) CreateMemo(context.Context, *Cre...
method ListMemos (line 328) | func (UnimplementedMemoServiceServer) ListMemos(context.Context, *List...
method GetMemo (line 331) | func (UnimplementedMemoServiceServer) GetMemo(context.Context, *GetMem...
method UpdateMemo (line 334) | func (UnimplementedMemoServiceServer) UpdateMemo(context.Context, *Upd...
method DeleteMemo (line 337) | func (UnimplementedMemoServiceServer) DeleteMemo(context.Context, *Del...
method SetMemoAttachments (line 340) | func (UnimplementedMemoServiceServer) SetMemoAttachments(context.Conte...
method ListMemoAttachments (line 343) | func (UnimplementedMemoServiceServer) ListMemoAttachments(context.Cont...
method SetMemoRelations (line 346) | func (UnimplementedMemoServiceServer) SetMemoRelations(context.Context...
method ListMemoRelations (line 349) | func (UnimplementedMemoServiceServer) ListMemoRelations(context.Contex...
method CreateMemoComment (line 352) | func (UnimplementedMemoServiceServer) CreateMemoComment(context.Contex...
method ListMemoComments (line 355) | func (UnimplementedMemoServiceServer) ListMemoComments(context.Context...
method ListMemoReactions (line 358) | func (UnimplementedMemoServiceServer) ListMemoReactions(context.Contex...
method UpsertMemoReaction (line 361) | func (UnimplementedMemoServiceServer) UpsertMemoReaction(context.Conte...
method DeleteMemoReaction (line 364) | func (UnimplementedMemoServiceServer) DeleteMemoReaction(context.Conte...
method CreateMemoShare (line 367) | func (UnimplementedMemoServiceServer) CreateMemoShare(context.Context,...
method ListMemoShares (line 370) | func (UnimplementedMemoServiceServer) ListMemoShares(context.Context, ...
method DeleteMemoShare (line 373) | func (UnimplementedMemoServiceServer) DeleteMemoShare(context.Context,...
method GetMemoByShare (line 376) | func (UnimplementedMemoServiceServer) GetMemoByShare(context.Context, ...
method mustEmbedUnimplementedMemoServiceServer (line 379) | func (UnimplementedMemoServiceServer) mustEmbedUnimplementedMemoServic...
method testEmbeddedByValue (line 380) | func (UnimplementedMemoServiceServer) testEmbeddedByValue() ...
type UnsafeMemoServiceServer (line 385) | type UnsafeMemoServiceServer interface
function RegisterMemoServiceServer (line 389) | func RegisterMemoServiceServer(s grpc.ServiceRegistrar, srv MemoServiceS...
function _MemoService_CreateMemo_Handler (line 400) | func _MemoService_CreateMemo_Handler(srv interface{}, ctx context.Contex...
function _MemoService_ListMemos_Handler (line 418) | func _MemoService_ListMemos_Handler(srv interface{}, ctx context.Context...
function _MemoService_GetMemo_Handler (line 436) | func _MemoService_GetMemo_Handler(srv interface{}, ctx context.Context, ...
function _MemoService_UpdateMemo_Handler (line 454) | func _MemoService_UpdateMemo_Handler(srv interface{}, ctx context.Contex...
function _MemoService_DeleteMemo_Handler (line 472) | func _MemoService_DeleteMemo_Handler(srv interface{}, ctx context.Contex...
function _MemoService_SetMemoAttachments_Handler (line 490) | func _MemoService_SetMemoAttachments_Handler(srv interface{}, ctx contex...
function _MemoService_ListMemoAttachments_Handler (line 508) | func _MemoService_ListMemoAttachments_Handler(srv interface{}, ctx conte...
function _MemoService_SetMemoRelations_Handler (line 526) | func _MemoService_SetMemoRelations_Handler(srv interface{}, ctx context....
function _MemoService_ListMemoRelations_Handler (line 544) | func _MemoService_ListMemoRelations_Handler(srv interface{}, ctx context...
function _MemoService_CreateMemoComment_Handler (line 562) | func _MemoService_CreateMemoComment_Handler(srv interface{}, ctx context...
function _MemoService_ListMemoComments_Handler (line 580) | func _MemoService_ListMemoComments_Handler(srv interface{}, ctx context....
function _MemoService_ListMemoReactions_Handler (line 598) | func _MemoService_ListMemoReactions_Handler(srv interface{}, ctx context...
function _MemoService_UpsertMemoReaction_Handler (line 616) | func _MemoService_UpsertMemoReaction_Handler(srv interface{}, ctx contex...
function _MemoService_DeleteMemoReaction_Handler (line 634) | func _MemoService_DeleteMemoReaction_Handler(srv interface{}, ctx contex...
function _MemoService_CreateMemoShare_Handler (line 652) | func _MemoService_CreateMemoShare_Handler(srv interface{}, ctx context.C...
function _MemoService_ListMemoShares_Handler (line 670) | func _MemoService_ListMemoShares_Handler(srv interface{}, ctx context.Co...
function _MemoService_DeleteMemoShare_Handler (line 688) | func _MemoService_DeleteMemoShare_Handler(srv interface{}, ctx context.C...
function _MemoService_GetMemoByShare_Handler (line 706) | func _MemoService_GetMemoByShare_Handler(srv interface{}, ctx context.Co...
FILE: proto/gen/api/v1/shortcut_service.pb.go
constant _ (line 22) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
constant _ (line 24) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
type Shortcut (line 27) | type Shortcut struct
method Reset (line 40) | func (x *Shortcut) Reset() {
method String (line 47) | func (x *Shortcut) String() string {
method ProtoMessage (line 51) | func (*Shortcut) ProtoMessage() {}
method ProtoReflect (line 53) | func (x *Shortcut) ProtoReflect() protoreflect.Message {
method Descriptor (line 66) | func (*Shortcut) Descriptor() ([]byte, []int) {
method GetName (line 70) | func (x *Shortcut) GetName() string {
method GetTitle (line 77) | func (x *Shortcut) GetTitle() string {
method GetFilter (line 84) | func (x *Shortcut) GetFilter() string {
type ListShortcutsRequest (line 91) | type ListShortcutsRequest struct
method Reset (line 100) | func (x *ListShortcutsRequest) Reset() {
method String (line 107) | func (x *ListShortcutsRequest) String() string {
method ProtoMessage (line 111) | func (*ListShortcutsRequest) ProtoMessage() {}
method ProtoReflect (line 113) | func (x *ListShortcutsRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 126) | func (*ListShortcutsRequest) Descriptor() ([]byte, []int) {
method GetParent (line 130) | func (x *ListShortcutsRequest) GetParent() string {
type ListShortcutsResponse (line 137) | type ListShortcutsResponse struct
method Reset (line 145) | func (x *ListShortcutsResponse) Reset() {
method String (line 152) | func (x *ListShortcutsResponse) String() string {
method ProtoMessage (line 156) | func (*ListShortcutsResponse) ProtoMessage() {}
method ProtoReflect (line 158) | func (x *ListShortcutsResponse) ProtoReflect() protoreflect.Message {
method Descriptor (line 171) | func (*ListShortcutsResponse) Descriptor() ([]byte, []int) {
method GetShortcuts (line 175) | func (x *ListShortcutsResponse) GetShortcuts() []*Shortcut {
type GetShortcutRequest (line 182) | type GetShortcutRequest struct
method Reset (line 191) | func (x *GetShortcutRequest) Reset() {
method String (line 198) | func (x *GetShortcutRequest) String() string {
method ProtoMessage (line 202) | func (*GetShortcutRequest) ProtoMessage() {}
method ProtoReflect (line 204) | func (x *GetShortcutRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 217) | func (*GetShortcutRequest) Descriptor() ([]byte, []int) {
method GetName (line 221) | func (x *GetShortcutRequest) GetName() string {
type CreateShortcutRequest (line 228) | type CreateShortcutRequest struct
method Reset (line 241) | func (x *CreateShortcutRequest) Reset() {
method String (line 248) | func (x *CreateShortcutRequest) String() string {
method ProtoMessage (line 252) | func (*CreateShortcutRequest) ProtoMessage() {}
method ProtoReflect (line 254) | func (x *CreateShortcutRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 267) | func (*CreateShortcutRequest) Descriptor() ([]byte, []int) {
method GetParent (line 271) | func (x *CreateShortcutRequest) GetParent() string {
method GetShortcut (line 278) | func (x *CreateShortcutRequest) GetShortcut() *Shortcut {
method GetValidateOnly (line 285) | func (x *CreateShortcutRequest) GetValidateOnly() bool {
type UpdateShortcutRequest (line 292) | type UpdateShortcutRequest struct
method Reset (line 302) | func (x *UpdateShortcutRequest) Reset() {
method String (line 309) | func (x *UpdateShortcutRequest) String() string {
method ProtoMessage (line 313) | func (*UpdateShortcutRequest) ProtoMessage() {}
method ProtoReflect (line 315) | func (x *UpdateShortcutRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 328) | func (*UpdateShortcutRequest) Descriptor() ([]byte, []int) {
method GetShortcut (line 332) | func (x *UpdateShortcutRequest) GetShortcut() *Shortcut {
method GetUpdateMask (line 339) | func (x *UpdateShortcutRequest) GetUpdateMask() *fieldmaskpb.FieldMask {
type DeleteShortcutRequest (line 346) | type DeleteShortcutRequest struct
method Reset (line 355) | func (x *DeleteShortcutRequest) Reset() {
method String (line 362) | func (x *DeleteShortcutRequest) String() string {
method ProtoMessage (line 366) | func (*DeleteShortcutRequest) ProtoMessage() {}
method ProtoReflect (line 368) | func (x *DeleteShortcutRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 381) | func (*DeleteShortcutRequest) Descriptor() ([]byte, []int) {
method GetName (line 385) | func (x *DeleteShortcutRequest) GetName() string {
constant file_api_v1_shortcut_service_proto_rawDesc (line 394) | file_api_v1_shortcut_service_proto_rawDesc = "" +
function file_api_v1_shortcut_service_proto_rawDescGZIP (line 433) | func file_api_v1_shortcut_service_proto_rawDescGZIP() []byte {
function init (line 474) | func init() { file_api_v1_shortcut_service_proto_init() }
function file_api_v1_shortcut_service_proto_init (line 475) | func file_api_v1_shortcut_service_proto_init() {
FILE: proto/gen/api/v1/shortcut_service.pb.gw.go
function request_ShortcutService_ListShortcuts_0 (line 38) | func request_ShortcutService_ListShortcuts_0(ctx context.Context, marsha...
function local_request_ShortcutService_ListShortcuts_0 (line 59) | func local_request_ShortcutService_ListShortcuts_0(ctx context.Context, ...
function request_ShortcutService_GetShortcut_0 (line 77) | func request_ShortcutService_GetShortcut_0(ctx context.Context, marshale...
function local_request_ShortcutService_GetShortcut_0 (line 98) | func local_request_ShortcutService_GetShortcut_0(ctx context.Context, ma...
function request_ShortcutService_CreateShortcut_0 (line 118) | func request_ShortcutService_CreateShortcut_0(ctx context.Context, marsh...
function local_request_ShortcutService_CreateShortcut_0 (line 148) | func local_request_ShortcutService_CreateShortcut_0(ctx context.Context,...
function request_ShortcutService_UpdateShortcut_0 (line 177) | func request_ShortcutService_UpdateShortcut_0(ctx context.Context, marsh...
function local_request_ShortcutService_UpdateShortcut_0 (line 218) | func local_request_ShortcutService_UpdateShortcut_0(ctx context.Context,...
function request_ShortcutService_DeleteShortcut_0 (line 256) | func request_ShortcutService_DeleteShortcut_0(ctx context.Context, marsh...
function local_request_ShortcutService_DeleteShortcut_0 (line 277) | func local_request_ShortcutService_DeleteShortcut_0(ctx context.Context,...
function RegisterShortcutServiceHandlerServer (line 300) | func RegisterShortcutServiceHandlerServer(ctx context.Context, mux *runt...
function RegisterShortcutServiceHandlerFromEndpoint (line 407) | func RegisterShortcutServiceHandlerFromEndpoint(ctx context.Context, mux...
function RegisterShortcutServiceHandler (line 431) | func RegisterShortcutServiceHandler(ctx context.Context, mux *runtime.Se...
function RegisterShortcutServiceHandlerClient (line 440) | func RegisterShortcutServiceHandlerClient(ctx context.Context, mux *runt...
FILE: proto/gen/api/v1/shortcut_service_grpc.pb.go
constant _ (line 20) | _ = grpc.SupportPackageIsVersion9
constant ShortcutService_ListShortcuts_FullMethodName (line 23) | ShortcutService_ListShortcuts_FullMethodName = "/memos.api.v1.ShortcutS...
constant ShortcutService_GetShortcut_FullMethodName (line 24) | ShortcutService_GetShortcut_FullMethodName = "/memos.api.v1.ShortcutS...
constant ShortcutService_CreateShortcut_FullMethodName (line 25) | ShortcutService_CreateShortcut_FullMethodName = "/memos.api.v1.ShortcutS...
constant ShortcutService_UpdateShortcut_FullMethodName (line 26) | ShortcutService_UpdateShortcut_FullMethodName = "/memos.api.v1.ShortcutS...
constant ShortcutService_DeleteShortcut_FullMethodName (line 27) | ShortcutService_DeleteShortcut_FullMethodName = "/memos.api.v1.ShortcutS...
type ShortcutServiceClient (line 33) | type ShortcutServiceClient interface
type shortcutServiceClient (line 46) | type shortcutServiceClient struct
method ListShortcuts (line 54) | func (c *shortcutServiceClient) ListShortcuts(ctx context.Context, in ...
method GetShortcut (line 64) | func (c *shortcutServiceClient) GetShortcut(ctx context.Context, in *G...
method CreateShortcut (line 74) | func (c *shortcutServiceClient) CreateShortcut(ctx context.Context, in...
method UpdateShortcut (line 84) | func (c *shortcutServiceClient) UpdateShortcut(ctx context.Context, in...
method DeleteShortcut (line 94) | func (c *shortcutServiceClient) DeleteShortcut(ctx context.Context, in...
function NewShortcutServiceClient (line 50) | func NewShortcutServiceClient(cc grpc.ClientConnInterface) ShortcutServi...
type ShortcutServiceServer (line 107) | type ShortcutServiceServer interface
type UnimplementedShortcutServiceServer (line 126) | type UnimplementedShortcutServiceServer struct
method ListShortcuts (line 128) | func (UnimplementedShortcutServiceServer) ListShortcuts(context.Contex...
method GetShortcut (line 131) | func (UnimplementedShortcutServiceServer) GetShortcut(context.Context,...
method CreateShortcut (line 134) | func (UnimplementedShortcutServiceServer) CreateShortcut(context.Conte...
method UpdateShortcut (line 137) | func (UnimplementedShortcutServiceServer) UpdateShortcut(context.Conte...
method DeleteShortcut (line 140) | func (UnimplementedShortcutServiceServer) DeleteShortcut(context.Conte...
method mustEmbedUnimplementedShortcutServiceServer (line 143) | func (UnimplementedShortcutServiceServer) mustEmbedUnimplementedShortc...
method testEmbeddedByValue (line 144) | func (UnimplementedShortcutServiceServer) testEmbeddedByValue() ...
type UnsafeShortcutServiceServer (line 149) | type UnsafeShortcutServiceServer interface
function RegisterShortcutServiceServer (line 153) | func RegisterShortcutServiceServer(s grpc.ServiceRegistrar, srv Shortcut...
function _ShortcutService_ListShortcuts_Handler (line 164) | func _ShortcutService_ListShortcuts_Handler(srv interface{}, ctx context...
function _ShortcutService_GetShortcut_Handler (line 182) | func _ShortcutService_GetShortcut_Handler(srv interface{}, ctx context.C...
function _ShortcutService_CreateShortcut_Handler (line 200) | func _ShortcutService_CreateShortcut_Handler(srv interface{}, ctx contex...
function _ShortcutService_UpdateShortcut_Handler (line 218) | func _ShortcutService_UpdateShortcut_Handler(srv interface{}, ctx contex...
function _ShortcutService_DeleteShortcut_Handler (line 236) | func _ShortcutService_DeleteShortcut_Handler(srv interface{}, ctx contex...
FILE: proto/gen/api/v1/user_service.pb.go
constant _ (line 23) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
constant _ (line 25) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
type User_Role (line 29) | type User_Role
method Enum (line 53) | func (x User_Role) Enum() *User_Role {
method String (line 59) | func (x User_Role) String() string {
method Descriptor (line 63) | func (User_Role) Descriptor() protoreflect.EnumDescriptor {
method Type (line 67) | func (User_Role) Type() protoreflect.EnumType {
method Number (line 71) | func (x User_Role) Number() protoreflect.EnumNumber {
method EnumDescriptor (line 76) | func (User_Role) EnumDescriptor() ([]byte, []int) {
constant User_ROLE_UNSPECIFIED (line 32) | User_ROLE_UNSPECIFIED User_Role = 0
constant User_ADMIN (line 34) | User_ADMIN User_Role = 2
constant User_USER (line 36) | User_USER User_Role = 3
type UserSetting_Key (line 81) | type UserSetting_Key
method Enum (line 105) | func (x UserSetting_Key) Enum() *UserSetting_Key {
method String (line 111) | func (x UserSetting_Key) String() string {
method Descriptor (line 115) | func (UserSetting_Key) Descriptor() protoreflect.EnumDescriptor {
method Type (line 119) | func (UserSetting_Key) Type() protoreflect.EnumType {
method Number (line 123) | func (x UserSetting_Key) Number() protoreflect.EnumNumber {
method EnumDescriptor (line 128) | func (UserSetting_Key) EnumDescriptor() ([]byte, []int) {
constant UserSetting_KEY_UNSPECIFIED (line 84) | UserSetting_KEY_UNSPECIFIED UserSetting_Key = 0
constant UserSetting_GENERAL (line 86) | UserSetting_GENERAL UserSetting_Key = 1
constant UserSetting_WEBHOOKS (line 88) | UserSetting_WEBHOOKS UserSetting_Key = 4
type UserNotification_Status (line 132) | type UserNotification_Status
method Enum (line 154) | func (x UserNotification_Status) Enum() *UserNotification_Status {
method String (line 160) | func (x UserNotification_Status) String() string {
method Descriptor (line 164) | func (UserNotification_Status) Descriptor() protoreflect.EnumDescriptor {
method Type (line 168) | func (UserNotification_Status) Type() protoreflect.EnumType {
method Number (line 172) | func (x UserNotification_Status) Number() protoreflect.EnumNumber {
method EnumDescriptor (line 177) | func (UserNotification_Status) EnumDescriptor() ([]byte, []int) {
constant UserNotification_STATUS_UNSPECIFIED (line 135) | UserNotification_STATUS_UNSPECIFIED UserNotification_Status = 0
constant UserNotification_UNREAD (line 136) | UserNotification_UNREAD UserNotification_Status = 1
constant UserNotification_ARCHIVED (line 137) | UserNotification_ARCHIVED UserNotification_Status = 2
type UserNotification_Type (line 181) | type UserNotification_Type
method Enum (line 200) | func (x UserNotification_Type) Enum() *UserNotification_Type {
method String (line 206) | func (x UserNotification_Type) String() string {
method Descriptor (line 210) | func (UserNotification_Type) Descriptor() protoreflect.EnumDescriptor {
method Type (line 214) | func (UserNotification_Type) Type() protoreflect.EnumType {
method Number (line 218) | func (x UserNotification_Type) Number() protoreflect.EnumNumber {
method EnumDescriptor (line 223) | func (UserNotification_Type) EnumDescriptor() ([]byte, []int) {
constant UserNotification_TYPE_UNSPECIFIED (line 184) | UserNotification_TYPE_UNSPECIFIED UserNotification_Type = 0
constant UserNotification_MEMO_COMMENT (line 185) | UserNotification_MEMO_COMMENT UserNotification_Type = 1
type User (line 227) | type User struct
method Reset (line 256) | func (x *User) Reset() {
method String (line 263) | func (x *User) String() string {
method ProtoMessage (line 267) | func (*User) ProtoMessage() {}
method ProtoReflect (line 269) | func (x *User) ProtoReflect() protoreflect.Message {
method Descriptor (line 282) | func (*User) Descriptor() ([]byte, []int) {
method GetName (line 286) | func (x *User) GetName() string {
method GetRole (line 293) | func (x *User) GetRole() User_Role {
method GetUsername (line 300) | func (x *User) GetUsername() string {
method GetEmail (line 307) | func (x *User) GetEmail() string {
method GetDisplayName (line 314) | func (x *User) GetDisplayName() string {
method GetAvatarUrl (line 321) | func (x *User) GetAvatarUrl() string {
method GetDescription (line 328) | func (x *User) GetDescription() string {
method GetPassword (line 335) | func (x *User) GetPassword() string {
method GetState (line 342) | func (x *User) GetState() State {
method GetCreateTime (line 349) | func (x *User) GetCreateTime() *timestamppb.Timestamp {
method GetUpdateTime (line 356) | func (x *User) GetUpdateTime() *timestamppb.Timestamp {
type ListUsersRequest (line 363) | type ListUsersRequest struct
method Reset (line 384) | func (x *ListUsersRequest) Reset() {
method String (line 391) | func (x *ListUsersRequest) String() string {
method ProtoMessage (line 395) | func (*ListUsersRequest) ProtoMessage() {}
method ProtoReflect (line 397) | func (x *ListUsersRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 410) | func (*ListUsersRequest) Descriptor() ([]byte, []int) {
method GetPageSize (line 414) | func (x *ListUsersRequest) GetPageSize() int32 {
method GetPageToken (line 421) | func (x *ListUsersRequest) GetPageToken() string {
method GetFilter (line 428) | func (x *ListUsersRequest) GetFilter() string {
method GetShowDeleted (line 435) | func (x *ListUsersRequest) GetShowDeleted() bool {
type ListUsersResponse (line 442) | type ListUsersResponse struct
method Reset (line 455) | func (x *ListUsersResponse) Reset() {
method String (line 462) | func (x *ListUsersResponse) String() string {
method ProtoMessage (line 466) | func (*ListUsersResponse) ProtoMessage() {}
method ProtoReflect (line 468) | func (x *ListUsersResponse) ProtoReflect() protoreflect.Message {
method Descriptor (line 481) | func (*ListUsersResponse) Descriptor() ([]byte, []int) {
method GetUsers (line 485) | func (x *ListUsersResponse) GetUsers() []*User {
method GetNextPageToken (line 492) | func (x *ListUsersResponse) GetNextPageToken() string {
method GetTotalSize (line 499) | func (x *ListUsersResponse) GetTotalSize() int32 {
type GetUserRequest (line 506) | type GetUserRequest struct
method Reset (line 522) | func (x *GetUserRequest) Reset() {
method String (line 529) | func (x *GetUserRequest) String() string {
method ProtoMessage (line 533) | func (*GetUserRequest) ProtoMessage() {}
method ProtoReflect (line 535) | func (x *GetUserRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 548) | func (*GetUserRequest) Descriptor() ([]byte, []int) {
method GetName (line 552) | func (x *GetUserRequest) GetName() string {
method GetReadMask (line 559) | func (x *GetUserRequest) GetReadMask() *fieldmaskpb.FieldMask {
type CreateUserRequest (line 566) | type CreateUserRequest struct
method Reset (line 583) | func (x *CreateUserRequest) Reset() {
method String (line 590) | func (x *CreateUserRequest) String() string {
method ProtoMessage (line 594) | func (*CreateUserRequest) ProtoMessage() {}
method ProtoReflect (line 596) | func (x *CreateUserRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 609) | func (*CreateUserRequest) Descriptor() ([]byte, []int) {
method GetUser (line 613) | func (x *CreateUserRequest) GetUser() *User {
method GetUserId (line 620) | func (x *CreateUserRequest) GetUserId() string {
method GetValidateOnly (line 627) | func (x *CreateUserRequest) GetValidateOnly() bool {
method GetRequestId (line 634) | func (x *CreateUserRequest) GetRequestId() string {
type UpdateUserRequest (line 641) | type UpdateUserRequest struct
method Reset (line 653) | func (x *UpdateUserRequest) Reset() {
method String (line 660) | func (x *UpdateUserRequest) String() string {
method ProtoMessage (line 664) | func (*UpdateUserRequest) ProtoMessage() {}
method ProtoReflect (line 666) | func (x *UpdateUserRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 679) | func (*UpdateUserRequest) Descriptor() ([]byte, []int) {
method GetUser (line 683) | func (x *UpdateUserRequest) GetUser() *User {
method GetUpdateMask (line 690) | func (x *UpdateUserRequest) GetUpdateMask() *fieldmaskpb.FieldMask {
method GetAllowMissing (line 697) | func (x *UpdateUserRequest) GetAllowMissing() bool {
type DeleteUserRequest (line 704) | type DeleteUserRequest struct
method Reset (line 715) | func (x *DeleteUserRequest) Reset() {
method String (line 722) | func (x *DeleteUserRequest) String() string {
method ProtoMessage (line 726) | func (*DeleteUserRequest) ProtoMessage() {}
method ProtoReflect (line 728) | func (x *DeleteUserRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 741) | func (*DeleteUserRequest) Descriptor() ([]byte, []int) {
method GetName (line 745) | func (x *DeleteUserRequest) GetName() string {
method GetForce (line 752) | func (x *DeleteUserRequest) GetForce() bool {
type UserStats (line 760) | type UserStats struct
method Reset (line 779) | func (x *UserStats) Reset() {
method String (line 786) | func (x *UserStats) String() string {
method ProtoMessage (line 790) | func (*UserStats) ProtoMessage() {}
method ProtoReflect (line 792) | func (x *UserStats) ProtoReflect() protoreflect.Message {
method Descriptor (line 805) | func (*UserStats) Descriptor() ([]byte, []int) {
method GetName (line 809) | func (x *UserStats) GetName() string {
method GetMemoDisplayTimestamps (line 816) | func (x *UserStats) GetMemoDisplayTimestamps() []*timestamppb.Timestamp {
method GetMemoTypeStats (line 823) | func (x *UserStats) GetMemoTypeStats() *UserStats_MemoTypeStats {
method GetTagCount (line 830) | func (x *UserStats) GetTagCount() map[string]int32 {
method GetPinnedMemos (line 837) | func (x *UserStats) GetPinnedMemos() []string {
method GetTotalMemoCount (line 844) | func (x *UserStats) GetTotalMemoCount() int32 {
type GetUserStatsRequest (line 851) | type GetUserStatsRequest struct
method Reset (line 860) | func (x *GetUserStatsRequest) Reset() {
method String (line 867) | func (x *GetUserStatsRequest) String() string {
method ProtoMessage (line 871) | func (*GetUserStatsRequest) ProtoMessage() {}
method ProtoReflect (line 873) | func (x *GetUserStatsRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 886) | func (*GetUserStatsRequest) Descriptor() ([]byte, []int) {
method GetName (line 890) | func (x *GetUserStatsRequest) GetName() string {
type ListAllUserStatsRequest (line 897) | type ListAllUserStatsRequest struct
method Reset (line 903) | func (x *ListAllUserStatsRequest) Reset() {
method String (line 910) | func (x *ListAllUserStatsRequest) String() string {
method ProtoMessage (line 914) | func (*ListAllUserStatsRequest) ProtoMessage() {}
method ProtoReflect (line 916) | func (x *ListAllUserStatsRequest) ProtoReflect() protoreflect.Message {
method Descriptor (line 929) | func (*ListAllUserStatsRequest) Descriptor() ([]byte, []int) {
type ListAllUserStatsResponse (line 933) | type ListAllUserStatsResponse struct
method Reset (line 941) | func (x *ListAllUserStatsResponse) Reset() {
method String (line 948) | func (x *ListAllUserStatsResponse) String() string {
method ProtoMessage (line 952) | func (*ListAllUserStatsResponse) ProtoMessage() {}
method ProtoReflect (line 954) | f
Condensed preview — 743 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,557K chars).
[
{
"path": ".dockerignore",
"chars": 117,
"preview": "web/node_modules\nweb/dist\n.git\n.github\nbuild/\ntmp/\nmemos\n*.md\n.gitignore\n.golangci.yaml\n.dockerignore\ndocs/\n.DS_Store"
},
{
"path": ".github/FUNDING.yml",
"chars": 17,
"preview": "github: usememos\n"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.yml",
"chars": 2783,
"preview": "name: Bug Report\ndescription: Something isn't working as expected\ntype: Bug\nbody:\n - type: markdown\n attributes:\n "
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 350,
"preview": "blank_issues_enabled: false\ncontact_links:\n - name: Questions & Support\n url: https://github.com/usememos/memos/disc"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.yml",
"chars": 2319,
"preview": "name: Feature Request\ndescription: Suggest a new feature or improvement\ntype: Feature\nbody:\n - type: markdown\n attri"
},
{
"path": ".github/workflows/backend-tests.yml",
"chars": 2388,
"preview": "name: Backend Tests\n\non:\n push:\n branches: [main]\n pull_request:\n branches: [main]\n paths:\n - \"go.mod\"\n "
},
{
"path": ".github/workflows/build-canary-image.yml",
"chars": 4754,
"preview": "name: Build Canary Image\n\non:\n push:\n branches: [main]\n\nconcurrency:\n group: ${{ github.workflow }}-${{ github.repo"
},
{
"path": ".github/workflows/demo-deploy.yml",
"chars": 414,
"preview": "name: Demo Deploy\n\non:\n workflow_dispatch:\n\njobs:\n deploy-demo:\n runs-on: ubuntu-latest\n steps:\n - name: Tr"
},
{
"path": ".github/workflows/frontend-tests.yml",
"chars": 1546,
"preview": "name: Frontend Tests\n\non:\n push:\n branches: [main]\n pull_request:\n branches: [main]\n paths:\n - \"web/**\"\n"
},
{
"path": ".github/workflows/proto-linter.yml",
"chars": 821,
"preview": "name: Proto Linter\n\non:\n push:\n branches: [main]\n pull_request:\n branches: [main]\n paths:\n - \"proto/**\"\n"
},
{
"path": ".github/workflows/release.yml",
"chars": 10236,
"preview": "name: Release\n\non:\n push:\n tags:\n - \"v*.*.*\"\n workflow_dispatch:\n\nconcurrency:\n group: ${{ github.workflow }}"
},
{
"path": ".github/workflows/stale.yml",
"chars": 658,
"preview": "name: Close Stale\n\non:\n schedule:\n - cron: \"0 */8 * * *\" # Every 8 hours\n\njobs:\n close-stale:\n name: Close Stale"
},
{
"path": ".gitignore",
"chars": 251,
"preview": "# temp folder\ntmp\n\n# Frontend asset\nweb/dist\n\n# Build artifacts\nbuild/\nbin/\nmemos\n\n# Plan/design documents\ndocs/plans/\n\n"
},
{
"path": ".golangci.yaml",
"chars": 2630,
"preview": "version: \"2\"\n\nlinters:\n enable:\n - revive\n - govet\n - staticcheck\n - misspell\n - gocritic\n - sqlclose"
},
{
"path": "AGENTS.md",
"chars": 5027,
"preview": "# AGENTS.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\nSel"
},
{
"path": "CLAUDE.md",
"chars": 194,
"preview": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\nSee"
},
{
"path": "CODEOWNERS",
"chars": 93,
"preview": "# These owners will be the default owners for everything in the repo.\n* @usememos/moderators\n"
},
{
"path": "LICENSE",
"chars": 1062,
"preview": "MIT License\n\nCopyright (c) 2025 Memos\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof t"
},
{
"path": "README.md",
"chars": 5132,
"preview": "<div align=\"center\">\n <p>\n <span>Featured Sponsor:</span>\n <a href=\"https://go.warp.dev/memos\" target=\"_blank\" re"
},
{
"path": "SECURITY.md",
"chars": 1513,
"preview": "# Security Policy\n\n## Project Status\n\nMemos is currently in beta (v0.x). While we take security seriously, we are not ye"
},
{
"path": "go.mod",
"chars": 6578,
"preview": "module github.com/usememos/memos\n\ngo 1.26.1\n\nrequire (\n\tconnectrpc.com/connect v1.19.1\n\tgithub.com/aws/aws-sdk-go-v2 v1."
},
{
"path": "go.sum",
"chars": 32496,
"preview": "cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=\ncel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ"
},
{
"path": "internal/base/resource_name.go",
"chars": 121,
"preview": "package base\n\nimport \"regexp\"\n\nvar (\n\tUIDMatcher = regexp.MustCompile(\"^[a-zA-Z0-9]([a-zA-Z0-9-]{0,30}[a-zA-Z0-9])?$\")\n)"
},
{
"path": "internal/base/resource_name_test.go",
"chars": 697,
"preview": "package base\n\nimport (\n\t\"testing\"\n)\n\nfunc TestUIDMatcher(t *testing.T) {\n\ttests := []struct {\n\t\tinput string\n\t\texpect"
},
{
"path": "internal/profile/profile.go",
"chars": 2960,
"preview": "package profile\n\nimport (\n\t\"fmt\"\n\t\"log/slog\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n)\n\n/"
},
{
"path": "internal/util/util.go",
"chars": 1746,
"preview": "package util //nolint:revive // util namespace is intentional for shared helpers\n\nimport (\n\t\"crypto/rand\"\n\t\"math/big\"\n\t\""
},
{
"path": "internal/util/util_test.go",
"chars": 551,
"preview": "package util //nolint:revive // util is an appropriate package name for utility functions\n\nimport (\n\t\"testing\"\n)\n\nfunc T"
},
{
"path": "internal/version/version.go",
"chars": 1487,
"preview": "package version\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"golang.org/x/mod/semver\"\n)\n\n// Version is the service current released ve"
},
{
"path": "internal/version/version_test.go",
"chars": 1920,
"preview": "package version\n\nimport (\n\t\"slices\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"golang.org/x/mod/semver\"\n)\n\nfunc"
},
{
"path": "plugin/cron/README.md",
"chars": 41,
"preview": "Fork from https://github.com/robfig/cron\n"
},
{
"path": "plugin/cron/chain.go",
"chars": 2176,
"preview": "package cron\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"runtime\"\n\t\"sync\"\n\t\"time\"\n)\n\n// JobWrapper decorates the given Job with some be"
},
{
"path": "plugin/cron/chain_test.go",
"chars": 6161,
"preview": "//nolint:all\npackage cron\n\nimport (\n\t\"io\"\n\t\"log\"\n\t\"reflect\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc waitFor(t *testing.T, tim"
},
{
"path": "plugin/cron/constantdelay.go",
"chars": 934,
"preview": "package cron\n\nimport \"time\"\n\n// ConstantDelaySchedule represents a simple recurring duty cycle, e.g. \"Every 5 minutes\".\n"
},
{
"path": "plugin/cron/constantdelay_test.go",
"chars": 1919,
"preview": "//nolint:all\npackage cron\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestConstantDelayNext(t *testing.T) {\n\ttests := []struct "
},
{
"path": "plugin/cron/cron.go",
"chars": 8593,
"preview": "package cron\n\nimport (\n\t\"context\"\n\t\"slices\"\n\t\"sync\"\n\t\"time\"\n)\n\n// Cron keeps track of any number of entries, invoking th"
},
{
"path": "plugin/cron/cron_test.go",
"chars": 16581,
"preview": "//nolint:all\npackage cron\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"log\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n)\n\n// Ma"
},
{
"path": "plugin/cron/logger.go",
"chars": 2552,
"preview": "package cron\n\nimport (\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\t\"time\"\n)\n\n// DefaultLogger is used by Cron if none is specified.\nv"
},
{
"path": "plugin/cron/option.go",
"chars": 1081,
"preview": "package cron\n\nimport (\n\t\"time\"\n)\n\n// Option represents a modification to the default behavior of a Cron.\ntype Option fun"
},
{
"path": "plugin/cron/option_test.go",
"chars": 880,
"preview": "//nolint:all\npackage cron\n\nimport (\n\t\"log\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestWithLocation(t *testing.T) {\n\tc := "
},
{
"path": "plugin/cron/parser.go",
"chars": 11165,
"preview": "package cron\n\nimport (\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n)\n\n// Configuration options for c"
},
{
"path": "plugin/cron/parser_test.go",
"chars": 10159,
"preview": "//nolint:all\npackage cron\n\nimport (\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\nvar secondParser = NewParser(Second | Mi"
},
{
"path": "plugin/cron/spec.go",
"chars": 4617,
"preview": "package cron\n\nimport \"time\"\n\n// SpecSchedule specifies a duty cycle (to the second granularity), based on a\n// tradition"
},
{
"path": "plugin/cron/spec_test.go",
"chars": 10999,
"preview": "//nolint:all\npackage cron\n\nimport (\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestActivation(t *testing.T) {\n\ttests := []str"
},
{
"path": "plugin/email/README.md",
"chars": 12274,
"preview": "# Email Plugin\n\nSMTP email sending functionality for self-hosted Memos instances.\n\n## Overview\n\nThis plugin provides a s"
},
{
"path": "plugin/email/client.go",
"chars": 3659,
"preview": "package email\n\nimport (\n\t\"crypto/tls\"\n\t\"net/smtp\"\n\n\t\"github.com/pkg/errors\"\n)\n\n// Client represents an SMTP email client"
},
{
"path": "plugin/email/client_test.go",
"chars": 2385,
"preview": "package email\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestNewClient(t *testing.T) {\n\tconfig "
},
{
"path": "plugin/email/config.go",
"chars": 1471,
"preview": "package email\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/pkg/errors\"\n)\n\n// Config represents the SMTP configuration for email sendin"
},
{
"path": "plugin/email/config_test.go",
"chars": 1542,
"preview": "package email\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestConfigValidation(t *testing.T) {\n\t"
},
{
"path": "plugin/email/doc.go",
"chars": 3003,
"preview": "// Package email provides SMTP email sending functionality for self-hosted Memos instances.\n//\n// This package is design"
},
{
"path": "plugin/email/email.go",
"chars": 1067,
"preview": "package email\n\nimport (\n\t\"log/slog\"\n\n\t\"github.com/pkg/errors\"\n)\n\n// Send sends an email synchronously.\n// Returns an err"
},
{
"path": "plugin/email/email_test.go",
"chars": 2739,
"preview": "package email\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\nfunc "
},
{
"path": "plugin/email/message.go",
"chars": 2277,
"preview": "package email\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n)\n\n// Message represents an email message to be sent.\ntype M"
},
{
"path": "plugin/email/message_test.go",
"chars": 4544,
"preview": "package email\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestMessageValidation(t *testing.T) {\n\ttests := []struct {\n\t\tname "
},
{
"path": "plugin/filter/MAINTENANCE.md",
"chars": 2223,
"preview": "# Maintaining the Memo Filter Engine\n\nThe engine is memo-specific; any future field or behavior changes must stay\nconsis"
},
{
"path": "plugin/filter/README.md",
"chars": 3378,
"preview": "# Memo Filter Engine\n\nThis package houses the memo-only filter engine that turns CEL expressions into\nSQL fragments. The"
},
{
"path": "plugin/filter/engine.go",
"chars": 4290,
"preview": "package filter\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/google/cel-go/cel\"\n\t\"github.com/pkg/errors\"\n"
},
{
"path": "plugin/filter/helpers.go",
"chars": 647,
"preview": "package filter\n\nimport (\n\t\"context\"\n\t\"fmt\"\n)\n\n// AppendConditions compiles the provided filters and appends the resultin"
},
{
"path": "plugin/filter/ir.go",
"chars": 3743,
"preview": "package filter\n\n// Condition represents a boolean expression derived from the CEL filter.\ntype Condition interface {\n\tis"
},
{
"path": "plugin/filter/parser.go",
"chars": 16639,
"preview": "package filter\n\nimport (\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\texprv1 \"google.golang.org/genproto/googleapis/api/expr/v1alp"
},
{
"path": "plugin/filter/render.go",
"chars": 22463,
"preview": "package filter\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n)\n\ntype renderer struct {\n\tschema Schem"
},
{
"path": "plugin/filter/schema.go",
"chars": 8904,
"preview": "package filter\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/google/cel-go/cel\"\n\t\"github.com/google/cel-go/common/types\"\n\t\"gith"
},
{
"path": "plugin/httpgetter/html_meta.go",
"chars": 3728,
"preview": "package httpgetter\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x/net/htm"
},
{
"path": "plugin/httpgetter/html_meta_test.go",
"chars": 748,
"preview": "package httpgetter\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestGetHTMLMeta(t *tes"
},
{
"path": "plugin/httpgetter/http_getter.go",
"chars": 19,
"preview": "package httpgetter\n"
},
{
"path": "plugin/httpgetter/image.go",
"chars": 713,
"preview": "package httpgetter\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n)\n\ntype Image struct {\n\tBlob []byte\n"
},
{
"path": "plugin/httpgetter/util.go",
"chars": 282,
"preview": "package httpgetter\n\nimport (\n\t\"mime\"\n\t\"net/http\"\n)\n\nfunc getMediatype(response *http.Response) (string, error) {\n\tconten"
},
{
"path": "plugin/idp/idp.go",
"chars": 134,
"preview": "package idp\n\ntype IdentityProviderUserInfo struct {\n\tIdentifier string\n\tDisplayName string\n\tEmail string\n\tAvatarU"
},
{
"path": "plugin/idp/oauth2/oauth2.go",
"chars": 4131,
"preview": "// Package oauth2 is the plugin for OAuth2 Identity Provider.\npackage oauth2\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt"
},
{
"path": "plugin/idp/oauth2/oauth2_test.go",
"chars": 4241,
"preview": "package oauth2\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"testing\""
},
{
"path": "plugin/markdown/ast/tag.go",
"chars": 547,
"preview": "package ast\n\nimport (\n\tgast \"github.com/yuin/goldmark/ast\"\n)\n\n// TagNode represents a #tag in the markdown AST.\ntype Tag"
},
{
"path": "plugin/markdown/extensions/tag.go",
"chars": 588,
"preview": "package extensions\n\nimport (\n\t\"github.com/yuin/goldmark\"\n\t\"github.com/yuin/goldmark/parser\"\n\t\"github.com/yuin/goldmark/u"
},
{
"path": "plugin/markdown/markdown.go",
"chars": 12035,
"preview": "package markdown\n\nimport (\n\t\"bytes\"\n\t\"strings\"\n\n\t\"github.com/yuin/goldmark\"\n\tgast \"github.com/yuin/goldmark/ast\"\n\t\"githu"
},
{
"path": "plugin/markdown/markdown_test.go",
"chars": 12925,
"preview": "package markdown\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nf"
},
{
"path": "plugin/markdown/parser/tag.go",
"chars": 3452,
"preview": "package parser\n\nimport (\n\t\"unicode\"\n\t\"unicode/utf8\"\n\n\tgast \"github.com/yuin/goldmark/ast\"\n\t\"github.com/yuin/goldmark/par"
},
{
"path": "plugin/markdown/parser/tag_test.go",
"chars": 5737,
"preview": "package parser\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"gith"
},
{
"path": "plugin/markdown/renderer/markdown_renderer.go",
"chars": 6440,
"preview": "package renderer\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"strings\"\n\n\tgast \"github.com/yuin/goldmark/ast\"\n\teast \"github.com/yuin/goldm"
},
{
"path": "plugin/markdown/renderer/markdown_renderer_test.go",
"chars": 4409,
"preview": "package renderer\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"gi"
},
{
"path": "plugin/scheduler/README.md",
"chars": 8386,
"preview": "# Scheduler Plugin\n\nA production-ready, GitHub Actions-inspired cron job scheduler for Go.\n\n## Features\n\n- **Standard Cr"
},
{
"path": "plugin/scheduler/doc.go",
"chars": 884,
"preview": "// Package scheduler provides a GitHub Actions-inspired cron job scheduler.\n//\n// Features:\n// - Standard cron express"
},
{
"path": "plugin/scheduler/example_test.go",
"chars": 3622,
"preview": "package scheduler_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/usememos/memos/plugin/schedul"
},
{
"path": "plugin/scheduler/integration_test.go",
"chars": 8609,
"preview": "package scheduler_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"git"
},
{
"path": "plugin/scheduler/job.go",
"chars": 1579,
"preview": "package scheduler\n\nimport (\n\t\"context\"\n\n\t\"github.com/pkg/errors\"\n)\n\n// JobHandler is the function signature for schedule"
},
{
"path": "plugin/scheduler/job_test.go",
"chars": 1685,
"preview": "package scheduler\n\nimport (\n\t\"context\"\n\t\"testing\"\n)\n\nfunc TestJobDefinition(t *testing.T) {\n\tcallCount := 0\n\tjob := &Job"
},
{
"path": "plugin/scheduler/middleware.go",
"chars": 2998,
"preview": "package scheduler\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n)\n\n// Middleware wraps a JobHandler to add cros"
},
{
"path": "plugin/scheduler/middleware_test.go",
"chars": 3115,
"preview": "package scheduler\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"sync/atomic\"\n\t\"testing\"\n)\n\nfunc TestMiddlewareChaining(t *testing.T) "
},
{
"path": "plugin/scheduler/parser.go",
"chars": 5706,
"preview": "package scheduler\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n)\n\n// Schedule represents a parsed c"
},
{
"path": "plugin/scheduler/parser_test.go",
"chars": 3571,
"preview": "package scheduler\n\nimport (\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestParseCronExpression(t *testing.T) {\n\ttests := []struct {\n\t\tna"
},
{
"path": "plugin/scheduler/scheduler.go",
"chars": 4274,
"preview": "package scheduler\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n)\n\n// Scheduler manages scheduled jobs."
},
{
"path": "plugin/scheduler/scheduler_test.go",
"chars": 3565,
"preview": "package scheduler\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestScheduler"
},
{
"path": "plugin/storage/s3/s3.go",
"chars": 3365,
"preview": "package s3\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"time\"\n\n\t\"github.com/aws/aws-sdk-go-v2/aws\"\n\t\"github.com/aws/aws-sdk-go-v2/config"
},
{
"path": "plugin/webhook/validate.go",
"chars": 2555,
"preview": "package webhook\n\nimport (\n\t\"net\"\n\t\"net/url\"\n\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n)\n\n// rese"
},
{
"path": "plugin/webhook/webhook.go",
"chars": 3693,
"preview": "package webhook\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"log/slog\"\n\t\"net\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.co"
},
{
"path": "plugin/webhook/webhook_test.go",
"chars": 16,
"preview": "package webhook\n"
},
{
"path": "proto/README.md",
"chars": 145,
"preview": "# Guide\n\n## Prerequisites\n\n- [buf](https://docs.buf.build/installation)\n\n## Generate\n\n```sh\nbuf generate\n```\n\n## Format\n"
},
{
"path": "proto/api/v1/README.md",
"chars": 168,
"preview": "# Memos API Design\n\nThis API design should follow the guidelines and best practices outlined in the [Google API Improvem"
},
{
"path": "proto/api/v1/attachment_service.proto",
"chars": 5402,
"preview": "syntax = \"proto3\";\n\npackage memos.api.v1;\n\nimport \"google/api/annotations.proto\";\nimport \"google/api/client.proto\";\nimpo"
},
{
"path": "proto/api/v1/auth_service.proto",
"chars": 3580,
"preview": "syntax = \"proto3\";\n\npackage memos.api.v1;\n\nimport \"api/v1/user_service.proto\";\nimport \"google/api/annotations.proto\";\nim"
},
{
"path": "proto/api/v1/common.proto",
"chars": 333,
"preview": "syntax = \"proto3\";\n\npackage memos.api.v1;\n\noption go_package = \"gen/api/v1\";\n\nenum State {\n STATE_UNSPECIFIED = 0;\n NO"
},
{
"path": "proto/api/v1/idp_service.proto",
"chars": 4796,
"preview": "syntax = \"proto3\";\n\npackage memos.api.v1;\n\nimport \"google/api/annotations.proto\";\nimport \"google/api/client.proto\";\nimpo"
},
{
"path": "proto/api/v1/instance_service.proto",
"chars": 6797,
"preview": "syntax = \"proto3\";\n\npackage memos.api.v1;\n\nimport \"api/v1/user_service.proto\";\nimport \"google/api/annotations.proto\";\nim"
},
{
"path": "proto/api/v1/memo_service.proto",
"chars": 20042,
"preview": "syntax = \"proto3\";\n\npackage memos.api.v1;\n\nimport \"api/v1/attachment_service.proto\";\nimport \"api/v1/common.proto\";\nimpor"
},
{
"path": "proto/api/v1/shortcut_service.proto",
"chars": 4041,
"preview": "syntax = \"proto3\";\n\npackage memos.api.v1;\n\nimport \"google/api/annotations.proto\";\nimport \"google/api/client.proto\";\nimpo"
},
{
"path": "proto/api/v1/user_service.proto",
"chars": 22581,
"preview": "syntax = \"proto3\";\n\npackage memos.api.v1;\n\nimport \"api/v1/common.proto\";\nimport \"google/api/annotations.proto\";\nimport \""
},
{
"path": "proto/buf.gen.yaml",
"chars": 772,
"preview": "version: v2\nmanaged:\n enabled: true\n disable:\n - file_option: go_package\n module: buf.build/googleapis/googlea"
},
{
"path": "proto/buf.yaml",
"chars": 349,
"preview": "version: v2\ndeps:\n - buf.build/googleapis/googleapis\nlint:\n use:\n - BASIC\n except:\n - ENUM_VALUE_PREFIX\n - F"
},
{
"path": "proto/gen/api/v1/apiv1connect/attachment_service.connect.go",
"chars": 12918,
"preview": "// Code generated by protoc-gen-connect-go. DO NOT EDIT.\n//\n// Source: api/v1/attachment_service.proto\n\npackage apiv1con"
},
{
"path": "proto/gen/api/v1/apiv1connect/auth_service.connect.go",
"chars": 10921,
"preview": "// Code generated by protoc-gen-connect-go. DO NOT EDIT.\n//\n// Source: api/v1/auth_service.proto\n\npackage apiv1connect\n\n"
},
{
"path": "proto/gen/api/v1/apiv1connect/idp_service.connect.go",
"chars": 14580,
"preview": "// Code generated by protoc-gen-connect-go. DO NOT EDIT.\n//\n// Source: api/v1/idp_service.proto\n\npackage apiv1connect\n\ni"
},
{
"path": "proto/gen/api/v1/apiv1connect/instance_service.connect.go",
"chars": 9224,
"preview": "// Code generated by protoc-gen-connect-go. DO NOT EDIT.\n//\n// Source: api/v1/instance_service.proto\n\npackage apiv1conne"
},
{
"path": "proto/gen/api/v1/apiv1connect/memo_service.connect.go",
"chars": 35618,
"preview": "// Code generated by protoc-gen-connect-go. DO NOT EDIT.\n//\n// Source: api/v1/memo_service.proto\n\npackage apiv1connect\n\n"
},
{
"path": "proto/gen/api/v1/apiv1connect/shortcut_service.connect.go",
"chars": 12414,
"preview": "// Code generated by protoc-gen-connect-go. DO NOT EDIT.\n//\n// Source: api/v1/shortcut_service.proto\n\npackage apiv1conne"
},
{
"path": "proto/gen/api/v1/apiv1connect/user_service.connect.go",
"chars": 41025,
"preview": "// Code generated by protoc-gen-connect-go. DO NOT EDIT.\n//\n// Source: api/v1/user_service.proto\n\npackage apiv1connect\n\n"
},
{
"path": "proto/gen/api/v1/attachment_service.pb.go",
"chars": 22229,
"preview": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.36.11\n// \tprotoc (unknown)\n// "
},
{
"path": "proto/gen/api/v1/attachment_service.pb.gw.go",
"chars": 26816,
"preview": "// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.\n// source: api/v1/attachment_service.proto\n\n/*\nPackage apiv1 "
},
{
"path": "proto/gen/api/v1/attachment_service_grpc.pb.go",
"chars": 12243,
"preview": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n// versions:\n// - protoc-gen-go-grpc v1.6.1\n// - protoc "
},
{
"path": "proto/gen/api/v1/auth_service.pb.go",
"chars": 21921,
"preview": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.36.11\n// \tprotoc (unknown)\n// "
},
{
"path": "proto/gen/api/v1/auth_service.pb.gw.go",
"chars": 17002,
"preview": "// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.\n// source: api/v1/auth_service.proto\n\n/*\nPackage apiv1 is a r"
},
{
"path": "proto/gen/api/v1/auth_service_grpc.pb.go",
"chars": 10589,
"preview": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n// versions:\n// - protoc-gen-go-grpc v1.6.1\n// - protoc "
},
{
"path": "proto/gen/api/v1/common.pb.go",
"chars": 6769,
"preview": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.36.11\n// \tprotoc (unknown)\n// "
},
{
"path": "proto/gen/api/v1/idp_service.pb.go",
"chars": 28987,
"preview": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.36.11\n// \tprotoc (unknown)\n// "
},
{
"path": "proto/gen/api/v1/idp_service.pb.gw.go",
"chars": 27381,
"preview": "// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.\n// source: api/v1/idp_service.proto\n\n/*\nPackage apiv1 is a re"
},
{
"path": "proto/gen/api/v1/idp_service_grpc.pb.go",
"chars": 13482,
"preview": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n// versions:\n// - protoc-gen-go-grpc v1.6.1\n// - protoc "
},
{
"path": "proto/gen/api/v1/instance_service.pb.go",
"chars": 50605,
"preview": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.36.11\n// \tprotoc (unknown)\n// "
},
{
"path": "proto/gen/api/v1/instance_service.pb.gw.go",
"chars": 17423,
"preview": "// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.\n// source: api/v1/instance_service.proto\n\n/*\nPackage apiv1 is"
},
{
"path": "proto/gen/api/v1/instance_service_grpc.pb.go",
"chars": 8598,
"preview": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n// versions:\n// - protoc-gen-go-grpc v1.6.1\n// - protoc "
},
{
"path": "proto/gen/api/v1/memo_service.pb.go",
"chars": 84228,
"preview": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.36.11\n// \tprotoc (unknown)\n// "
},
{
"path": "proto/gen/api/v1/memo_service.pb.gw.go",
"chars": 85260,
"preview": "// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.\n// source: api/v1/memo_service.proto\n\n/*\nPackage apiv1 is a r"
},
{
"path": "proto/gen/api/v1/memo_service_grpc.pb.go",
"chars": 34773,
"preview": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n// versions:\n// - protoc-gen-go-grpc v1.6.1\n// - protoc "
},
{
"path": "proto/gen/api/v1/shortcut_service.pb.go",
"chars": 17904,
"preview": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.36.11\n// \tprotoc (unknown)\n// "
},
{
"path": "proto/gen/api/v1/shortcut_service.pb.gw.go",
"chars": 27268,
"preview": "// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.\n// source: api/v1/shortcut_service.proto\n\n/*\nPackage apiv1 is"
},
{
"path": "proto/gen/api/v1/shortcut_service_grpc.pb.go",
"chars": 11873,
"preview": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n// versions:\n// - protoc-gen-go-grpc v1.6.1\n// - protoc "
},
{
"path": "proto/gen/api/v1/user_service.pb.go",
"chars": 104079,
"preview": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.36.11\n// \tprotoc (unknown)\n// "
},
{
"path": "proto/gen/api/v1/user_service.pb.gw.go",
"chars": 98534,
"preview": "// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.\n// source: api/v1/user_service.proto\n\n/*\nPackage apiv1 is a r"
},
{
"path": "proto/gen/api/v1/user_service_grpc.pb.go",
"chars": 39744,
"preview": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n// versions:\n// - protoc-gen-go-grpc v1.6.1\n// - protoc "
},
{
"path": "proto/gen/openapi.yaml",
"chars": 129653,
"preview": "# Generated with protoc-gen-openapi\n# https://github.com/google/gnostic/tree/master/cmd/protoc-gen-openapi\n\nopenapi: 3.0"
},
{
"path": "proto/gen/store/attachment.pb.go",
"chars": 9769,
"preview": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.36.11\n// \tprotoc (unknown)\n// "
},
{
"path": "proto/gen/store/idp.pb.go",
"chars": 14501,
"preview": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.36.11\n// \tprotoc (unknown)\n// "
},
{
"path": "proto/gen/store/inbox.pb.go",
"chars": 8411,
"preview": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.36.11\n// \tprotoc (unknown)\n// "
},
{
"path": "proto/gen/store/instance_setting.pb.go",
"chars": 41267,
"preview": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.36.11\n// \tprotoc (unknown)\n// "
},
{
"path": "proto/gen/store/memo.pb.go",
"chars": 9199,
"preview": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.36.11\n// \tprotoc (unknown)\n// "
},
{
"path": "proto/gen/store/user_setting.pb.go",
"chars": 33828,
"preview": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.36.11\n// \tprotoc (unknown)\n// "
},
{
"path": "proto/store/attachment.proto",
"chars": 827,
"preview": "syntax = \"proto3\";\n\npackage memos.store;\n\nimport \"google/protobuf/timestamp.proto\";\nimport \"store/instance_setting.proto"
},
{
"path": "proto/store/idp.proto",
"chars": 738,
"preview": "syntax = \"proto3\";\n\npackage memos.store;\n\noption go_package = \"gen/store\";\n\nmessage IdentityProvider {\n int32 id = 1;\n "
},
{
"path": "proto/store/inbox.proto",
"chars": 408,
"preview": "syntax = \"proto3\";\n\npackage memos.store;\n\noption go_package = \"gen/store\";\n\nmessage InboxMessage {\n message MemoComment"
},
{
"path": "proto/store/instance_setting.proto",
"chars": 3975,
"preview": "syntax = \"proto3\";\n\npackage memos.store;\n\nimport \"google/type/color.proto\";\n\noption go_package = \"gen/store\";\n\nenum Inst"
},
{
"path": "proto/store/memo.proto",
"chars": 562,
"preview": "syntax = \"proto3\";\n\npackage memos.store;\n\noption go_package = \"gen/store\";\n\nmessage MemoPayload {\n Property property = "
},
{
"path": "proto/store/user_setting.proto",
"chars": 2938,
"preview": "syntax = \"proto3\";\n\npackage memos.store;\n\nimport \"google/protobuf/timestamp.proto\";\n\noption go_package = \"gen/store\";\n\nm"
},
{
"path": "scripts/Dockerfile",
"chars": 1788,
"preview": "FROM --platform=$BUILDPLATFORM golang:1.26.1-alpine AS backend\nWORKDIR /backend-build\n\n# Install build dependencies\nRUN "
},
{
"path": "scripts/build.sh",
"chars": 630,
"preview": "#!/bin/sh\n\nset -e\n\n# Change to repo root\ncd \"$(dirname \"$0\")/../\"\n\nOS=$(uname -s)\n\n# Determine output binary name\ncase \""
},
{
"path": "scripts/compose.yaml",
"chars": 153,
"preview": "services:\n memos:\n image: neosmemo/memos:stable\n container_name: memos\n volumes:\n - ~/.memos/:/var/opt/me"
},
{
"path": "scripts/entrypoint.sh",
"chars": 1097,
"preview": "#!/usr/bin/env sh\n\n# Fix ownership of data directory for users upgrading from older versions\n# where files were created "
},
{
"path": "scripts/entrypoint_test.sh",
"chars": 3701,
"preview": "#!/usr/bin/env sh\n\n# Test script for entrypoint.sh file_env function\n# Run: ./scripts/entrypoint_test.sh\n\nset -e\n\nSCRIPT"
},
{
"path": "scripts/install.sh",
"chars": 6624,
"preview": "#!/bin/sh\n\nset -eu\n\nREPO=\"${REPO:-usememos/memos}\"\nBIN_NAME=\"memos\"\nVERSION=\"${MEMOS_VERSION:-}\"\nINSTALL_DIR=\"${MEMOS_IN"
},
{
"path": "server/auth/authenticator.go",
"chars": 6216,
"preview": "package auth\n\nimport (\n\t\"context\"\n\t\"log/slog\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\t\"google.golang.org/protobuf/"
},
{
"path": "server/auth/context.go",
"chars": 3082,
"preview": "package auth\n\nimport (\n\t\"context\"\n\n\t\"github.com/usememos/memos/store\"\n)\n\n// ContextKey is the key type for context value"
},
{
"path": "server/auth/extract.go",
"chars": 820,
"preview": "package auth\n\nimport (\n\t\"net/http\"\n\t\"strings\"\n)\n\n// ExtractBearerToken extracts the JWT token from an Authorization head"
},
{
"path": "server/auth/token.go",
"chars": 8225,
"preview": "// Package auth provides authentication and authorization for the Memos server.\n//\n// This package is used by:\n// - serv"
},
{
"path": "server/auth/token_test.go",
"chars": 9766,
"preview": "package auth\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n"
},
{
"path": "server/router/api/v1/acl_config.go",
"chars": 1985,
"preview": "package v1\n\n// PublicMethods defines API endpoints that don't require authentication.\n// All other endpoints require a v"
},
{
"path": "server/router/api/v1/acl_config_test.go",
"chars": 2867,
"preview": "package v1\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\n// TestPublicMethodsArePublic verifies that me"
},
{
"path": "server/router/api/v1/attachment_exif_test.go",
"chars": 4521,
"preview": "package v1\n\nimport (\n\t\"bytes\"\n\t\"image\"\n\t\"image/color\"\n\t\"image/jpeg\"\n\t\"testing\"\n\n\t\"github.com/disintegration/imaging\"\n\t\"g"
},
{
"path": "server/router/api/v1/attachment_service.go",
"chars": 22180,
"preview": "package v1\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io\"\n\t\"log/slog\"\n\t\"mime\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/fi"
},
{
"path": "server/router/api/v1/auth_service.go",
"chars": 23100,
"preview": "package v1\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"regexp\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\t\"golang.org/x"
},
{
"path": "server/router/api/v1/auth_service_client_info_test.go",
"chars": 6143,
"preview": "package v1\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"google.golang.org/grpc/metadata\"\n\n\tstorepb \"github.com/usememos/memos/prot"
},
{
"path": "server/router/api/v1/common.go",
"chars": 1568,
"preview": "package v1\n\nimport (\n\t\"encoding/base64\"\n\n\t\"github.com/pkg/errors\"\n\t\"google.golang.org/protobuf/proto\"\n\n\tv1pb \"github.com"
},
{
"path": "server/router/api/v1/connect_handler.go",
"chars": 2554,
"preview": "package v1\n\nimport (\n\t\"net/http\"\n\n\t\"connectrpc.com/connect\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/sta"
},
{
"path": "server/router/api/v1/connect_interceptors.go",
"chars": 7408,
"preview": "package v1\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"reflect\"\n\t\"runtime/debug\"\n\n\t\"connectrpc.com/connect\"\n\tpkg"
},
{
"path": "server/router/api/v1/connect_services.go",
"chars": 20452,
"preview": "package v1\n\nimport (\n\t\"context\"\n\n\t\"connectrpc.com/connect\"\n\t\"google.golang.org/protobuf/types/known/emptypb\"\n\n\tv1pb \"git"
},
{
"path": "server/router/api/v1/header_carrier.go",
"chars": 4194,
"preview": "package v1\n\nimport (\n\t\"context\"\n\n\t\"connectrpc.com/connect\"\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/metadata\"\n"
},
{
"path": "server/router/api/v1/health_service.go",
"chars": 845,
"preview": "package v1\n\nimport (\n\t\"context\"\n\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/health/grpc_health_v1\"\n\t\"googl"
},
{
"path": "server/router/api/v1/idp_service.go",
"chars": 9763,
"preview": "package v1\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n\t\"google.golang"
},
{
"path": "server/router/api/v1/instance_service.go",
"chars": 16030,
"preview": "package v1\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n\tcolorpb \"google.golang.org/genproto"
},
{
"path": "server/router/api/v1/memo_attachment_service.go",
"chars": 4498,
"preview": "package v1\n\nimport (\n\t\"context\"\n\t\"slices\"\n\t\"time\"\n\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n\t\"go"
},
{
"path": "server/router/api/v1/memo_relation_service.go",
"chars": 6163,
"preview": "package v1\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/pkg/errors\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grp"
},
{
"path": "server/router/api/v1/memo_service.go",
"chars": 30443,
"preview": "package v1\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log/slog\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\t\"google.golang.org/grpc"
},
{
"path": "server/router/api/v1/memo_service_converter.go",
"chars": 7515,
"preview": "package v1\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/pkg/errors\"\n\t\"google.golang.org/protobuf/types/known/timest"
},
{
"path": "server/router/api/v1/memo_service_filter.go",
"chars": 11,
"preview": "package v1\n"
},
{
"path": "server/router/api/v1/memo_share_service.go",
"chars": 7725,
"preview": "package v1\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n\t\"googl"
},
{
"path": "server/router/api/v1/reaction_service.go",
"chars": 5408,
"preview": "package v1\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"google.golang.org/grpc/codes\"\n\t\"google.golang.org/grpc/status\"\n\t\"googl"
},
{
"path": "server/router/api/v1/resource_name.go",
"chars": 4992,
"preview": "package v1\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/lithammer/shortuuid/v4\"\n\t\"github.com/pkg/errors\"\n\t\"google.golang.or"
},
{
"path": "server/router/api/v1/shortcut_service.go",
"chars": 10777,
"preview": "package v1\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/pkg/errors\"\n\t\"google.golang.org/grpc/codes\"\n\t\"google.gol"
},
{
"path": "server/router/api/v1/sse_handler.go",
"chars": 2447,
"preview": "package v1\n\nimport (\n\t\"fmt\"\n\t\"log/slog\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/labstack/echo/v5\"\n\n\t\"github.com/usememos/memos"
},
{
"path": "server/router/api/v1/sse_hub.go",
"chars": 2473,
"preview": "package v1\n\nimport (\n\t\"encoding/json\"\n\t\"log/slog\"\n\t\"sync\"\n)\n\n// SSEEventType represents the type of change event.\ntype S"
},
{
"path": "server/router/api/v1/sse_hub_test.go",
"chars": 1766,
"preview": "package v1\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n"
},
{
"path": "server/router/api/v1/test/attachment_service_test.go",
"chars": 1837,
"preview": "package test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\tv1pb \"github.com/usememos/memos/p"
},
{
"path": "server/router/api/v1/test/auth_test.go",
"chars": 19282,
"preview": "package test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testif"
},
{
"path": "server/router/api/v1/test/idp_service_test.go",
"chars": 16169,
"preview": "package test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"google.golang.org/protobuf/types/"
},
{
"path": "server/router/api/v1/test/instance_admin_cache_test.go",
"chars": 1661,
"preview": "package test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\tv1pb \"github.com/usememos/memos/p"
},
{
"path": "server/router/api/v1/test/instance_service_test.go",
"chars": 10148,
"preview": "package test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\tcolorpb \"google.golang.org/genprot"
},
{
"path": "server/router/api/v1/test/memo_attachment_service_test.go",
"chars": 4594,
"preview": "package test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\tapiv1 \"github.com/usememos/memos/"
},
{
"path": "server/router/api/v1/test/memo_relation_service_test.go",
"chars": 4583,
"preview": "package test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\tapiv1 \"github.com/usememos/memos/"
},
{
"path": "server/router/api/v1/test/memo_service_test.go",
"chars": 13995,
"preview": "package test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"slices\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"google.g"
},
{
"path": "server/router/api/v1/test/memo_share_service_test.go",
"chars": 2965,
"preview": "package test\n\nimport (\n\t\"context\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"google.golang.org/grpc"
},
{
"path": "server/router/api/v1/test/reaction_service_test.go",
"chars": 5436,
"preview": "package test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\tapiv1 \"github.com/usememos/memos/"
},
{
"path": "server/router/api/v1/test/shortcut_service_test.go",
"chars": 22449,
"preview": "package test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\t\"google.golang.org/protobuf"
},
{
"path": "server/router/api/v1/test/sse_handler_test.go",
"chars": 2305,
"preview": "package test\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n\n\t\"github.com/labstack/echo/v5\"\n\t\"github.c"
},
{
"path": "server/router/api/v1/test/test_helper.go",
"chars": 2560,
"preview": "package test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/usememos/memos/internal/profile\"\n\t\"github.com/usememos/memos/"
},
{
"path": "server/router/api/v1/test/user_notification_test.go",
"chars": 4307,
"preview": "package test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\tapiv1 \"github.com/usememos"
},
{
"path": "server/router/api/v1/test/user_service_registration_test.go",
"chars": 5162,
"preview": "package test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\tapiv1 \"github.com/usememos/memos/"
},
{
"path": "server/router/api/v1/test/user_service_stats_test.go",
"chars": 2987,
"preview": "package test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n\n\tv1pb \"github.com/usememos/"
}
]
// ... and 543 more files (download for full content)
About this extraction
This page contains the full source code of the usememos/memos GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 743 files (4.0 MB), approximately 1.1M tokens, and a symbol index with 4675 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.