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