222 lines
14 KiB
Markdown
222 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 panel (bypasses api, hits Postgres directly) |
|
|
| `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 ──────────────────────→ Postgres directly (bypasses api)
|
|
```
|
|
|
|
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
|
|
- **Global singleton**: `config.Cfg` — a `sync.Once` ensures it's initialized exactly once
|
|
- **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
|
|
|
|
Both `auth` and `wallet` follow a layered architecture:
|
|
|
|
- **`config/`** — TOML config via koanf. `config.Cfg` global singleton, `sync.Once` initialized.
|
|
- **`domain/stub/go/`** — Generated protobuf Go code. **Never edit manually.**
|
|
- **`cmd/`** — Cobra CLI entry points. Subcommands map to serve modes.
|
|
- **`repository/`** — Data access. Aggregates `IPostgres`, `IRedis`, `IService`, and `IQueue` (wallet only) interfaces.
|
|
- **`core/`** (wallet) / **`usecase/`** (auth) — Business logic. Implements gRPC server interfaces from proto.
|
|
- **`util/`** — Shared helpers; no business logic.
|
|
|
|
In `auth`, `usecase.UseCase` interface directly embeds the generated gRPC server interfaces (`authv1.AuthorizationServiceServer`, `authv1.InternalAuthorizationServiceServer`).
|
|
|
|
In `wallet`, `core/` contains sub-packages: `walletImp/`, `marketImp/`, `alertImp/`, `cronJobs/`.
|
|
|
|
## 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**: Generates into root directories (`base/`, `wallet/`, etc.) then deletes them with `rm -rf`. The betterproto generator outputs Python classes directly.
|
|
|
|
## AdminPanel Deep Dive
|
|
|
|
- **Architecture**: Django admin panel that bypasses all Go backend services. Connects directly to `core_db` (same Postgres as Go services) for read/write. Uses Django DB routers in `src/adminpanel/db/routers.py` to route `coreLogic` models 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/` — 17 admin files (`asset.py`, `wallets.py`, `market.py`, etc.). All inherit from `MultiDBModelAdmin` in `src/utils/base_admin.py` which handles: multi-database writes (`using="core_db"`), soft deletes via `deleted_at`, asset-level permission filtering, and Jalali date widgets.
|
|
- **Permission system**: `src/usermapper/user_perm.py` + `src/coreLogic/acl.py` — admin users get asset-level access control. `save_model` checks `user_perm.can_access_asset()` before allowing writes.
|
|
- **Key gotchas**:
|
|
- `check_token_policy()` in `admin/asset.py:161` duplicates validation from wallet service. Changes to asset validation must be made in **both** places.
|
|
- `GENERIC_ASSET_META_VALUE` in `admin/asset.py:31` is a hardcoded JSON blob — if the wallet service changes asset metadata structure, this must be updated too.
|
|
- The router has a typo: `no_migartion` → should be `no_migration`.
|
|
- All `coreLogic` models are read via `core_db` directly. **Any admin write bypasses Go services** — no trustline updates, no blockchain operations, no validation from wallet/market services.
|
|
|
|
## 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 access to `core_db` with no gRPC layer. Business logic duplicated in Django admin classes (`check_token_policy`, `auto_gen` instead of delegating to Go services). `managed = False` models drift from Go GORM models.
|
|
|
|
**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.
|