223 lines
14 KiB
Markdown
223 lines
14 KiB
Markdown
# AGENTS.md
|
|
|
|
This file provides guidance for agents working in the Darano monorepo.
|
|
|
|
## Repository Structure
|
|
|
|
Multi-service monorepo for the Darano financial/crypto platform. Each subdirectory is its own independent git repository:
|
|
|
|
| Directory | Language | Role |
|
|
|-----------|----------|------|
|
|
| `api/` | Go (Gin) | HTTP REST gateway — the only public-facing service |
|
|
| `auth/` | Go (gRPC) | Authorization — OTP, JWT, permissions, identity |
|
|
| `wallet/` | Go (gRPC) | Wallet — assets, transactions, Stellar blockchain, market |
|
|
| `ui/` | TypeScript/Next.js | Customer-facing frontend |
|
|
| `AdminPanel/` | Python/Django | Internal admin: direct SQL reads, authenticated service-owned writes |
|
|
| `proto/` | Protobuf | Central schema definitions shared by all services |
|
|
| `DevOps/` | Docker Compose | Infrastructure — Postgres, Redis, RabbitMQ, MinIO, Traefik |
|
|
| `docs/` | MkDocs | Documentation site |
|
|
|
|
**Root-level**: `Procfile` (deployment), `CLAUDE.md` (Claude Code config) — these are the monorepo's coordination files, not service-level.
|
|
|
|
## Service Communication Architecture
|
|
|
|
```
|
|
Browser/Client → api (REST/HTTP) → auth, wallet/market/alert (gRPC)
|
|
AdminPanel ──reads───────────────→ Postgres
|
|
AdminPanel ──authenticated gRPC──→ wallet (asset administration)
|
|
```
|
|
|
|
The `api` gateway is the **only** HTTP-facing service. All inter-service communication is gRPC.
|
|
|
|
### API Gateway (api/)
|
|
|
|
- **Entry/composition**: `api/main.go` → `cmd.Execute()` (Cobra) → `cmd/apiRuntime`, which explicitly owns clients, handlers, router, HTTP servers, permission synchronization, and cleanup.
|
|
- **Upstream boundary**: `api/application/port.Upstreams` composes generated service contracts. `api/infrastructure/grpcclient` implements lazy connection creation, active-call tracking, configurable idle closure (**2-minute default**), reconnection, health checks, and explicit shutdown without reflection.
|
|
- **Handlers**: `api/interface/http/handler/` — one file per domain. Handlers depend on `application/port.Upstreams`.
|
|
- **Routing**: `api/interface/http/handler/routing.go` — routes grouped into `/v1/public/`, `/v1/client/`, `/v1/admin/`. The admin group is protected but currently empty; internal routes remain disabled.
|
|
- **Middleware chain** (order matters): APM → Profiling → Prometheus → CORS → JSON → I18n → Error → optional Logger
|
|
- **Endpoints**: `GET /` (health), `GET /metrics` (Prometheus), `GET /swagger/*any`, `GET /ws` (WebSocket)
|
|
- **Profiling**: `net/http/pprof` is imported (blank import `_`); runs on a separate port when `Profiling.Enabled` in config
|
|
|
|
### Wallet Binary (wallet/)
|
|
|
|
Single binary hosts multiple sub-services via Cobra subcommands: `wallet`, `market`, `alert`, `internal_wallet`, `stream`. These are spawned concurrently via `errgroup` in `cmd/cmdServe/main.go`.
|
|
|
|
**Concurrent build safety**: The Makefile uses `flock` on `./.build/air-build.lock` to serialize protobuf generation and binary writes when multiple air instances run simultaneously (`make dev-wallet`, `make dev-market`, etc. all trigger `make build`).
|
|
|
|
### Config
|
|
|
|
- **Go services** use `knadh/koanf` (not `fig` as older docs may suggest) to parse TOML config files
|
|
- **Explicit ownership**: commands load configuration once and inject it through composition; legacy `config.Cfg` globals were removed
|
|
- **Defaults baked into code**: `config.go` in `api/` sets default `ipg_callback_url` and `ui_server_error_status_url` before loading the TOML file (TOML overrides defaults)
|
|
- **Struct tags**: use `koanf:"field-name"` (not env vars)
|
|
|
|
### Config files reference
|
|
|
|
| Service | Config file |
|
|
|---------|------------|
|
|
| API | `./cfg.toml` (or `--conf ./config.cfg`) |
|
|
| Wallet sub-services | `./wallet.cfg.toml`, `./market.cfg.toml`, `./alert.cfg.toml`, `./stream.cfg.toml` |
|
|
| Internal wallet | `./wallet.internal.cfg.toml` |
|
|
|
|
## Commands
|
|
|
|
### Go services (`api/`, `auth/`)
|
|
|
|
```bash
|
|
cd api/ # or cd auth/
|
|
|
|
make dep # install buf, protoc plugins, swag, air
|
|
make build # fmt + proto gen + binary → ./build/main
|
|
make dev # build + hot reload via air
|
|
make test # go test ./...
|
|
./build/main serve --conf ./cfg.toml
|
|
```
|
|
|
|
### Wallet service (`wallet/`)
|
|
|
|
```bash
|
|
cd wallet/
|
|
|
|
make build # proto gen + binary
|
|
make dev-wallet # hot reload wallet sub-service (.wallet.air.toml)
|
|
make dev-market # hot reload market sub-service (.market.air.toml)
|
|
make dev-stream # hot reload stream sub-service (.stream.air.toml)
|
|
make run-wallet # ./build/main serve wallet -c ./wallet.cfg.toml
|
|
make run-market # ./build/main serve market -c ./market.cfg.toml
|
|
make run-internal # ./build/main serve internal_wallet -c ./wallet.internal.cfg.toml
|
|
make run-alert # ./build/main serve alert -c ./alert.cfg.toml
|
|
make run-stream # ./build/main stream -c ./stream.cfg.toml
|
|
make grpc-ui-wallet # open grpcui on port 7210 (localhost:8200)
|
|
make grpc-ui-market # open grpcui on port 7300 (localhost:8300)
|
|
make grpc-ui-alert # open grpcui on port 7400 (127.0.0.1:8400)
|
|
```
|
|
|
|
**Important**: When running multiple sub-services concurrently via `make run` or multiple `make dev-*` instances, the Makefile uses `flock` to prevent concurrent protobuf generation or binary overwrites. Don't parallelize make (`-j`) without `flock` — it will corrupt stub files.
|
|
|
|
### AdminPanel (`AdminPanel/`)
|
|
|
|
```bash
|
|
cd AdminPanel/
|
|
|
|
# DB setup (uv virtual env)
|
|
uv run python src/manage.py makemigrations
|
|
uv run python src/manage.py migrate
|
|
|
|
make run # runserver on 0.0.0.0:8080
|
|
make dev # watch mode using funzzy
|
|
make proto # buf generate → proto stubs via betterproto
|
|
make build-msg # compile Django messages (i18n)
|
|
```
|
|
|
|
### UI (`ui/`)
|
|
|
|
```bash
|
|
cd ui/
|
|
|
|
yarn build-proto # generate TS types from protos (buf generate)
|
|
yarn dev # Next.js dev server (also runs buf generate)
|
|
make dev # same, bound to 0.0.0.0:3000
|
|
yarn lint # ESLint
|
|
yarn lint:fix # ESLint with auto-fix
|
|
yarn build # production build (also runs buf generate)
|
|
```
|
|
|
|
## Protobuf Code Generation
|
|
|
|
All services depend on generated code. **Always run `make build-proto` (or `yarn build-proto` for UI) before building any service.**
|
|
|
|
Proto definitions live in `proto/` with subdirectories: `base/`, `auth/`, `wallet/`, `market/`, `alert/`, `errors/`. Each service has its own `buf.gen.yaml` controlling code generation output into `domain/stub/go/` (Go) or `src/types/stub/` (TypeScript).
|
|
|
|
**Gotcha**: The Go Makefiles **strip `omitempty`** from all JSON struct tags in `.pb.go` files after generation. This is intentional — protobuf JSON serialization needs consistent field presence. Do not re-add `omitempty` manually.
|
|
|
|
## Go Service Layer Pattern
|
|
|
|
The active Go services follow inward-facing layers:
|
|
|
|
- **`domain/`** — entities, exact value objects, errors, and ports without transport/framework ownership.
|
|
- **`application/`** — business policies and orchestration against domain ports.
|
|
- **`infrastructure/`** — koanf configuration, PostgreSQL/Redis, external clients, queues, Stellar, and generated-client adapters.
|
|
- **`interface/`** — gRPC/HTTP/process adapters and protocol mapping.
|
|
- **`domain/stub/go/`** — Generated protobuf Go code. **Never edit manually.**
|
|
- **`cmd/`** — Cobra CLI entry points. Subcommands map to serve modes.
|
|
- **`repository/` / `usecase/`** — remaining compatibility composition/interfaces; implementations live in infrastructure and new business logic belongs in application/domain.
|
|
|
|
In `auth`, `usecase.UseCase` interface directly embeds the generated gRPC server interfaces (`authv1.AuthorizationServiceServer`, `authv1.InternalAuthorizationServiceServer`).
|
|
|
|
In `wallet`, runtime adapters live under `interface/grpc` and `interface/process`; the superseded `core/*Imp` packages no longer exist.
|
|
|
|
## API Gateway Patterns
|
|
|
|
### Request/Response Flow
|
|
|
|
```
|
|
HTTP request → interface/http middleware → interface/http/handler → application Upstreams port → infrastructure/grpcclient → backend service
|
|
```
|
|
|
|
- **Response helpers**: `interface/http/handler/response.go` — `JSON()` and `JSONList[X]()` wrap responses in `transport.Response{Meta, Data}`. Pointer-backed empty lists use the `EmptyList` sentinel to serialize `[]` rather than `null`.
|
|
- **HTTP-to-gRPC context**: `interface/http/handler/contextWithMetadata()` copies HTTP headers into incoming and outgoing gRPC metadata.
|
|
- **Handler interface**: `interface/http/handler.Server` receives the `application/port.Upstreams` boundary and configuration explicitly.
|
|
|
|
### Route conventions
|
|
|
|
- Public routes: `/v1/public/{domain}/{action}` — no JWT needed
|
|
- Client routes: `/v1/client/{domain}/{action}` — JWT required (validated by `AuthorizationMiddleware`)
|
|
- Admin routes: `/v1/admin/{domain}/{action}` — JWT required (mostly placeholder currently)
|
|
|
|
## UI Architecture
|
|
|
|
- **Next.js App Router** under `src/app/`. Notable route groups: `dashboard/` (auth-protected), `auth/login/`, `blog/` (MDX content), `projects/`, `receipt/`.
|
|
- **Auth**: `src/middleware.ts` uses next-auth to protect `/dashboard/:path*`. Exports default from `next-auth/middleware`.
|
|
- **Services**: `src/services/` — plain functions wrapping axios calls to the api gateway, typed with generated protobuf types.
|
|
- **State**: `src/stores/` — Zustand stores.
|
|
- **Hooks**: `src/hooks/` — TanStack Query hooks on top of service functions.
|
|
- **Components**: HeroUI + Tailwind CSS with RTL support (Persian/Farsi UI).
|
|
- **Forms**: Formik + Yup (older), react-hook-form (newer).
|
|
- **Date picker**: `@amir04lm26/react-modern-calendar-date-picker` (Jalali calendar).
|
|
- **MDX**: Blog content uses `.mdx` files with remark/rehype plugins.
|
|
- **Path alias**: `@/*` maps to `./src/*`.
|
|
- **Output**: `standalone` mode for Docker. Image domains hardcoded in `next.config.mjs` remote patterns.
|
|
|
|
## Infrastructure
|
|
|
|
Dev infrastructure: `DevOps/dev/compose.yml` — PostgreSQL 16, Redis, RabbitMQ, MinIO. Each Go service has its own DB name.
|
|
|
|
All Go services instrumented with Elastic APM and Prometheus metrics. Traefik handles TLS/routing in production.
|
|
|
|
## Gotchas and Non-Obvious Details
|
|
|
|
1. **Config library**: Services use `knadh/koanf` with TOML parser, not `fig`. Tags are `koanf:"field-name"`.
|
|
2. **gRPC connection lifecycle**: Connections are lazy — created on first RPC. Active calls prevent idle closure; peers close after the configured inactivity timeout (2-minute default) and reconnect on the next call.
|
|
3. **Peer mapping**: `infrastructure/grpcclient` maps configured peers explicitly; the former reflection/enum generator is removed.
|
|
4. **Swagger docs**: Generated by `swag init` and served via `interface/http`. URL adapts per environment (prod → `api.darano.ir`, dev → `dev.api.darano.ir`, local → `localhost:<port>`).
|
|
5. **Multi-service wallet**: The wallet binary serves 5 sub-services. `make run` kills existing processes matching the service name pattern via `pgrep | xargs kill` before starting.
|
|
6. **Concurrent dev**: Running multiple `make dev-*` targets simultaneously requires the `flock` serialization in the Makefile. Using `make -j` without flock will corrupt the build.
|
|
7. **Proto stubs excluded from watchers**: `.air.toml` excludes `domain/stub/` from file watching. Air also excludes `swagger/` and `testdata/`.
|
|
8. **API testing requests**: `api/req/` contains JS files for API request testing (axios-based) — not part of the app, useful for manual testing.
|
|
9. **Django Admin**: Uses `unfold` theme (AdminPanel admin customizations in `sites.py` and `unfoldconf.py`).
|
|
10. **Proto generation for AdminPanel**: `make proto` uses the adjacent local `proto/` checkout and generates only the active Base/Auth/Wallet BetterProto message subset into `src/stub/`.
|
|
|
|
## AdminPanel Deep Dive
|
|
|
|
- **Architecture**: Django admin panel that reads `core_db` directly for fast projections. Unmanaged models are read-only by default; the Assets admin sends authenticated typed commands to Wallet instead of writing the database. Django DB routers route `coreLogic` reads to `core_db` and other apps to `default`.
|
|
- **Models**: `src/coreLogic/models.py` contains `managed = False` Django models — manual replicas of Go GORM models generated via `inspectdb`. **Not auto-generated from protos.** `make proto` generates betterproto Python stubs separately but Django models are maintained by hand. **Never edit models.py manually** — it drifts from Go services.
|
|
- **Admin classes**: `src/coreLogic/admin/` — admin files inherit from `MultiDBModelAdmin` in `src/utils/base_admin.py`, which routes reads to `core_db`, applies asset-level filters/Jalali widgets, and makes unmanaged projections fail closed for add/change/delete. Assets opt into service-backed mutations only.
|
|
- **Permission system**: `src/usermapper/user_perm.py` + `src/coreLogic/acl.py` — admin users get asset-level access control. Asset service commands enforce the same access decision before dispatch.
|
|
- **Key gotchas**:
|
|
- Asset policy and default metadata are owned by Wallet's `application/adminasset`; do not recreate them in Django.
|
|
- The Wallet admin client requires matching AdminPanel `WALLET_ADMIN_GRPC_TOKEN` and internal-wallet `[admin-assets].token` configuration.
|
|
- All `coreLogic` models are read via `core_db` directly. Adding a mutation requires an explicit typed owning-service workflow; never opt an unmanaged model into direct ORM writes.
|
|
|
|
## Ongoing Refactoring: DDD / Clean Architecture
|
|
|
|
This repository is being migrated to a unified Domain-Driven Design / Clean Architecture. See [`REFACTORING-PLAN.md`](REFACTORING-PLAN.md) for the full plan.
|
|
|
|
**Go services current state**: API, Auth, and Wallet configuration and architecture phases are complete on `feat/refactor-v1`; use the refactoring tracker and audit for the current package boundaries and verification evidence.
|
|
|
|
**AdminPanel current state**: Direct DB reads remain, while unmanaged projections fail closed for writes. Asset upsert/deactivation uses an authenticated Wallet gRPC adapter; duplicated asset policy and metadata generation have been removed from Django. `managed = False` models can still drift from Go persistence models and must remain projection-only.
|
|
|
|
**Target**: All Go services follow `domain/` → `application/` → `infrastructure/` → `interface/` with inward dependencies. AdminPanel routes writes through gRPC to Go services while keeping reads from DB for performance.
|
|
|
|
**Migration phases** (see plan for details): Config standardization → auth/ restructure → wallet/ restructure → api/ restructure → AdminPanel service layer → shared domain types. Parallel workstreams: Go services restructure can run alongside AdminPanel gRPC client creation.
|