file-system/CLAUDE.md

146 lines
4.9 KiB
Markdown

# CLAUDE.md
> **Cross-repo rules** — see `/Users/wen/project/rag/CLAUDE.md` for full workspace conventions.
> - .NET backends share JWT key: `RagJwtSecretKey2026MustBeAtLeast32CharsLong!`
> - gRPC auth: call `rag-backend:50051` for token validation + permission checks
> - Other repos: `rag-backend` (5211), `im-system` (5212), `work-flow`, `rag-frontend` (5666 Vue)
## Build & Run
```bash
go run ./cmd/server -conf configs/config.yaml # HTTP :8080, gRPC :9000
make api # Regenerate proto code
make wire # Regenerate Wire DI
go test ./... # All tests
buf generate # Regenerate proto
docker compose up -d # Via local build
```
## Architecture
Go 1.25 microservice. Kratos framework + DDD four-layer + Watermill CQRS.
```
cmd/server/main.go → Entry point, load config
cmd/server/wire.go → Wire DI declarations
cmd/server/wire_gen.go → Wire generated code
api/file/v1/ → Proto definitions (HTTP+gRPC dual protocol)
internal/conf/ → Config structs (proto-defined)
internal/biz/ → Business logic layer (repo interface definitions + usecases)
internal/data/ → Data access layer (GORM + S3, implements biz repo interfaces)
internal/service/ → Service implementation (implements proto Service interface)
internal/server/ → HTTP/gRPC server creation and middleware
internal/watermark/ → Watermill CQRS (CommandBus + EventBus)
internal/pkg/sanitize/ → Input sanitization utilities
internal/pkg/s3errors/ → S3 error mapping
configs/config.yaml → Local dev config
```
## Layered Call Chain
```
service (DTO conversion) → biz (business logic) → data (data access)
watermark (CQRS commands/events)
```
## Tech Stack
- **Kratos** — HTTP + gRPC framework, proto-first API definition
- **Wire** — Compile-time dependency injection
- **Watermill** — CQRS (CommandBus + EventBus), PGSQL as message store
- **GORM** — Business data ORM (folders, files, share_links)
- **AWS SDK v2** — S3 interface to RustFS/MinIO
- **PostgreSQL** — Business data + Watermill message queue
## Code Patterns
### Wire ProviderSet Pattern
Each layer defines a ProviderSet:
```go
// internal/data/data.go
var ProviderSet = wire.NewSet(NewData, NewFileRepo, NewFolderRepo, NewFileMetaRepo, NewShareRepo)
// internal/biz/biz.go
var ProviderSet = wire.NewSet(NewFileUsecase, NewBucketUsecase, NewFolderUsecase, NewShareUsecase)
// internal/service/service.go
var ProviderSet = wire.NewSet(NewFileService)
// internal/server/server.go
var ProviderSet = wire.NewSet(NewHTTPServer, NewGRPCServer)
```
Wire bindings (in cmd/server/wire.go):
- `biz.FileRepo``*data.FileRepo`
- `biz.FolderRepo``*data.FolderRepo`
- `biz.FileMetaRepo``*data.FileMetaRepo`
- `biz.ShareRepo``*data.ShareRepo`
### GORM Transaction Management
```go
// biz layer defines interface
type Transaction interface { InTx(ctx context.Context, fn func(ctx context.Context) error) error }
// data layer implements
func (d *Data) DB(ctx context.Context) *gorm.DB {
tx, ok := ctx.Value(contextTxKey{}).(*gorm.DB)
if ok { return tx }
return d.db.WithContext(ctx)
}
```
### Proto Error Definition
Errors defined in `api/file/v1/error_reason.proto`:
```protobuf
enum ErrorReason {
FILE_NOT_FOUND = 0 [(errors.code) = 404];
INVALID_PARAMETER = 1 [(errors.code) = 400];
}
```
Usage: `return nil, api.file.v1.ErrorFileNotFound("file %s not found", key)`
### GORM Model Naming
- Models suffixed with `PO`: `FolderPO`, `FileMetaPO`, `ShareLinkPO`
- Table names via `TableName()` method with snake_case
### Service → Biz → Data Pattern
```go
// service: proto request → usecase call → proto response
func (s *FileService) UploadFile(ctx context.Context, req *pb.UploadFileRequest) (*pb.UploadFileResponse, error) {
err := s.fileUC.UploadFile(ctx, req.BucketName, req.ObjectKey, bytes.NewReader(req.Data))
return &pb.UploadFileResponse{Message: "uploaded"}, err
}
// biz: business logic, repo interface calls
func (uc *FileUsecase) UploadFile(ctx context.Context, bucket, key string, data io.Reader) error {
return uc.repo.UploadFile(ctx, bucket, key, data)
}
// data: concrete implementation
func (r *FileRepo) UploadFile(ctx context.Context, bucket, key string, data io.Reader) error {
_, err := r.client.PutObject(ctx, &s3.PutObjectInput{...})
return s3errors.Wrap(err)
}
```
### Configuration
`configs/config.yaml` for local development. Environment variables via `${ENV_VAR}` placeholders.
Loaded via Kratos config component with file source.
### Middleware Chain (Kratos)
HTTP and gRPC share the same middleware:
```
recovery → tracing → logging
```
Configured in `internal/server/http.go` and `internal/server/grpc.go`.