diff --git a/.gitignore b/.gitignore index e512e7ab7..e15372555 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,9 @@ tmp # Frontend asset web/dist -# build folder -build +# Build artifacts +build/ +bin/ .DS_Store diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 016dd0e66..16d64be9a 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -6,7 +6,7 @@ before: - go mod tidy builds: - - main: ./bin/memos + - main: ./cmd/memos binary: memos goos: - linux diff --git a/AGENTS.md b/AGENTS.md index 12afb6c7e..355a6b769 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,11 +1,11 @@ # Repository Guidelines ## Project Structure & Module Organization -Memos pairs a Go backend with a Vite React client. The CLI entry in `bin/memos` boots the HTTP server under `server`, backed by shared domain logic in `internal` and persistence adapters in `store`. Frontend code lives in `web/src` with static assets in `web/public`; `pnpm release` publishes bundles into `server/router/frontend/dist`. API schemas sit in `proto/` (Buf-managed), extensions in `plugin/`, deployment helpers in `scripts/`, and sample SQLite databases in `build/`. +Memos pairs a Go backend with a Vite React client. The CLI entry in `cmd/memos` boots the HTTP server under `server`, backed by shared domain logic in `internal` and persistence adapters in `store`. Frontend code lives in `web/src` with static assets in `web/public`; `pnpm release` publishes bundles into `server/router/frontend/dist`. API schemas sit in `proto/` (Buf-managed), extensions in `plugin/`, deployment helpers in `scripts/`, and sample SQLite databases in `build/`. ## Build, Test, and Development Commands -- `go run ./bin/memos --mode dev --port 8081` – start the backend with the default SQLite store. -- `go build ./bin/memos` – compile the backend binary. +- `go run ./cmd/memos --mode dev --port 8081` – start the backend with the default SQLite store. +- `go build ./cmd/memos` – compile the backend binary. - `go test ./...` – run Go unit and store tests. - `cd web && pnpm install` – install frontend dependencies. - `cd web && pnpm dev` – start the Vite dev server with hot reload. diff --git a/bin/memos/main.go b/cmd/memos/main.go similarity index 100% rename from bin/memos/main.go rename to cmd/memos/main.go diff --git a/plugin/filter/parser.go b/plugin/filter/parser.go index 5af924c09..76bb1630b 100644 --- a/plugin/filter/parser.go +++ b/plugin/filter/parser.go @@ -278,6 +278,8 @@ func buildValueExpr(expr *exprv1.Expr, schema Schema) (ValueExpr, error) { if ok { return &LiteralValue{Value: value}, nil } + default: + // Fall through to error return below } } @@ -402,10 +404,12 @@ func evaluateNumeric(expr *exprv1.Expr) (int64, bool, error) { return left - right, true, nil case "_*_": return left * right, true, nil + default: + return 0, false, errors.Errorf("unsupported arithmetic operator %q", call.Function) } + default: + return 0, false, nil } - - return 0, false, nil } func timeNowUnix() int64 { diff --git a/scripts/Dockerfile b/scripts/Dockerfile index 38456b37e..451b2d91a 100644 --- a/scripts/Dockerfile +++ b/scripts/Dockerfile @@ -7,7 +7,7 @@ COPY . . # Refer to `pnpm release` in package.json for the build command. RUN --mount=type=cache,target=/go/pkg/mod \ --mount=type=cache,target=/root/.cache/go-build \ - go build -ldflags="-s -w" -o memos ./bin/memos/main.go + go build -ldflags="-s -w" -o memos ./cmd/memos # Make workspace with above generated files. FROM alpine:latest AS monolithic diff --git a/scripts/build.sh b/scripts/build.sh index 832813342..ef687d061 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -25,7 +25,7 @@ export GOCACHE="$(pwd)/build/.gocache" export GOMODCACHE="$(pwd)/build/.gomodcache" # Build the executable -go build -o "$OUTPUT" ./bin/memos +go build -o "$OUTPUT" ./cmd/memos echo "Build successful!" echo "To run the application, execute the following command:" diff --git a/server/router/api/v1/auth_service.go b/server/router/api/v1/auth_service.go index be34a2699..5380bbfcb 100644 --- a/server/router/api/v1/auth_service.go +++ b/server/router/api/v1/auth_service.go @@ -359,14 +359,12 @@ func (*APIV1Service) parseUserAgent(userAgent string, clientInfo *storepb.Sessio userAgent = strings.ToLower(userAgent) // Detect device type - if strings.Contains(userAgent, "ipad") { + if strings.Contains(userAgent, "ipad") || strings.Contains(userAgent, "tablet") { clientInfo.DeviceType = "tablet" } else if strings.Contains(userAgent, "mobile") || strings.Contains(userAgent, "android") || strings.Contains(userAgent, "iphone") || strings.Contains(userAgent, "ipod") || strings.Contains(userAgent, "windows phone") || strings.Contains(userAgent, "blackberry") { clientInfo.DeviceType = "mobile" - } else if strings.Contains(userAgent, "tablet") { - clientInfo.DeviceType = "tablet" } else { clientInfo.DeviceType = "desktop" } diff --git a/server/router/api/v1/common.go b/server/router/api/v1/common.go index 1e1999347..66fc032e5 100644 --- a/server/router/api/v1/common.go +++ b/server/router/api/v1/common.go @@ -30,8 +30,6 @@ func convertStateFromStore(rowStatus store.RowStatus) v1pb.State { func convertStateToStore(state v1pb.State) store.RowStatus { switch state { - case v1pb.State_NORMAL: - return store.Normal case v1pb.State_ARCHIVED: return store.Archived default: diff --git a/server/router/api/v1/inbox_service.go b/server/router/api/v1/inbox_service.go index a14fb06d2..223be9d04 100644 --- a/server/router/api/v1/inbox_service.go +++ b/server/router/api/v1/inbox_service.go @@ -216,8 +216,6 @@ func convertInboxStatusFromStore(status store.InboxStatus) v1pb.Inbox_Status { func convertInboxStatusToStore(status v1pb.Inbox_Status) store.InboxStatus { switch status { - case v1pb.Inbox_UNREAD: - return store.UNREAD case v1pb.Inbox_ARCHIVED: return store.ARCHIVED default: diff --git a/server/router/api/v1/markdown_service.go b/server/router/api/v1/markdown_service.go index 42775a942..99f6cb5d0 100644 --- a/server/router/api/v1/markdown_service.go +++ b/server/router/api/v1/markdown_service.go @@ -299,8 +299,6 @@ func convertListKindToASTNode(kind v1pb.ListNode_Kind) ast.ListKind { return ast.OrderedList case v1pb.ListNode_UNORDERED: return ast.UnorderedList - case v1pb.ListNode_DESCRIPTION: - return ast.DescriptionList default: // Default to description list. return ast.DescriptionList diff --git a/server/router/api/v1/memo_relation_service.go b/server/router/api/v1/memo_relation_service.go index 3c59e3ce5..0e902c500 100644 --- a/server/router/api/v1/memo_relation_service.go +++ b/server/router/api/v1/memo_relation_service.go @@ -160,8 +160,6 @@ func convertMemoRelationTypeFromStore(relationType store.MemoRelationType) v1pb. func convertMemoRelationTypeToStore(relationType v1pb.MemoRelation_Type) store.MemoRelationType { switch relationType { - case v1pb.MemoRelation_REFERENCE: - return store.MemoRelationReference case v1pb.MemoRelation_COMMENT: return store.MemoRelationComment default: diff --git a/server/router/api/v1/memo_service.go b/server/router/api/v1/memo_service.go index 797cfd6ed..e8f812c8e 100644 --- a/server/router/api/v1/memo_service.go +++ b/server/router/api/v1/memo_service.go @@ -892,16 +892,11 @@ func (*APIV1Service) parseMemoOrderBy(orderBy string, memoFind *store.FindMemo) } switch field { - case "display_time": - memoFind.OrderByTimeAsc = direction == "asc" - case "create_time": + case "display_time", "create_time", "name": memoFind.OrderByTimeAsc = direction == "asc" case "update_time": memoFind.OrderByUpdatedTs = true memoFind.OrderByTimeAsc = direction == "asc" - case "name": - // For ordering by memo name/id - not commonly used but supported - memoFind.OrderByTimeAsc = direction == "asc" default: return errors.Errorf("unsupported order field: %s, supported fields are: display_time, create_time, update_time, name", field) } diff --git a/server/router/api/v1/memo_service_converter.go b/server/router/api/v1/memo_service_converter.go index cc423cc81..07077e229 100644 --- a/server/router/api/v1/memo_service_converter.go +++ b/server/router/api/v1/memo_service_converter.go @@ -134,8 +134,6 @@ func convertVisibilityFromStore(visibility store.Visibility) v1pb.Visibility { func convertVisibilityToStore(visibility v1pb.Visibility) store.Visibility { switch visibility { - case v1pb.Visibility_PRIVATE: - return store.Private case v1pb.Visibility_PROTECTED: return store.Protected case v1pb.Visibility_PUBLIC: diff --git a/server/router/api/v1/shortcut_service.go b/server/router/api/v1/shortcut_service.go index 0d4467dd6..c7edd6561 100644 --- a/server/router/api/v1/shortcut_service.go +++ b/server/router/api/v1/shortcut_service.go @@ -331,8 +331,6 @@ func (s *APIV1Service) validateFilter(ctx context.Context, filterStr string) err var dialect filter.DialectName switch s.Profile.Driver { - case "sqlite": - dialect = filter.DialectSQLite case "mysql": dialect = filter.DialectMySQL case "postgres": diff --git a/server/router/api/v1/test/idp_service_test.go b/server/router/api/v1/test/idp_service_test.go index fb376311d..7b7da6bd4 100644 --- a/server/router/api/v1/test/idp_service_test.go +++ b/server/router/api/v1/test/idp_service_test.go @@ -1,4 +1,4 @@ -package v1 +package test import ( "context" diff --git a/server/router/api/v1/test/inbox_service_test.go b/server/router/api/v1/test/inbox_service_test.go index 44cc82afc..fdef31a5e 100644 --- a/server/router/api/v1/test/inbox_service_test.go +++ b/server/router/api/v1/test/inbox_service_test.go @@ -1,4 +1,4 @@ -package v1 +package test import ( "context" diff --git a/server/router/api/v1/test/memo_service_test.go b/server/router/api/v1/test/memo_service_test.go index c968c2327..e9cb1d3bd 100644 --- a/server/router/api/v1/test/memo_service_test.go +++ b/server/router/api/v1/test/memo_service_test.go @@ -1,4 +1,4 @@ -package v1 +package test import ( "context" diff --git a/server/router/api/v1/test/shortcut_service_test.go b/server/router/api/v1/test/shortcut_service_test.go index 90921cdff..880426f39 100644 --- a/server/router/api/v1/test/shortcut_service_test.go +++ b/server/router/api/v1/test/shortcut_service_test.go @@ -1,4 +1,4 @@ -package v1 +package test import ( "context" diff --git a/server/router/api/v1/test/test_helper.go b/server/router/api/v1/test/test_helper.go index b40e9c7cb..c34aafc90 100644 --- a/server/router/api/v1/test/test_helper.go +++ b/server/router/api/v1/test/test_helper.go @@ -1,4 +1,4 @@ -package v1 +package test import ( "context" diff --git a/server/router/api/v1/test/user_service_stats_test.go b/server/router/api/v1/test/user_service_stats_test.go index 4af148ccd..4c593367a 100644 --- a/server/router/api/v1/test/user_service_stats_test.go +++ b/server/router/api/v1/test/user_service_stats_test.go @@ -1,4 +1,4 @@ -package v1 +package test import ( "context" diff --git a/server/router/api/v1/test/workspace_service_test.go b/server/router/api/v1/test/workspace_service_test.go index 95a93a0dc..90abff4e6 100644 --- a/server/router/api/v1/test/workspace_service_test.go +++ b/server/router/api/v1/test/workspace_service_test.go @@ -1,4 +1,4 @@ -package v1 +package test import ( "context" diff --git a/server/router/api/v1/user_service.go b/server/router/api/v1/user_service.go index 1da7191c1..be3adf2dc 100644 --- a/server/router/api/v1/user_service.go +++ b/server/router/api/v1/user_service.go @@ -45,7 +45,7 @@ func (s *APIV1Service) ListUsers(ctx context.Context, request *v1pb.ListUsersReq userFind := &store.FindUser{} if request.Filter != "" { - if err := s.validateUserFilter(ctx, request.Filter); err != nil { + if err := validateUserFilter(ctx, request.Filter); err != nil { return nil, status.Errorf(codes.InvalidArgument, "invalid filter: %v", err) } } @@ -1065,8 +1065,6 @@ func convertUserRoleToStore(role v1pb.User_Role) store.Role { return store.RoleHost case v1pb.User_ADMIN: return store.RoleAdmin - case v1pb.User_USER: - return store.RoleUser default: return store.RoleUser } @@ -1147,10 +1145,6 @@ func convertUserSettingFromStore(storeSetting *storepb.UserSetting, userID int32 } switch key { - case storepb.UserSetting_GENERAL: - setting.Value = &v1pb.UserSetting_GeneralSetting_{ - GeneralSetting: getDefaultUserGeneralSetting(), - } case storepb.UserSetting_SESSIONS: setting.Value = &v1pb.UserSetting_SessionsSetting_{ SessionsSetting: &v1pb.UserSetting_SessionsSetting{ @@ -1365,7 +1359,7 @@ func extractWebhookIDFromName(name string) string { } // validateUserFilter validates the user filter string. -func (s *APIV1Service) validateUserFilter(_ context.Context, filterStr string) error { +func validateUserFilter(_ context.Context, filterStr string) error { if strings.TrimSpace(filterStr) != "" { return errors.New("user filters are not supported") } diff --git a/store/memo.go b/store/memo.go index a32a9320e..7949eae07 100644 --- a/store/memo.go +++ b/store/memo.go @@ -27,8 +27,6 @@ func (v Visibility) String() string { return "PUBLIC" case Protected: return "PROTECTED" - case Private: - return "PRIVATE" default: return "PRIVATE" } diff --git a/store/test/activity_test.go b/store/test/activity_test.go index 3ac3d8c19..96ab81023 100644 --- a/store/test/activity_test.go +++ b/store/test/activity_test.go @@ -1,4 +1,4 @@ -package teststore +package test import ( "context" diff --git a/store/test/attachment_test.go b/store/test/attachment_test.go index a653a0d97..8655029bf 100644 --- a/store/test/attachment_test.go +++ b/store/test/attachment_test.go @@ -1,4 +1,4 @@ -package teststore +package test import ( "context" diff --git a/store/test/idp_test.go b/store/test/idp_test.go index a204f33fa..0522454f8 100644 --- a/store/test/idp_test.go +++ b/store/test/idp_test.go @@ -1,4 +1,4 @@ -package teststore +package test import ( "context" diff --git a/store/test/inbox_test.go b/store/test/inbox_test.go index da79c5034..0c74bc104 100644 --- a/store/test/inbox_test.go +++ b/store/test/inbox_test.go @@ -1,4 +1,4 @@ -package teststore +package test import ( "context" diff --git a/store/test/memo_relation_test.go b/store/test/memo_relation_test.go index 56565bf89..feec0c28c 100644 --- a/store/test/memo_relation_test.go +++ b/store/test/memo_relation_test.go @@ -1,4 +1,4 @@ -package teststore +package test import ( "context" diff --git a/store/test/memo_test.go b/store/test/memo_test.go index d2590ac03..3122136a1 100644 --- a/store/test/memo_test.go +++ b/store/test/memo_test.go @@ -1,4 +1,4 @@ -package teststore +package test import ( "context" diff --git a/store/test/migrator_test.go b/store/test/migrator_test.go index 8b22eb295..a76f27ee1 100644 --- a/store/test/migrator_test.go +++ b/store/test/migrator_test.go @@ -1,4 +1,4 @@ -package teststore +package test import ( "context" diff --git a/store/test/reaction_test.go b/store/test/reaction_test.go index 142c6a8bf..fc83861e4 100644 --- a/store/test/reaction_test.go +++ b/store/test/reaction_test.go @@ -1,4 +1,4 @@ -package teststore +package test import ( "context" diff --git a/store/test/store.go b/store/test/store.go index 5ae05e51b..71435bfd6 100644 --- a/store/test/store.go +++ b/store/test/store.go @@ -1,4 +1,4 @@ -package teststore +package test import ( "context" diff --git a/store/test/user_setting_test.go b/store/test/user_setting_test.go index ce954ac5c..47ff170b8 100644 --- a/store/test/user_setting_test.go +++ b/store/test/user_setting_test.go @@ -1,4 +1,4 @@ -package teststore +package test import ( "context" diff --git a/store/test/user_test.go b/store/test/user_test.go index 14131b4ae..3f336f2f3 100644 --- a/store/test/user_test.go +++ b/store/test/user_test.go @@ -1,4 +1,4 @@ -package teststore +package test import ( "context" diff --git a/store/test/workspace_setting_test.go b/store/test/workspace_setting_test.go index 18939fdde..2a142d349 100644 --- a/store/test/workspace_setting_test.go +++ b/store/test/workspace_setting_test.go @@ -1,4 +1,4 @@ -package teststore +package test import ( "context" diff --git a/store/user.go b/store/user.go index b5f1974e0..c07c5c3ee 100644 --- a/store/user.go +++ b/store/user.go @@ -22,8 +22,6 @@ func (e Role) String() string { return "HOST" case RoleAdmin: return "ADMIN" - case RoleUser: - return "USER" default: return "USER" }