docs: update Memos Codebase Guide for clarity and structure

This commit is contained in:
Johnny 2025-12-02 08:19:20 +08:00
parent 0610257562
commit a863154224
1 changed files with 58 additions and 322 deletions

380
CLAUDE.md
View File

@ -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 <token>` header
**Flow:**
- Authentication interceptor (`server/router/api/v1/acl.go`) runs on all gRPC methods
- Public endpoints bypass auth (see `acl_config.go` for allowlist)
- Context values set: `userIDContextKey`, `sessionIDContextKey`, `accessTokenContextKey`
- Sessions have sliding expiration (14 days from last access)
## Code Style and Conventions
### Go
- **Formatting:** All code must be `gofmt`-compliant (tabs for indentation)
- **Imports:** Group stdlib, external, and local imports (enforced by `goimports`)
- **Error Handling:** Wrap errors with `%w` when propagating: `errors.Wrap(err, "context")`
- **Naming:** Package names lowercase, no underscores
- **Linting:** Enforced via `.golangci.yaml` (revive, staticcheck, gocritic, etc.)
### TypeScript/React
- **Components:** PascalCase filenames (e.g., `MemoEditor.tsx`)
- **Hooks:** camelCase filenames (e.g., `useMemoList.ts`)
- **Formatting:** 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`